Second guessing the PowerMac firmware

I have a PowerMac G3 Blue and White, which I acquired because the previous owner didn't want it and I knew I could run OpenBSD on it. It runs quite nicely, if somewhat noisily, warmly and slowly. However, the BIOS battery is depleted, and so it loses its firmware settings and the clock resets every time it loses mains power. The clock settings can be fixed with NTP, but the loss of boot settings is inconvenient, as this requires having a keyboard (and preferably also a monitor) connected to it every time it (re)boots to manually enter boot instructions. I don't have room to have (another) standalone monitor on my desk for this machine (nor a keyboard), so I needed a workaround.

Open Firmware

The PowerMac (as well as other "New World" PowerPC Apple machines) use Open Firmware, a standardised firmware interface which most hardware vendors who aren't Intel or ARM have used at one point or another. In my experience, Open Firmware is actually pretty nice to work with. It's quite similar to the U-Boot bootloader (which though technically a bootloader is commonly used as a firmware ROM image on single-board devices) in that you get a reasonably featureful command-line interface to configure the boot settings, and both have some scripting capability. They do have their differences, as U-Boot usually loads an operating system to execute directly, and so needs a fair amount of intelligence and configurability to support the broad range of devices and configurations it runs on (as there isn't much of a standard for this in the SBC world). Open Firmware, on the other hand, only needs to load a second-stage bootstrap program to load the operating system, and thus (typically) doesn't need to be quite as smart (or at least not as cunning). Open Firmware is also standardised which means that the interface and command semantics are consistent across implementations, which rather conveniently means that the firmware interface on the PowerMac is the same as the UltraSPARC system I have on a shelf somewhere.

It's also worth noting that Open Firmware came into being in the mid nineties, predating (U)EFI (or at least the widespread use thereof) by a few years. If it weren't for the fact that all the architectures using Open Firmware died and/or lost their markets (and maybe Intel's monopoly of consumer silicon), the already-unified and extensible Open Firmware might have remained a common firmware interface [citation needed]. While an interesting historical and philosophical point, this doesn't help me get the PowerMac to boot reliably.

Booting the PowerMac

The PowerMac's firmware's default settings are as follows:

  1. Scan for hard disks, and if one is found, attempt to load MacOS from it.

  2. If that fails, check if there is a CD in the optical disk drive. If there is a CD present, attempt to boot from that.

  3. If that fails, initialise the ethernet card and start sending DHCP/BOOTP requests.

The hard disk I have in the PowerMac has OpenBSD installed on it (and therefore no partitions with an HFS filesystem), so the first step fails, however in order to get OpenBSD onto the machine in the first place I used a bootable CD. The firmware will boot from this CD automatically if left unattended. This CD contains a second-stage bootloader, which in turn loads a ramdisk kernel containing the installation environment from the CD. So in order to investigate my options I loaded the installation disk I had used and got to a boot(8) prompt. boot(8) has a couple of knobs that can be turned, such as passing arguments to the kernel so it boots in single user mode, and variables which directly influence the behaviour of boot(8) itself, one of which is the path of the root disk.

Open Firmware (from which the bootloader gets hardware information) represents attached devices as a filesystem-like tree (which can be viewed by issuing dev / and then ls at the Open Firmware prompt), however the device paths can be quite long, so devices such as storage media are usually referred to via a shorthand alias for brevity's sake -- the primary IDE hard disk on this machine is aliased to hd and the CD drive is aliased to cd. The complete list can be viewed using the devalias command.

Thus, I found that the path to the primary IDE hard disk is /pci/@d/pci-ata@1/ata-4@0/disk@0, and if I issued set device /pci/@d/pci-ata@1/ata-4@0/disk@0 at the boot(8) prompt then the CD bootloader would load the kernel from the hard disk. boot(8) will attempt to read configuration from the /etc/boot.conf file before prompting, so I thought I could put the appropriate root disk configuration into a boot.conf(8) file which would be loaded automatically. However as the CD bootloader is loaded from the CD, it won't search the hard disk for a boot.conf(8) file there. So my next idea was to remaster the boot CD ISO image to include a suitable configuration file.

Brewing custom ISO files

I downloaded a copy of the OpenBSD 6.3 macppc boot-only CD to use as a starting point. Mounting ISO images is pretty easy, however creating them is a little more involved, especially if they're intended to be bootable, so I went and looked up how OpenBSD builds bootable ISO images for macppc, and eventually found this Makefile. The appropriate incantation for creating the ISO image are these lines here:

mkhybrid -v -v -r -part -hfs \
    -hfs-bless ${.OBJDIR}/cd-dir/${OSREV}/macppc \
    -map ${.CURDIR}/../../../gnu/usr.sbin/mkhybrid/src/more.mapping \
    -A "OpenBSD ${OSREV} macppc bootonly CD" \
    -P "Copyright (c) `date +%Y` Theo de Raadt, The OpenBSD project" \
    -p "Theo de Raadt <>" \
    -V "OpenBSD/macppc  ${OSREV} boot-only CD" \
    -o ${.OBJDIR}/cd${OSrev}.iso ${.OBJDIR}/cd-dir

There is a lot of byzantine magic in that command; while mkhybrid(8) has a manual page, I'll provide a brief walkthrough of the options:

So, I copied the contents of the boot CD to a directory, created a suitable boot.conf file and then created my test ISO image using a command similar to the one in the Makefile above. After locating some blank CD's to use, I burned the ISO and then attempted to boot the PowerMac from my magic CD.

It booted into the ramdisk kernel on the CD instead of the kernel image on the hard disk, i.e. it didn't work.

Brewing custom ISO files, take 2

Given that the CD bootloader was booting the kernel image on the CD, my next idea was to replace the kernel image on the ISO file. So I repeated my previous steps, except I copied the kernel image from /bsd over the image at /6.3/macppc/bsd.rd in the CD's filesystem. This ISO image worked (and there was much rejoicing), however this had the major disadvantage that upgrading the kernel required a new CD to be burnt. As it turned out I had exhausted my stock of recordable CD's (I only had two to begin with), so I settled with this (decidedly suboptimal) solution for the time being.


I came back the next day, and lacking any other blank CD's to test with, I did some finer investigation of the PowerMac's boot process. I found a few interesting bits of information:

From this, I guessed that the boot target the PowerMac's firmware tried after failing to boot from the hard disk (as there's no HFS partition there) must be cd:,\\:tbxi -- the OpenBSD boot CD is loaded automatically, and it contains a blessed magic HFS directory with a "tbxi" file, and this file loads the second-stage bootloader and kernel from the CD. So at this point it looked like what I needed to do to make the PowerMac automatically boot from disk was to create an image with a modified bsd.tbxi file with an appropriate boot script.

Brewing custom ISO files, take 3

Firstly, I acquired more writable CD's.

I have only OpenBSD installed on this PowerMac's hard disk (i.e. there is no MacOS dual-booting), so the installer created a small MSDOS partition with a FAT filesystem for storing the second-stage OpenBSD bootloader. Open Firmware understands FAT and can load images from such a filesystem, so I made a copy of the default bsd.tbxi file and changed the boot script to boot hd:,ofwboot. This is the command I had to enter manually when booting the PowerMac beforehand, and it instructs the firmware to load the second-stage bootloader ofwboot from (the first partition of) the primary hard disk. I created an ISO image containing this ISO file with the appropriate partitioning, mapping and blessing, something like this (on a system with CVS checked out):

$ cd /tmp; mkdir -p root/boot
$ cp /usr/mdec/bsd.tbxi root/boot
$ sed -i 's|cd:,ofwboot /6.3/macppc/bsd.rd|hd:,ofwboot|' root/boot/bsd.tbxi
$ cp /bsd root/  # The ISO image needs to be at least 800K in size for mkhybrid to like it
$ mkhybrid -v -v -r -part -hfs -hfs-bless /tmp/root/boot \
    -map /usr/src/gnu/usr.sbin/mkhybrid/src/more.mapping \
    -A "OpenBSD macppc magic boot CD" -V "OpenBSD magic boot disk" \
    -P $(whoami) -p $(whoami) -o image.iso /tmp/root

I then burnt this image and tried it out. This worked (more rejoicing), and will not be affected by kernel upgrades.

This certainly a much better solution than sourcing a replacement 3.6V half-AA motherboard battery.