Choosing a Version
If you Google around for "68000 linux", it won't be too long before you find some mention of uCLinux. uCLinux is a project that supports a handful of CPUs without a memory management unit which makes it perfect for the 68008 (and most of the early 68k family). Several other homebrew 68k projects have used this “distribution” with some success, but a lot of them are now approaching a decade old and uCLinux itself seems to have been abandoned around 2016. The releases are still hosted on SourceForge, with the newest one building on v4 of the Linux kernel.
Collecting the Tools
So what does it actually take to compile uCLinux into something Mackerel can execute? Where does it even start? I’ve already been using a gcc-based cross-compiler for the m68k platform and that won’t change. However, I’m going to start over and rebuild the toolchain from an older version with the hope that tools from around 2016 will have better compatibility with code from the same era. I did this whole process in a Debian 9 virtual machine.
The first step is to build binutils and gcc as a cross-compiler (https://wiki.osdev.org/GCC_Cross-Compiler is a good reference for this). I settled on binutils v2.27 and gcc v5.5.0, both from around 2016 . Now I’m basically back to where I started but with older versions of my cross-compiler and tools.
In theory, there’s nothing stopping me from cloning the Linux kernel source, setting up a config manually, and building any version I like that targets the 68k. The problem is that the kernel is only one part of a full Linux system. I also need a standard library, applications, and a file system set up, not to mention drivers and startup code that will be specific to the Mackerel hardware. This is where uCLinux comes in. There are a few dozen preset configurations for known hardware. For example, Atari and Amiga have prebuilt configs that should build a full bootable system image. Let’s start with one of these to make sure the toolchain is working and to iron out any build issues before attempting to customize things further.
Let's Just Compile Something!
The uCLinux build process is pretty straight forward: run make menuconfig and select the vendor and board. Then run make. If all goes well, there’s an image file waiting at the other end. I chose the Arcturus uCsimm board for no particular reason other than the config seemed to be pretty barebones. The first dozen or so build attempts ended in failure for various reasons.
At first, uCLinux was attempting to use the wrong compiler. I updated the vendors/config/m68knommu/config.arch file to point to the correct prefix, i.e. m68k-elf-, the prefix for the toolchain I built previously.
Then the build failed due to some compiler errors in the startup code. I had to comment out most of the interrupt handling code in ints.c and remove the call to the config_BSP() function from the setup_no.c file. Obviously, this will need to be fixed at some point, but I want to see a successful build before I write a bunch of driver code.
After a lot of trial and error with different combinations of toolchains, uCLinux releases, and configs, I have a Linux image:
-rw-r--r-- 1 colin colin 2.4M Mar 3 12:49 image.bin -rwxr-xr-x 1 colin colin 204K Mar 3 12:49 linux.data -rwxr-xr-x 1 colin colin 1.2M Mar 3 12:49 linux.text -rw-r--r-- 1 colin colin 997K Mar 3 12:49 romfs.img
What does this image.bin file actually contain? According to the config, it should be a Linux kernel and a filesystem image containing busybox and some other basic Linux applications. Let’s confirm.
Looking at the Makefile for the Arcturus uCsimm configuration, there’s a make image step that runs after all the compiling is done. This step is stripping various sections from the linux ELF file and combining it together with the ROM filesystem into a single image.bin file.
The linux ELF is really just object code with some metadata wrapped around it. Using objdump on the file shows that it’s one big m68k executable.
$ m68k-elf-objdump -d linux linux: file format elf32-m68k Disassembly of section .text: 00008000 <_start>: 8000: 46fc 2700 movew #9984,%sr 8004: 23fc 0000 0000 movel #0,11d004 <_ramvec> 800a: 0011 d004 …
There’s a _start function at address 0x8000 as expected. Awesome!
The other piece of the full image is the filesystem, that romfs.img file listed above. Since that’s just a Linux filesystem, it should be mountable on the host machine. Let’s take a look:
$ mount romfs.img /mnt/rom && ls -lah /mnt/rom total 4.0K drwxr-xr-x 1 root root 32 Dec 31 1969 . drwxr-xr-x 3 root root 4.0K Mar 3 19:39 .. drwxr-xr-x 1 root root 32 Dec 31 1969 bin drwxr-xr-x 1 root root 32 Dec 31 1969 dev drwxr-xr-x 1 root root 32 Dec 31 1969 etc drwxr-xr-x 1 root root 32 Dec 31 1969 home drwxr-xr-x 1 root root 32 Dec 31 1969 lib drwxr-xr-x 1 root root 32 Dec 31 1969 mnt drwxr-xr-x 1 root root 32 Dec 31 1969 proc lrwxrwxrwx 1 root root 4 Dec 31 1969 sbin -> /bin lrwxrwxrwx 1 root root 8 Dec 31 1969 tmp -> /var/tmp drwxr-xr-x 1 root root 32 Dec 31 1969 usr drwxr-xr-x 1 root root 32 Dec 31 1969 var
Looks like Linux to me!
Finally, using hexdump, it’s easy to verify that the final image.bin really is just the kernel code concatenated with the file system image.
This is all very exciting, but there’s just one problem you may have spotted already. The image file is 2.4MB and Mackerel only has 2MB of RAM plus there will need to be some free RAM available to run programs once Linux boots up. To solve this, there are three things to try. In order of priority:
Plan A: See if there’s any more room in the config to trim a significant amount of size off of the image. Remove anything and everything that isn’t 100% critical.
Plan B: Install more RAM. This is the brute force option. I have enough 512KB SRAM chips to max out the 4MB address space, but it’s tedious and time-consuming (also expensive) to wire up another RAM board.
Plan C: Finally, if the Linux v4 kernel cannot be shrunk down far enough, maybe v3 or v2.6 could be. This is the least attractive option, but I’m not ruling it out just yet.
One other consideration I haven’t addressed is how to get this 2+ MB file into RAM on the board. One option would be to install a lot more ROM and run the kernel from there, but that takes up valuable address space and is less flexible than RAM. The other option is to transfer the image directly into RAM either over the serial port or from the CH376S USB module. My serial port is currently limited to 9600 baud, so transferring a 2MB file doesn’t sound like fun. The USB module is much faster than that, but I haven’t built it up for this version of Mackerel yet. Some things to think about there…
Plenty of work is still required to see Linux boot on Mackerel, but I’m pretty happy just to have a kernel compiling. I don’t think I’ve ever seen anyone boot Linux v4 on a 68008, so it will be fun to see if I can pull this off. Stay tuned!