Using CompactFlash Cards in Your Embedded Linux System
After reading Bruce Perens' articles (``Building Tiny Linux Systems with Busybox, Parts 1 and 2'') in the first two issues of Embedded Linux Journal, I decided to build my own system. It would be impossible to use one of the full-size major distributions like Red Hat or Mandrake on such a small CompactFlash card. There are some small distributions that might work, such as Tom's Root/Boot disk or the LEM distribution. You also could use BlueCat or Hard Hat Linux. I decided that Bruce had given me enough information to build my own, so I chose that path. It was a bumpy road, but you can learn from my mistakes and build your own system. I recommend that you try it to learn how the system works.
I used the fdisk command to create two partitions on the CompactFlash card. The command you run is fdisk /dev/hdc. I created a partition for the root directory and one for the /var directory. The root directory should be mounted read-only, so none of the files will get changed even if a program goes crazy when your system is running. The /var directory is writable, so any parameters or changeable data can be placed under /var. I created each of these partitions as 16MB partitions, even though only 5MB of space is used on the root partition. If you are using an 8MB CompactFlash card, be sure to make the root partition 5MB in size and the /var partition 3MB in size.
The second thing you will want to do is build a kernel. I chose the 2.2.18 kernel. Version 2.4 has been released, but I didn't need any of its extra features and I already had the source code to version 2.2.18. If your target system is low on memory, you might consider a 2.0 kernel if it has all the drivers you need. My target system is a Pentium, but if yours is a 386 or 486, make sure you build a kernel that will run on your processor. If you use a 386, you'll probably need to enable Math Emulation because this processor doesn't have a built-in math coprocessor.
It is always a good practice to include only the drivers you need in your kernel. Remember, your target device isn't a 30GB hard drive! You can use modules if you like, but you'll need to load them in your code when you need them. Most distributions give you utilities like depmod and others to make sure all the proper modules are loaded in the right order. If you depend on utilities like these, you'll need to make sure they are installed on your embedded device.
The only driver you'll need to support the CompactFlash card is the IDE disk driver. Linux thinks your CompactFlash card is an IDE hard drive. No other drivers are needed to access it.
After building your kernel, you need to build a root filesystem. I won't go into all the details you need to build a filesystem as this is covered in Bruce Perens' article. I added a web server and some networking tools to my filesystem though. Here's the list of software packages I used:
linux-2.2.18.tar.gz--the Linux kernel
busybox-0.48.tar.gz--many Linux utilities
net-tools-1.57.tar.gz--network support applications
lilo-21.6.1.tar.gz--a boot loader for the kernel
thttpd-2.20b.tar.gz--a small web server
ash-0.2--a small /bin/sh compatible shell
I also copied the following library files from /lib on my Mandrake system. These libraries were used by the packages above: ld-linux.so.2, libcrypt.so.1, libnss_files.so.2, libc.so.6, libnsl.so.1 and libpwdb.so.0.
I used two utilities to help me determine what libraries were needed on my embedded system. The ldd command tells which libraries are dynamically linked to an executable. If we were trying to get our system to run on a 2MB CompactFlash card or a floppy disk, we would need to be very careful how we compiled our code to make sure they were as small as possible. I'm using a 32MB card, so there is a little breathing room. I made almost all of my executables dynamic and copied the proper libraries to my system. If disk space is a problem, you might consider a replacement library. There are libraries that are much smaller than the standard libc library included in most distributions.
The other utility I needed to use includes the strace command. The web server software needs to become the user ``nobody'' on your embedded system. The code needs a few extra libraries to read the /etc/passwd file. For some reason, ldd didn't tell me the web server needed these extra libraries. The strace command will run your target program and tell you every file it tries to open. This is how I discovered the additional libraries that were needed.
There are around 200 files in my embedded Linux filesystem. We don't have the space to cover every one in this article. Please refer to Bruce Perens' article for details on most of these files. I will summarize which files belong in each directory of our filesystem. You can download these files from the Embedded Linux Journal web site:
/bin--the bin directory contains user utilities. Most of these come from the BusyBox program.
/boot--the boot directory contains the kernel and the LILO boot loader files.
/dev--the dev directory contains device files needed in our system. The MAKEDEV command can help you create these. Remember that these take up disk space, so only include the ones you will need.
/etc--the etc directory contains configuration files needed for your system. We'll need eight files for our system. I will go into detail about these files a little later.
/lib--shared libraries go in this directory. Also, if you use modules in your kernel, they will go under the modules directory in /lib.
/mnt--this directory is normally empty, but can be used if you need to mount another filesystem.
/proc--this directory is empty. The kernel will put system status files in this directory.
/sbin--system utilities go in this directory. These utilities affect the entire system. In our embedded system, they are from the BusyBox and the net-tools packages.
/tmp--this directory should be a soft link to /var/tmp. /tmp should be a writable directory. When a program needs to write a temporary file, it will do so in the /tmp directory. Our root filesystem is read-only, so programs cannot write to /tmp. We will mount the /var filesystem as read/write, so we can make a tmp directory under /var.
/usr--the /usr directory contains user program files. Most of the files in our /usr directory come from the BusyBox package. I chose to put the web server and its associated files under the /usr directory.
/var--the /var directory normally contains log files and other writable directories. On our system, the /var directory will be mounted read/write. The only files we'll need to have in our /var directory is a tmp subdirectory and a file that contains our networking configuration. The reason this file is in var is so we can change the networking configuration in the field.
The first program run by the kernel after loading is /sbin/init. This program initializes the system and handles the console terminals and system shutdown. The BusyBox application will provide a basic init program for our embedded system. The init program gets its parameters from the /etc/inittab file:
::sysinit:/etc/init.d/rcS ::askfirst:/bin/sh ::ctrlaltdel:/bin/umount -a -rThe first line tells init to run the rcS script immediately. The second line tells it to start a shell on the console when initialization is complete. The third line tells it to unmount any filesystems when the system goes down.
The rcS script is shown in Listing 1. It should mount all the filesystems, start up the network and start the web server. Notice that we look to see if the file /var/etc/network exists. If it does, we source the file. This allows us to change the networking parameters, such as the IP address and gateway address on the fly. If the file doesn't exist, there are default parameters in the script. Using DHCP might be a good idea also, but I didn't implement it in my system. Here are the contents of the /var/etc/network file:
NETWORK_IP=192.168.1.2 NETWORK_MASK=255.255.255.0 NETWORK_BROADCAST=192.168.1.255 NETWORK_GATEWAY=192.168.1.254
Make sure to change the IP addresses to match your network.
There are a few other configuration files that you will need on your system. The /etc/fstab file is needed so the mount command knows where each filesystem should be mounted:
proc /proc proc defaults 0 0 /dev/hda2 /var ext2 defaults 1 1
/etc/thttpd.conf, /etc/nsswitch.conf, /etc/passwd and /etc/group are needed by the web server. When the web server starts, it becomes a non-root user. The nsswitch.conf tells it where to find user and group information. Users are in /etc/passwd, and groups are in /etc/group. You can copy the /etc/passwd and /etc/group file from your system. Only include the users and groups that are needed. The nsswitch.conf file is shown in Listing 2. It tells applications to look in files to find users and passwords.