Close

Linux on the 68008: Signs of Life

A project log for Mackerel 68k Linux Computer

A series of m68k computers designed to run Linux

colin-mColin M 03/08/2022 at 03:553 Comments

I’ve compiled Linux hundreds of times, but it’s almost always automated behind a build system of some sort. I don’t spend that much time configuring it once it’s working as expected and I’ve spent even less time parsing through the source code.  The last few days have been eye-opening in more ways than one. Since the last log, I’ve managed to shave the Linux image file down to 1.5MB. It will now fit comfortably in the 2MB of RAM currently installed on Mackerel, but will it run?

CH376S USB Module
CH376S USB Module connected to the 68008 bus

The first thing to do was to hook up the CH376S USB module again. I haven’t used this since the first prototype build, but it’s essentially a USB-to-parallel adapter with support for FAT filesystems. The setup involves hooking it up to the CPU’s 8-bit data bus, connecting some control lines, and memory-mapping it like any other peripheral. There’s a command/response API for querying the file system and transferring data. My current implementation takes about 3 minutes to transfer the entire 1.5MB Linux image into RAM, which is terrible, but it’s a lot less terrible than sending it over a 9600 baud serial port. I added an option in the bootloader to automatically copy the image file into RAM and jump to the entry point.

At this point, Linux should be running, but you can’t tell. There’s no console activity, but the 68901’s LEDs do eventually show the pattern for “unhandled exception”, so something happened. Let’s figure out how far it went.

The Linux startup process varies depending on the CPU architecture. Fortunately, the m68k version is pretty straightforward. In the kernel source code, there’s a file called linux/arch/m68k/68000/head.S. This is the starting point, i.e. the assembly function called _start is defined here. This function will be put directly at the start of the compiled image. In this case it ends up at memory address 0x8000 and jumping to that address will kick off the boot process.

Since I have no idea if this code is actually running properly, I hacked in a few assembly statements to update the LEDs on the 68901 as the code progressed through _start. This at least proves that object code in the image file is executing and it gives me a place to start debugging. I haven’t actually implemented a serial driver or any platform-specific initialization code yet, but I’m just hoping to see something work. After the slow process of recompiling the kernel and booting from the USB drive, the LED lights show the expected pattern and then quickly change to the “unhandled exception” pattern again. Something happened!

Tracing through the code further, _start eventually calls jsr start_kernel. This is where the official jump to the Linux kernel happens. The start_kernel() function is defined in linux/init/main.c. Taking a similar approach to the assembly code, I added a few LED pattern commands in here, only this time written in C. Rinse and repeat, success! I know the CPU is at least getting to the start of the Linux kernel initialization code.

By now, the LED patterns are becoming cumbersome, but I’m still no closer to a working serial driver. Time to cheat! My serial output code for the 68901 is really simple, so I just copy-pasted the bare minimum code right into the main.c kernel source. This does not give Linux a way to communicate, but it acts like a more useful LED display, the old “printf debugging” routine. Sprinkling mfp_puts() messages all over the place and rebuilding gave me a better idea what was happening:

Booting from USB...
...........................................................................................................................................................................................................................................................................................................................................................................
Image loaded at 0x8000
Booting image...
lockdep_init()
set_task_stack_end_magic()
smp_setup_processor_id()
debug_objects_early_init()
boot_init_stack_canary()
cgroup_init_early()
local_irq_disable()
boot_cpu_init()
page_address_init()
pr_notice()

Everything up to pr_notice() is running successfully. Digging deeper, it looks like pr_notice() ends up calling printk(), the kernel’s main output function, and ultimately a null function pointer is called which throws the “unhandled exception” error.

Commenting out the internals of printk() allows the bootup to go a little further. I’m even able to manually print out the version string (kind of hack, but pretty cool to see):

Linux version 4.4.0-uc0 (colin@debian) (gcc version 5.5.0 (GCC) ) #11 Mon Mar 7 20:17:15 EST 2022

Eventually, the code reaches mm_init() and does not go any further. There’s a lot of code in there and I think it’s time to take a break from manual debugging and figure out how to write a serial driver. I’m sure there are plenty of other issues with the kernel config and filesystem I’ll also need to deal with, but I’m happy to see a Linux system starting to take shape.

Discussions

Stephen Moody wrote 03/14/2022 at 18:05 point

I find this very interesting, Is it the current Linux code? I have a 68000 comupter that I am trying to write a basic OS for and that is the focus but it would be fun to try and get it up and running

  Are you sure? yes | no

Colin M wrote 03/14/2022 at 18:34 point

I'm using the newest release of uCLinux that I could find (around 2016). The Linux kernel included is version 4.4 - not the latest, but it's not completely ancient.

  Are you sure? yes | no

Gravis wrote 03/15/2022 at 00:33 point

So I looked at your computer specs and it seem like it has enough memory to run the latest build of Linux but you will need to write drivers for your system.

uCLinux has been used on far more restricted m68k systems: https://www.bigmessowires.com/68-katy/

  Are you sure? yes | no