Linux for Macintosh 68K Port

“I don't care if space aliens ate my mouse” or a case study in both the technical and human issues in porting the Linux OS to a new M68K target platform.
Consoling Yourself

Hitting start_kernel is the beginning of the easy road; at least on a PC, text-mode consoles are now present instead of stripes. So theoretically, hitting start_kernel on a Macintosh should have meant that getting the kernel to initialise a text console and begin showing useful debugging information was close. Nothing could have been further from the truth.

After several attempts to get the console up, I wrote some routines to print penguins and Macintosh logos on the screen (this was easier than text). Each significant point the kernel reached added a penguin to the display, and a failure point before the console came up printed a given number of burning Macintosh logos. While hardly as good as print statements, this was good enough to rapidly locate several bugs in the processing of options passed by the boot loader (little things like apparently having 0KB of memory upset the Linux memory initialisation). The code would get to the beginning of the console setup and die.

To get past this point, I had to fill in support for the 4-bit packed pixel displays that were used by the Apple Macintosh “Toby” display card. The generic bitmapped console drivers for the 680x0 port supported a wide variety of pixel formats and naturally excluded the one I needed.

Had I known at the time, I could have simply switched the machine to Mono in the display preferences, but I didn't know that action physically switched the card into a monochrome mode. Adding 4-bit packed pixel wasn't too difficult. I left the somewhat scarier 2-bit packed pixel support for later, hoping someone else would write it. The console code is also very modular on the 680x0, and these console layers (abscon, fbcon) are now used by most non-Intel ports. It is reasonable to assume that it will be driving all the ports by the 2.3 kernel series.

The machine still crashed mysteriously and all evidence pointed to a structure getting stamped on. I put guard values on either side of it and checked that they were not overwritten; I moved the structure in memory; I tried everything I could think of in order to stop it from being apparently corrupted. (No joy, no change.) After a bit of head scratching, I added code to check that the values were okay at boot and at initialisation of each subsystem. The value was wrong at the start of the C code; it was also wrong at the start of the assembler.

This looked as if the boot loader was corrupting data, yet this made no sense, since the loader would corrupt the same location, not pick on a specific variable wherever it might be located. Eventually, I used the GNU objdump tools to look at the binary I was loading. It turned out the GNU linker was at fault and in some places was loading a completely bogus address for relocation.

A new linker and the magic words “Calibrating Bogomips” appeared on the screen, followed by a hang, then much rejoicing. In many ways, the time lost to the linker bug was not that bad. Eyeballing the code in search of the mystery bug, I had fixed some twenty or thirty other serious bugs in a vain attempt to find the illusionary real bug.

I wasn't too worried about the Bogomip calibration hanging. It is hard to calibrate time before the interrupt routines and, in particular, the timer interrupt routines have been written. I commented it out and after a short while the rest of the code booted to the point of saying “Panic:unable to mount root file system”. A reasonable situation, as it had exactly no device support except the screen.

Filling In the Blanks

Getting the machine to the point where everything appears to boot is by no means a completion of the first steps of a porting project. This stage is when you finally appreciate the real problems and the scale of work remaining to be done.

The most important items to fill in were those that dealt with the most basic system resources: interrupts, memory and the I/O buses. The interrupts and several I/O subsystems are handled by a pair of 6522 VIA chips, 8-bit controllers from the Stone Age. These chips themselves are documented and their locations are known, even if some of the connections to their I/O pins are a mystery. A certain amount of mapping work and other detective information showed that the VIA chips provided the all-important system timer ticks, handled the keyboard at an extremely low (and at the time undeciphered) level and provided interfaces for external interrupts from the bus controllers.

Several other pins appear to do things such as turn the Macintosh off. Even now, we don't know what everything on the VIA chips does or if all the pins have a real use. It also turned out I got the easy end. The later Macintosh machines replace the second VIA with a device known as RBV (RAM-based video), which contains a bad emulation of a VIA chip and various other components in one piece of glue logic.

Basic interrupt handling on a Macintosh is relatively clean. A great deal of attention has been paid to keeping interrupts that need a fast response at a higher priority than time-consuming processes. That works well under MacOS, but Linux tends to take rather too binary a view of interrupts, especially in the drivers. Certain interrupts are wired in strange ways, presumably to save components; the SCSI interrupt, for example, is wired through a VIA but is effectively upside down compared to the other interrupt sources. Apple saved an inverter by using as an interrupt signal the fact that the VIA can handle either direction of state change.

I ended up with two layers of interrupt handling, which were mostly hard coded. Unlike a PC, the Macintosh interrupts are hard wired. Only the Nubus (plug in) cards change positions, and they all share one interrupt which sets bits in a VIA register to indicate the real interrupt source.

Nubus proved quite entertaining. The documentation is weak and written from the viewpoint of someone building a card for a Macintosh. It took about a week before the boot-up code would scan and report a list of which Nubus slots were occupied and the names of the devices. Once it worked, the Nubus turned out to be an extremely well-designed system with features much like PCI. Each slot is allocated a set of memory resources and can raise an interrupt. A ROM allows the OS to read each device for identification and driver information. The ROM also contains other “useful” data, including icons for the device. At the moment, these are not visible under Linux, but the intention is to support /proc/nubus/[slot]/icon.xpm at some time in the future.