olpc
From NetBSD Wiki
| NetBSD/olpc www.NetBSD.org/ports/olpc olpc | |
|---|---|
| Status: | |
| First: | |
| Last: | |
| Maintainer: | mellon |
Contents |
NetBSD/olpc
NetBSD/olpc is an ongoing project to get NetBSD/i386 running on the OLPC XO. It's not clear yet whether this is a separate port, or whether it's just a build option of the i386 port. For now I am doing it as its own port.
Status
Starts up and initializes the frame buffer to a pattern to indicate that it's running. Parses boot args. Then dies. Right now I'm working on segregating out the OLPC port code so that I can work, for now, on pc-compatibility-free sources.
One problem I'm having is that there are currently two x86 architectures - i386 and x86_64. There's also common x86 code in arch/x86. But some of this code actually assumes a traditional PC architecture - it's not machine-independent. Frequently code is conditionally compiled based on the setting of either __i386__ or __x86_64__. This code shouldn't be conditional on the CPU architecture but rather on the system architecture. I'm thinking of adding three flags to cover this: TRADITIONAL, TRADITIONAL_I386 and TRADITIONAL_X86_64. These would always be defined, in the std.* files for the relevant ports.
Building a Kernel
Right now I don't have a source release you can build from. If you're interested, send me mail. There's nothing secret about the code - I just don't want to commit it to the NetBSD repository yet because it's too raw.
Booting a kernel
Right now a kernel built for OLPC page faults at or immediately after start. This is not unexpected. To get a kernel to load, find yourself a FAT-formatted thumb drive. Make a /boot directory. Copy this file into the /boot directory. Copy your NetBSD kernel into the /boot directory. Plug the thumb drive into one of the OLPC's USB ports. Reboot it. The olpc.fth file should be automatically detected. Please not that Open Firmware gets confused by partition tables with multiple partitions, so you can't have multiple partitions on your USB drive. It also doesn't handle multiple USB drives, so don't plug in more than one. And it doesn't grok the NetBSD ffs filesystem format, so you can't boot off an ffs filesystem.
Netbooting a kernel
Surprisingly easy. Set up an HTTP server if you don't already have one. Put your kernel somewhere in the server's file space. Set up a DHCP server if you don't already have one. Now, at the Open Firmware prompt, type wifi XXX where XXX is the essid of your wifi network. Do not put quotes around the essid if there are spaces in it - the firmware eats everything on the line after the space following wifi, so it will assume that the quotes are part of the essid. It is supposedly possible to get WPA to work if you need it, but I run an open network right now, so I haven't tried.
Now, type boot http:\\IPA\kernel-path, where IPA is the IP address of your web server, and kernel-path is the path to the kernel; path separators must be backslashes, as far as I can tell, although I just put my kernel in the root, so I may be wrong. If you are running more than one domain off the same www server, you will have to put your kernel into the domain that gets selected if the HTTP client doesn't specify the domain.
The kernel should load and boot. Well, load and crash... :')
If you want to load a kernel and not run it, use load instead of boot.
Getting locore.s working
When the boot loader calls the kernel, it puts the address of an open firmware callback address in the EAX register. This can then be used to get boot arguments, memory map information, and the like. This is incompatible both with multiboot and the traditional boot loader. I was hoping to make it possible to boot a generic kernel on OLPC, but I don't think that's going to work - I think it's going to be a lot easier to build an OLPC-specific kernel. We can revisit this later, but that's the path I'm traveling at the moment.
I've got a kernel now that does callbacks into open firmware. In order to preserve this functionality after the kernel starts running, it is necessary to preserve the open firmware mappings. I think this is doable - you can read all the current page mappings out of openfirmware() before changing the map, and so it should be possible to set up a compatible map. I don't see much utility in calling back into open firmware after booting. Also, memory is tight.
So at some point it would make sense to reclaim all of the page table allocation that Open Firmware has done. However, OLPC starts up the kernel in protected mode, whereas i386 assumes that the kernel is started in real mode. Getting out of protected mode is hard. So for nowI am using the existing Open Firmware page table. FTR, Linux blows off the Open Firmware pages immediately on starting, so when the time comes to do this, we have a good excuse.
Memory Layout
The OLPC kernel currently starts up with GDT Base = 0xFF9F5DE0 and GDT limit = 0xFF. CR0 = 0x80000011. CR2 = 0. CR3 = 0xEDFFF000. CR4=0x10.
The content of the GDTR is a linear address, not a physical address. The physical address to which the GDTR maps is 0xeff5de0. The code segment and data segment registers point to segment descriptors that address the entire linear address space. So all the mapping is happening in the page table.
If you type "device /mmu" and then ".properties" you get a long list of translations. It appears to be the case that for each translation, the first word is the virtual address, the second is the length, the third is the physical address, and the fourth is the flags.
The page table is at physical address 0xEDFF000, which is mapped to virtual address 0xffa00000. Bear in mind that all of these values are probably specific to the version of firmware I'm running, so it will not be safe to hard-code them into the kernel. Pretty much anything that I'm reporting here has to be figured out dynamically by locore.s/ofw.c on startup.
It appears to be the case that on startup, there is a sixteen-bit frame buffer at physical 0xff000000, which is mapped to virtual 0xff000000. Pixels contain five bits of red (0xF800), six bits of green (0x07E0) and five bits of blue (0x001F).
I am fairly certain that physical memory (actual memory, not I/O mappings) starts at 0 and goes to 0xFFFFFFF. However, there may be a hole in there somewhere that the video memory is being chunked out of - I do not know yet whether video memory and main memory are all coming out of a single 256M pool, or whether they're separate, but given the focus on "cheap," I suspect the former.
Here's the layout after loading a kernel:
In Use: (ef7c000 -> fffffff) Free: (ef6c000 -> ef7bfff) ef6c000 10000 In Use: (ef51000 -> ef6bfff) Free: (ef41000 -> ef50fff) ef41000 10000 In Use: (ef23000 -> ef40fff) Free: (eeac000 -> ef22fff) eeac000 77000 In Use: (edfd000 -> eeabfff) Free: (c366000 -> edfcfff) c366000 2a97000 In Use: (c000000 -> c365fff) <- kernel Free: (100000 -> bffffff) 100000 bf00000 In Use: (a0000 -> fffff) <- this is where the 640k hole would be - what gives? Free: (2000 -> 9ffff) 2000 9e000 In Use: (0 -> 1fff) <- this would be consumed by the BIOS on a traditional PC.
Page Mapping
The traditional i386 code assumes that locore.S is entered in real mode. This isn't true on OLPC. There are advantages and disadvantages to this. The advantage is that the OLPC code can put the kernel wherever you want. The disadvantage is that none of the code in locore.S works at all, and neither will pmap_bootstrap(). Plus, since, as indicated above, the memory layout isn't simple, the fairly naive single-contiguous-block-with-hole strategy of the i386 pmap bootstrap code won't work for OLPC.
So I'm rewriting pmap_bootstrap(). I think that most of what's in x86/x86/pmap.c can be kept, but pmap_bootstrap() is going away. What I'm trying to do is to set up the kernel pmap from the C code, but I'm not entirely clear yet on how kvm is managed. So right now I'm in the process of learning about that.
Debugging the kernel
Open firmware has a debugger. Type help-debug for a list of commands. To debug a kernel, first load it: load u:\boot\netbsd to load it off a USB stick, or load sd:\boot\netbsd to load it off the internal SD card. I don't recommend using the internal flash ROM for debugging, because you can't replace it once it's worn out.
When you load the kernel, the debugger is automatically set up to run it. You can see what's going to get run with %pc dis. You can see the registers with .registers. To run to a breakpoint, type the address of the breakpoint in hex, followed by till. The address should not start with a leading zero. Control+T single-steps, and hop single steps over calls. go starts the program running.
Things to do
- Figure out how to use the framebuffer
- Figure out how to interface with the existing page map code.
- Make cnputc work
- Figure out how to probe PCI bus (ofwpci?)
- Lots more
