Sunday, September 15, 2013

Working around OFW and kernel bugs on OldWorlds

Trying to boot up a 2.6 kernel with initrd on the 3400c I ran into a couple of bugs that were show stoppers. One resulted in the kernel not finding it's initrd image ("VFS panic"), the second in a hard hang probing a non-existing media bay ATA.

To understand the first bug (and the workaround for both) we need to see how the OFW structures are used in kernel bootup.

On PowerPC, the Linux kernel can be called anywhere. The early boot code is capable of running at any address (it is reloc-safe), but the kernel needs to be moved to its linked address before it can be run. Of course, the linked address might conflict with OFW mappings, hence the early boot code needs to exit the firmware services before relocating and jumping to the rest of the kernel. Of course, the rest of the kernel needs the device tree information to initialize the platform, and because OFW has been already quiesced, the early boot code needs to make a copy of the device tree structures, thereby "flattening" it. This early OFW-handling code is called from kernel/head_32.S and is in arch/powerpc/kernel/prom_init.c(prom_init).

Before flattening the device tree, prom_init first queries various bits and pieces and uses the OF setprop call to store them inside the /chosen node. The /chosen node represents information passed from OFW to the OS, and Linux sets properties in that node for itself to find later. For example, the initrd information (base and size), which is passed in registers to the kernel from the bootloader, is then stored in /chosen/linux,initrd-start and /chosen/linux,initrd-end. This information is then copied using the OFW getprop call when the dt is flattened later inside prom_init.

The problem on the 3400c with OFW 2.0.1 is that the setprop client call doesn't actually make a copy of the data, but just stashes the passed pointer away. Because the values set during calls from prom_init are built on the stack, the later getprop calls during dt flattening return garbage.

So how can we deal with it? Because we know the kernel only makes calls to the PROM before it trashes the memory map, we can "shim away" the real client interface with a filter inside the bootloader, that can
feed the kernel the right values and fix up any broken OF behavior. In case of the "shallow" setprop problem above, I simply feed the correct initrd base and end addresses when I detect accesses to /chosen/linux,initrd-start and /chosen/linux,initrd-end. Of course, a much better and generic way would be to make deep copies of any setprop-ed field onto the bootloader heap...

Come to think of it, it's highly likely older OF versions (like the infamous 1.0.5) have the same bug.

The second bug involved a hard hang scanning the media bay ATA controller on the 3400c. I don't have a CD drive, so I have no idea if this only happens with the bay empty. There are no kernel parameters to black list devices. Since we already have a shim to intercept and transform OF client interface results, I simply have hidden /bandit/ohare/media-bay/ata away from the kernel.

No comments:

Post a Comment