Wednesday, October 9, 2013

Scripted boot sequences on OldWorld PowerMacs

Having a decent bootloader on an installed system is a good start, but how do you install it in the first place?

iQUIK relies on having OFW properties or boot arguments to figure out where to look for the configuration file. Having the user pass extra arguments involves knowing those arguments - in our case, the file path and the device path. While the first we know (as a developer), the second depends on the choice of boot media and machine. Of course, we could bake in nasty logic to special case CDs, or scan all partitions on the booted device, or use magic locations in the binary, but none of this is a clean approach.

Ideally we would want to run on a CHRP machine, where we could boot a "CHRP script", which looks something like this:

FreeBSD/powerpc bootloader
FreeBSD
 $FreeBSD$ 

MacRISC MacRISC3 MacRISC4


boot device:partition,\ppc\kernel;.elf


This would let us verify the target system, and run arbitrary Forth to set up the needed parameters (or even interact with the user). A good example of a complicated CHRP script is the MacOS ROM file on NewWorlds, which is a pretty large blob of Forth followed by an embedded ELF image.

But we're on OldWorld. No bootinfo support. What can we do? We can do pretty much the same, except we have to prefix our Forth with a machine code stub that will call into PROM to interpret the following Forth code. Afterwards, we can pass control to the regular boot loader, which is embedded right after the Forth words. Whereas before the boot partition contained just the iQUIK boot block, now it's a "sandwich" of simply concatenated Forth hand-off stub, the Forth code and the iQUIK boot block. All we have to do is compute the load address correctly, such that the iQUIK boot block falls on its expected linked address.

Thursday, October 3, 2013

iQUIK updates

iQUIK (my fork of QUIK, the OldWorld Linux bootloader) has seen a few enhancements.
  • Better 3400c support. Now that I have a CD drive, I verified that the media bay ATA only needs to be hidden when there's nothing attached to it. CD booting is still at large (but will probably work just fine).
  • PDQ/Wallstreet documentation. This includes patches to get booting via PCMCIA/CF and via internal IDE, as well as kernel options needed. CD booting is still untested (and will likely cause the nvramrc patches needed to grow).
  • Bug fixes around large disk access that was preventing from correctly reading partitions larger than 4GB. There's still a problem with partitions on large disks, so you should probably create a '/boot' partition. I wonder if certain ATA packages on old OF have issues... Needs investigation.
  • !cat to display short files
  • Bug fixes to path handling and compatibility with yaboot.conf used by debian installer.
  • Turn on shallow setprop support for OF 1.0.5 (pending testing to see if it's needed)
The current to-do list:
  • Clean-up command parsing/handling a-la U-Boot
  • Investigate partition access on large disks
  • Automatic nvramrc patching to ease deployment
  • Janitorial:
    • malloc/printf replacements
    • claim() load area and prom_exit() path should close all opened handles, release all claimed memory (end goal is being able to quit and load again)
  • Add !parts to scan and display partition/fs info for devices
  • Rewrite shallow setprop support to work for all props, not just initrd-base/initrd-size
  • BSD kernel loading
  • Partition-less device support
  • FAT support
  • ISO9660 support
  • EXT4 support
  • FCode loading
  • Forth loading
Current test plans:
  • OF 1.0.5
  • CD booting
  • More machines (anyone?), maybe try some different CPU modules on my PDQ
It's actually sort of interesting that there's no guarantee that the same OpenFirmware release (i.e. 2.0.1) will have the same bugs. And I'm not just talking about device support, because that's fairly obvious (different I/O chipsets need different drivers). For example, the 3400c has 'setprop' issues that need to be worked around, but the Wallstreet doesn't. The Wallstreet is obviously newer, but there's no way to draw a line between support without trying it on every model (so might as well always use the work-around).

As usual, everything is at https://github.com/andreiw/quik

A

Wednesday, September 18, 2013

Booting PDQ with iQUIK

Got my PDQ today. Boots into MacOS 9 and doesn't boot (with BootX) into some installed Ubuntu.

We can fix that :-).

Popped-in my PCMCIA CF adapter with the debian installer files and iQUIK installed. OpenFirmware 2.0.1 is the same version as on the 3400c, but refuses to see anything beyond the PC card bridge.

What?

Anyway. For whatever reason it just doesn't enumerate them. Easy to fix. Put the following in your nvramrc (unless you really like typing):
: probe-card select-dev " probe-pccard" my-self $call-method unselect-dev ;
: my-boot " ata2" probe-card " ata3" probe-card boot ;
You can setenv boot-command to be my-boot now.
0 > my-boot ata3/pccard45,401@0:0
...of course, you may have a different name of the actual CF device. Mine was made by Sandisk, hence the 45,401.

Linux loads file, then screen goes psychodelic with the LCD obviously driven incorrectly. Still working on the "real" fix, but you can convince Linux to just use the OF framebuffer instead of re-initing the video with "video=ofonly video="offb". Yes, you need both.

This PDQ is a pretty nice machine. 400MHz G3 and 512MB RAM. It's pretty usable even in 2013, which is pretty damn quick for a machine made in 1998. Of course, back in '98 it probably cost a kidney.

Have fun...

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.

iQUIK - the new old PowerMac bootloader for OldWorld machines

About three quarters of a year ago I found a PowerBook 3400c in the trash pile at work. Given my ongoing nostalgia for PowerPC and OpenFirmware, I couldn't pass up the opportunity to reminisce with this PowerBook.

It is by far the oldest mac I've had. The UMAX S900 I dumpster dove for back in 2002 was a dual 604e. This baby is a 603ev. That's three generations behind my old G4 iBook. It's OF is slightly less broken than 1.0.5 on the S900 - at least it doesn't default stdio to serial, and doesn't need nvramc patches to boot from disk. The 3400c booted into MacOS, but clearly it was asking for Linux.

Unfortunately, the best you could do is install via old miBoot floppies, and get a 2.2(!)-based Debian system, just like I had to on the S900 ten whole years ago. Of course you could painfully and slowly upgrade releases, only to realize you can't actually boot a 2.4 kernel with an initrd with the OldWorld bootloader, QUIK. Staring at a "VFS panic" message after investing a week into slowly getting to /that/ point would be bound to enrage most people just toss the aging laptop into trash...

I guess I have an odd fascination with obsolete hardware. I've decided to take ownership of the QUIK code, fixing many of its bugs and limitations. The effort is now called iQUIK and is available for everybody at https://github.com/andreiw/quik.

iQUIK is a Linux bootloader for "OldWorld" PowerMacs. "OldWorld" PowerMacs are all machines that have OpenFirmware, but that don't have built-in USB. Due to a lacking firmware implementation, the typical Yaboot bootloader cannot be used on "OldWorld" machines.

Some highlights:
  • You can install and boot Debian Wheezy (the current stable release)
  • You can install the boot loader on any medium, including floppy.
  • Initrd image work fine, as do 2.2., 2.4 and 2.6 kernels.
  • Can boot any kernel/initrd/argument combos without a quik.conf.
  • Can list filesystems (only ext2 supported at the moment).
  • Works around OF/hw bugs with an innovative shim layer. At least the many bugs on the 3400c.
  • Single-stage installation for robustness, using partition-zero boot block and bootstrap partition.No more LILO-style block maps.
You can see the fairly exhaustive documentation at https://github.com/andreiw/quik/blob/master/README. It's only really been tested on a 3400c, although I'm getting a G3 PDQ (that's the last OldWorld PowerBook!) for testing soon.

...but that's not what I wanted to write this post about. I was curious if I could boot iQUIK successfully with the QEMU PowerPC system emulator and OpenBIOS. Turns out yes, but it's a bit painful. The problem is that OpenBIOS doesn't support the mac's "partition-zero" booting, e.g.
0 > boot ata0/ata-disk@0:0
How does "partition-zero" booting work? The mac partition table contains a structure that describes the disk offset of the bootloader, its size and where to load it. The bootloader has to be plain binary (i.e. not XCOFF). iQUIK leverages this by copying the boot code into it's own partition and setting up the boot descriptor in the partition table to point to it. http://www.opensource.apple.com/source/bless/bless-11/README.BOOTING is a pretty good document that describes this more in-depth.

So how to boot on QEMU, then? We could cheat and load the non-munged ELF file the boot code is created from, but I don't have my 3400c nearby to compile with. Instead we will convince OpenBIOS to load our code just like a PowerMac would.

This example assumes:
Steps are pretty simple. We launch the simulator like so:
qemu-system-ppc  -hda floppy.img -hdb install.img -serial stdio
At the "0 >" prompt in the framebuffer window we type the following commands to switch the firmware to serial port for input/output. You might wonder why we couldn't use the "prom-env" option to set output-device and input-device? It doesn't work. Heh.
0 > " /pci/mac-io/escc/ch-a" output
0 > " /pci/mac-io/escc/ch-a" input
And now paste the rest of this script into terminal you ran QEMU from. Of course, you could put it on bootable media, but...
0 value ih
\
\ size and load-base need to match
\ SECOND_BASE and SECOND_SIZE in
\ quik/include/layout
\
10000 value size
setenv load-base 3e0000
\
\ boot-file specifies where iQUIK will look
\ for the configuration file. This corresponds to
\ the second ATA disk (-hdb), first FAT partition.
\
setenv boot-file /pci/mac-io/ata-1/disk@1:1/yaboot.conf
\
\ OpenBIOS doesn't know how to parse the MAC "partition-zero"
\ boot descriptor, so we'll manually load it. XXX:2 refers
\ to the Apple_Boostrap partition on -hda, which in the floppy.img
\ image is the first parition (wonky indexing... yes).
\
\ I don't really understand why OpenBIOS needs the seek to 0.
\ Seems like a bug...
\
" /pci/mac-io/ata-1/disk@0:2" open-dev to ih
0 " seek" ih $call-method .
load-base size " read" ih $call-method .
\
\ Based on OpenBIOS libopenbios/bootcode_load.c
\
load-base saved-program-state >sps.entry !
size saved-program-state >sps.file-size !
bootcode saved-program-state >sps.file-type !
-1 state-valid !
\
\ Now can boot. For completeness probably should set
\ /chosen/bootargs but iQUIK can handle the inconsistency.
\
ih close-dev
go
You should now see iQUIK in the framebuffer window (not the serial, because we haven't set the input-device and output-device env variables). You can press the tab key to show the boot labels available. Just press enter to boot the default 'install'.

There you have it.
Of course it would be ideal to just implement bootsector-zero support or even this hack as an nvramrc patch, but unfortunately QEMU PPC has no NVRAM support. Boo...