Populating the root file system
The root filesystem needs an
will do whatever it is we want done. This can be a custom program
rather than a conventional UNIX-style init, but it is easier to work
with the system if you provide a working environment complete with
console shell and everything.
crosstool-ng build created a default
system root providing library code and headers. Make a copy of it:
$ cp -r toolchain/arm-unknown-linux-uclibc/sys-root rootfs
Note that no effort is being made to fix permissions right now; for a real distribution, you'd probably want to correct this, but it's harmless enough for demonstration purposes. This gets you the first basic parts of a root filesystem, containing only the libraries (and headers) future builds will use.
When it comes to embedded root filesystems, busybox is probably the best starting point. Busybox offers a reasonably complete set of core system utilities all built into a single binary, which uses the name it gets invoked with to determine what function to perform. This, coupled with a lot of links, allows dozens of the main system programs to be bundled into small amount of space. This isn't always necessary, but it is convenient for our purposes.
Busybox can be downloaded from its downloads page (see Resources). The archive is just a standard tarball and can be unpacked in a build directory. I used version 1.10.2.
Busybox is built around similar configuration tools to those used for
crosstool-ng and the Linux kernel. To build
a default configuration, run
make defconfig. This creates the default
configuration. A following
allows you to change settings; under "Busybox Settings," the "Build
Options" menu allows you to specify a static build. Do that, because
crosstool-ng build of uClibc
requires it. You might sometimes prefer a dynamically linked binary,
to reduce the size of the executable, but the size problem isn't such
a big deal on a system where nearly every binary is just a symbolic
link. As with the kernel, the top-level
Makefile has a
CROSS_COMPILE variable which holds the
prefix to use on the names of compiler tools. Set it to the same value
you used before.
To build busybox, just run
Surprisingly, there was a compilation problem during the build, which
required me to add
<linux/types.h> to the main
libbb.h header. With that out of the way,
the compile completed quite quickly.
To install busybox, you need to run
make install, but specify the root
$ make CONFIG_PREFIX=$HOME/7800/rootfs install
This copies in the
busybox binary and
creates all of the necessary links. This root filesystem now has
libraries and all the common UNIX utilities. What's missing?
You need device nodes. There are two ways to provide device nodes for a
root file system. One is to statically create all the device nodes you
plan to have, the other is to use something like
udev, which provides an automatic current
device node tree for the current kernel. While
udev is extremely elegant, it can also be
fairly slow, and embedded systems have the luxury of being able to
predict their hardware components.
Many programs depend on access to device nodes, such as
/dev/null. Device nodes are typically made
at runtime on ARM systems, using the mdev utility, part of busybox. In
fact, this has already been done by the initrd filesystem; one of the
changes above was to move the already populated
/dev mount point onto the new root
filesystem. You can manually create key device nodes, but it is
usually not worth the trouble;
Several mount points are required. The
/proc mount points are used to move the
sysfs and procfs virtual filesystems. The
/dev directory is used as a mount point for
a tmpfs filesystem, populated by
These directories do not need any other contents:
$ cd $HOME/7800/rootfs
$ mkdir sys proc dev
The /etc directory, while not strictly necessary to get to a shell
prompt, is very useful to get to a useful shell prompt. The most
important part during initial booting is the
init.d subdirectory, which
init searches for system startup files.
Create a trivial
rcS script and give it
execute permissions for initial system startup:
$ mkdir rootfs/etc/init.d
rootfs/etc/init.d/rcS file with
the following contents:
echo "Hello, world!"
Now mark it executable:
$ chmod 755 rootfs/etc/init.d/rcS