M8SBC-486: Linux
[Go back to the main page of this project] [Previous part]

First plans
From the beginning of this project, I always thought about running some sort of Linux on this board. While developing the first version of the BIOS, at some point I looked into ways to make Linux work on this board. I found out that the biggest constraint of this board is that it has only 4 MB of RAM. I ruled out newer Linux kernel versions because they probably wouldn't fit and even if they did, there wouldn't be much RAM left for user applications. After considering various factors, including those described later, I decided to use the 2.2 Linux kernel from 1999, specifically its latest release 2.2.26 from 2004.
Boot process
Second thing I considered was how to actually boot Linux on this device. At that time, my BIOS could already load basic operating systems that did not rely heavily on BIOS service interrupts. I instantly ruled out using existing boot loaders, such as GRUB and LILO, because my BIOS was unfinished at that time. I searched online for information about the Linux boot process on a standard BIOS x86 machine and found that it was documented well, even though it was quite old. It was also not difficult to implement. I actually had a working base for a simple operating system that after loading, switched to protected mode and could interact with the FAT32 filesystem from which it loaded. I decided to use it as a base for my own Linux bootloader!
So, how said moment ago, first thing I did was to search how the Linux boot process work. In the Kernel 2.2.26 source, there are two files in /Documentation/i386/ directory named boot.txt and zero-page.txt. They explain how the Linux boot process works on the PC. In summary of this documentation: Linux expects the bootloader to load kernel image, set up the "zero-page", set up processor in protected mode and pass execution to the kernel.
The "zero-page" (historically located at physical address 0x90000) is essentially a handover note from the bootloader to the kernel. It tells it things like how much memory is present, memory map, video mode, etc. On a standard PC, a bootloader like LILO or GRUB would call BIOS interrupts to gather this data. Since I knew exactly what hardware I was running, I could hardcode the answers. I wrote a C function to manually populate the zero-page structure in memory. The zero page is well documented in the kernel documentation, so it didn't take me long to implement it.
Another thing I had to implement properly was the GDT, as Linux initially expects the "kernel code" to be at GDT entry 0x10, rather than 0x08 as in many hobby operating systems.
Compiling and running Linux
Before trying my bootloader, I had to get the kernel by compiling it by source. This way I can configure it to my needs as well. To do this, I downloaded Debian 3.0 image off internet and installed it in a VM.

I downloaded the 2.2.26 kernel source from the kernel.org archives, copied it onto the VM hard drive and started reading this kernel version configuration and compilation instructions that I found on the internet.
I was worried about the missing DMA and second PIC in the system. As it turns out, DMA in Linux is mostly used by device drivers rather than the kernel itself, so I thought perhaps this would be okay. Additionally, some drivers allow you to disable the DMA requirement and use "virtual DMA" (like the floppy driver). To start, I disabled everything that required DMA.
The second PIC might be more problematic. After some research, I found out that the ATA/IDE driver uses interrupt 14. It might be harder to bypass because the interrupt line is unconnected on my board. I decided to ignore it and hope that this driver would run without interrupts.
During configuration, I also tried to keep the kernel size as small as possible to make more RAM available to user applications. I disabled many features that are unnecessary for my board or my goal of simply running Linux. If I recall correctly, the initial kernel was 600 KB and the compressed zImage was 340 KB.
Then, I placed the zImage in the bootloader image and first tried running the result in the PCem emulator. Surprisingly, something showed on the screen! I saw the "Uncompressing Linux..." message, followed by "Not enough memory". I had made a mistake by incorrectly populating a memory size related entry in the Zero Page. After fixing it, I made further progress and actually saw the kernel entry! I was surprised by the result. At this point, the bootloader code looked very similar to the final product. I continued working on it. If I recall correctly, I reached the phase where it attempts to execute the init process. When I tried what I had made on the M8SBC-486, I was surprised again when it got to the kernel entry, but it started having issues during partition detection.

Screenshot from PCem
Second PIC issues
After booting the kernel on the board, I continued experimenting on the M8SBC-486 instead of in PCem. The missing second PIC turned out to be a big issue. Any disk access took a long time because the IDE driver timed out waiting for an interrupt. I received "Lost interrupt" messages and sometimes the system would freeze randomly. I thought I could disable it, but then I found out that I could remap it using a kernel line parameter instead. I added ide0=0x1f0,0x3f6,5 to the command line and soldered a jumper wire (the first one in the entirety of this project) to connect interrupt 14 to interrupt 5 on the board (on the ISA slot closer to the CPU).

First bodge wire in this project! At least it's not essential for the board to work.
I powered the board and finally encountered the kernel panic caused by the missing init process! That was a big success, as the only thing I needed to do was create a root file system. I considered using an initramfs (which is a root file system in RAM), but due to the low amount of RAM, I decided to put it on the disk instead. After all, one reason I wanted to make the IDE work was to use the disks for storage on Linux.

The bootloader source code is available here: https://github.com/maniekx86/linux_m8sbc_boot.
Booting into the shell
Next, I started working on the root partition and the init process. I decided to compile a simple program that prints "Hello World" on the screen and use it as the init process. I wrote the program on the Debian 3.0 VM and compiled it statically. To create the root file system, I partitioned the second hard drive with an ext2 file system. It's the newest file system that Linux 2.2 supports. I put the root file system on the second hard drive because it was easier to just plug it in (Linux detected the second hard drive without any problems on the M8SBC-486) than to modify the bootloader to work with multiple partitions.
After putting the test init program on the second hard drive, I booted Linux with modified boot arguments to search for the root partition on /dev/hdb (the second hard drive). I encountered one issue. The mkfs.ext2 program on my computer created an ext2 partition with an inode size that was too large (I don't remember the exact values, but today's default size is bigger). The kernel wouldn't mount the partition because of this. I had to reformat the root file system on the hard drive with a smaller inode size, which can be done with an extra argument to the mkfs.ext2. After solving that problem, I saw "Hello World!" on the screen. Amazing!

Init process running! Notice the two hard drives.
The next step was to get a shell to use with Linux. I decided to use BusyBox because it's lightweight and offers a wide selection of commands and tools. I had to use an old version because newer versions probably don't work well with older Linux kernels. I chose version 1.0 from 2004. I did the same thing as with the old kernel - I downloaded the source from the archive, copied it onto the Debian 3.0 VM, configured it and compiled it. I did so without encountering any real problems. First, I configured BusyBox to include basic tools (like shell, basic utilities, no networking). After getting the static compiled BusyBox executable, I created a root file system using instructions I found online (for example, I had to link the tool names to the BusyBox binary and set up a few configuration files). I connected the hard drive back to the M8SBC-486, turned it on and after a few moments, I saw the BusyBox shell!

I couldn't type anything because I hadn't started implementing the keyboard controller in the FPGA design yet. For a quick test before doing that, I thought I could just run the shell on the serial interface. As you might have noticed on the photos, the kernel detected the serial ports without any problems. After changing one configuration line, the console appeared on the serial port and I could type! Below is the CPU and memory information. The low Bogomips value was due to the fact that the L1 cache was not working yet when I was working on Linux. I fixed that later (see bottom of Initial hardware and chipset page) and got 23.91 Bogomips.

I decided to quickly finish implementing the keyboard controller on the board. After a few hours, I had it working well enough to type in the terminal. The kernel still complained about a "timeout", as seen in the photo of the "Hello World" init process, but it was usable. At that point, I had a working Linux homebrew, which was awesome! Using Linux on hardware that you designed from scratch (from the schematic to the PCB to the chipset) is a truly wonderful feeling.
Further experimenting
I continued experimenting with the Linux:
- I added more utilities to Busybox, including "top." I also compiled other programs from sources, such as the nano text editor and the tint (terminal tetris).

- I managed to connect the board to the internet using SLIP networking. SLIP is an old, lightweight protocol for routing packets via a serial port. I managed to get ping, wget, and telnet working among other things. The coolest thing I did was compile httpd into BusyBox and host a simple website on my board!

- Linux has more drivers, so it supports more devices than my BIOS did. Some time later, I modified the bootloader to use the partition table and load from a partition rather than from an entirely formatted drive. This freed up an ATA device on the ATA bus. With that, I was able to connect a CD-ROM which worked perfectly! I only had to enable ISO9960 support in the Linux kernel config and then I could browse CD discs without any problems! It was great to see more standard PC components work with my board. I tried to connect and use a floppy drive, but it failed with kernel hangs and crashes probably due to the missing DMA (I tried using the virtual DMA, but it seems to be broken on my board as well).


Browsing Windows 95 setup disc on the M8SBC-486!
Summary
Running Linux on the M8SBC-486 made me realize how much I had actually managed to make the board PC compatible. Despite the missing second PIC and DMA, it was capable of "reusing a lot of code from standard PCs" - I mean, Linux was written for PCs, and I managed to run it on my board without encountering any major issues. At this point, I was about to slow down the development of the board, but not long after, I successfully booted DOS for the first time, which opened up more work for me that was addictive! Read more about it on the next [upcoming] page!
More coming soon
Last updated: 24/02/2026




![Validate my RSS feed [Valid RSS]](http://maniek86.xyz/cmsesus/uploads/image/valid-rss-rogers.png)
