Firmware Linux

Firmware Linux is a bootable single file linux system, based on busybox and uClibc. After downloading the source code, extract it, read the README, and run "./build.sh". This will (eventually) create a self-contained firmware-uml executable you can use to try out an emulated version of Firmware Linux.

Prebuilt versions are available: The basic build is 2.5 megabytes (which includes the linux kernel, all command line utilities, and the minimal set of shared libraries). The full development environment is 15 megabytes (the base system plus enough development tools for Firmware Linux to rebuild itself from source code).

News

Read the development log to see what I've been up to on this project.

Here's the length patch I'm using on lilo.

I wrote a Quick and Dirty User Mode Linux HOWTO if you've never played with UML before.

What is it?

Firmware Linux is a bootable single file linux system.

Firmware Linux is one file containing a kernel, initramfs, read-only root filesystem, and cryptographic signature. You can boot Linux from this file as if it was a normal kernel image (a slightly modified LILO is required, patches for GRUB and other bootloaders are a to-do item). You can upgrade your entire OS (and any applications in the root filesystem) atomically, by downloading a new file and pointing your bootloader at it.

Firmware Linux is a Linux distro using busybox and uClibc as the basis for a self-hosting development environment.

When the Firmware Linux project started, busybox applets like sed and sort weren't powerful enough to handle the "./configure; make; make install" of packages like binutils or gcc. Busybox was usable in an embedded router or rescue floppy, but trying to get real work done with it revealed numerous bugs and limitations.

Busybox has now been fixed, and in Firmware Linux Busybox functions as an effective replacement for bzip2, coreutils, e2fsprogs, file, findutils, gawk, grep, inetutils, less, modutils, net-tools, patch, procps, sed, shadow, sysklogd, sysvinit, tar, util-linux, and vim. (Eventually, it should be capable of replacing bash and diffutils as well, but it's not there yet.)

The base system consists of uClibc-0.9.28, busybox-1.1-pre1, and linux-2.6.13.2. (Currently, bash-2.05 is also included due to limitations in the busybox built-in shell. This is a temporary measure until Busybox is further improved.)

The build toolchain uses the base system plus binutils, gcc-core, bison, linux-libc-headers, and make.

The install software uses lilo, which needs bin86 and as86 to build it.

The full development system (which creates a development environment sufficient for Firmware Linux to rebuild itself from source) adds m4, flex, bison, diffutils, zlib, bin86, and nasm.

Busybox is effectively replacing all the following packages: bzip2, coreutils, e2fsprogs, file, findutils, gawk, grep, inetutils, less, modutils, net-tools, patch, procps, sed, shadow, sysklogd, sysvinit, tar, util-linux, and vim. (Eventually, it should be capable of replacing bash and diffutils as well, but it's not there yet.)

Design

The single file packaging combines a linux kernel (either a bootable kernel or User Mode Linux executable), initramfs, squashfs partition, and cryptographic signature.

In 2.6, the kernel and initramfs are already combined into a single file. At the start of this file is either the obsolete floppy boot sector (just a stub in 2.6), or an ELF header which has 12 used bytes followed by 8 unused bytes. Either way, we can use the 4 bytes starting at offset 12 to store the original length of the kernel image, then append a squashfs root partition to the file, followed by a whole-file cryptographic signature.

A User Mode Linux executable should still run just fine (loading is controlled by the ELF segments, the appended data is ignored). Note: don't strip the file or the appended data will be lost.

Loading the bootable kernel image requires a modified boot loader that can be told the original size of the kernel, rather than querying the current file length which would be too long. Hence the patch to Lilo allowing a "length=xxx" argument in the config file.

Upon boot, the kernel runs the initramfs code which finds the firmware file. In the case of User Mode Linux, the symlink /proc/self/exe points to the path of the file. A bootable kernel needs a command line argument of the form firmware=device:/path/to/file (it can lookup the device in /sys/block and create a temporary device node to mount it with; this is in expectation of dynamic major/minor happening sooner or later). Once the file is found, /dev/loop0 is bound to it with an offset (losetup -o, with a value extracted from the 4 bytes stored at offset 12 in the file), and the resulting squashfs is used as the new root partition.

The cryptographic signature can be verified on boot, but more importantly it can be verified when upgrading the firmware. New firmware images can be installed beside old firmware, and LILO can be updated with boot options for both firmware, with a default pointing to the _old_ firmware. The lilo -R option sets the command line for the next boot only, and that can be used to boot into the new firmware. The new firmware can run whatever self-diagnostic is desired before permanently changing the default. If the new firmware doesn't boot (or fails its diagnostic), power cycle the machine and the old firmware comes up. (Note that grub does not have an equivalent for LILO's -R option; which would mean that if the new firmware doesn't run, you have a brick.)

Notes

Currently, the version of gcc it builds and uses only has a C compiler, not c++. This restricts the packages I can build with it, and you'd be amazed what kind of things need c++. (Python is the one I'm really missing at the moment.) Possibly uclibc++ could help here, but I'm also looking at tcc instead of gcc and binutils. (tcc doesn't quite build an unmodified Linux kernel yet, and who knows what other packages would need to be tweaked, but it's definitely worth a look once version 1.0 comes out. Perhaps some kind of front-end could make it do c++?)

User Mode Linux is used during the build for a number of reasons. The kernel headers used to build the C library may be newer than the kernel the system doing the build is using, and this may result in programs linked against this C library trying to use new features the existing kernel doesn't have. This tends to result in programs segfaulting. Building a UML kernel to run these programs under during the build solves this problem, by translating the calls into ones the host system understands.

UML also avoids the need to run the build as root. The build needs to mount partitions, associate files with looback devices, create device nodes, create absolute paths requiring new entries in the root directory, chroot, and so on. Doing all of this within the emulated UML environment avoids the need for root permissions on the host.

That said, if you're running the same kernel version the Firmware Linux build is using, and you have root access, you can skip the UML wrapper to speed up the build and make things more easily debuggable. I need to make a wrapper script for this, but basically in sources/scripts, stage 0.0, 1.1, and 2.2 are still needed, then 2.3 to package the final result (although 2.3 depends on an executable built in 0.1).

How to build it

Run "./build.sh". This runs all the stages (numbered files in sources/scripts) in sequence. It'll start by downloading all the source code needed to build everything (which it'll keep around in the sources/packages directory for future builds). If you just want to download the source, run "sources/scripts/0.0-*". The 1-* stages create a cross-compile environment independent of the parent system. The 2-* stages build the final system by using that cross-compile environment.

Contact

My name is Rob Landley and my email address is rob@landley.net. My development log is probably the best way to keep track of what I'm working on, although I'll start a mailing list if enough people pester me.