The Linux root FilesystemThe Linux root filesystem contains files and executables that the kernel requires, as well as executables for system administration. In a desktop workstation installation, the kernel mounts a hard disk partition on the / directory. The following directories exist beneath the / directory:
The Trailblazer engineers began to understand the root filesystem contents by examining tbdev1 (the Debian distribution development computer, which is described in Chapter 3, "Selecting a Platform and Installing Tool Sets"). They found that the root filesystem contained 10,734 files and used 67.428MB of hard disk space. They found that a default Red Hat installation contains 29,296 files and uses 382.020MB. Clearly, taking the simple approach of merely copying the contents from a default Debian or Red Hat disk exceeds the storage capability of the engineers' four target platforms, which ranges from 4MB to 16MB. The engineers needed to decide what files are really necessary to boot Linux and execute a bash prompt. TIP Developing a root filesystem by examining the Linux boot process from start_kernel to the bash prompt results in a minimum set of required files and gives you an understanding of Linux's interaction with programs and libraries. Someone suggested that they could just start deleting files and see what happens. That hit-or-miss approach was quickly dismissed. The engineers learned from the Linux boot process that the kernel starts the init process that executes network and system initialization scripts and then executes bash. The engineers then wondered what files are required for init, the network and system initialization, and bash. Then, concern surfaced about this init/bash file approach and the four target platforms. The engineers wondered if they could determine what files are necessary for init and bash, would those same files be required for all architectures? Or would some architecture-based files be missing? No one had an answer other than to say that after the kernel boots, all architectures shared the same source code for init, bash, and the shared libraries. This means that if the engineers determine a root filesystem for one architecture, it should work for the others. Required Files for initAfter the kernel initializes its caches and various hardware devices, it executes the first user process, init, which spawns all other process. In order for init to run, it needs certain files and libraries to be present in the filesystem. On tbdev1, the engineers used a program called ldd to discover init's shared library dependencies. This is the output of ldd: root@tbdev1[514]: ldd /sbin/init libc.so.6 => /lib/libc.so.6 (0x40015000) /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000) The first file the new root filesystem needs is /sbin/init, which requires /lib/libc.so.6 and /lib/ld-Linux.so.2. By reading the man page for init, the engineers determined that init uses a configuration file called /etc/inittab that contains instructions for init. On a desktop or server Linux workstation, the init process initializes the networking and system services, and then it enters a runlevel typically the login process. On the target systems, the engineers are not concerned with runlevels per se; rather, they want to boot the system and get bash running. So they aren't using a predefined runlevel, as in the Unix/Linux world. They are configuring init to simply initialize the network. Then they are configuring init to execute bash without authentication and to respawn bash if bash terminates. TIP The kernel completes booting by executing the first user process, init. init's scripts start all the system services. You can configure init to start programs and then to restart them if they terminate. The engineers examined the /etc/inittab file on the tbdev1 workstation. They then created a simplified version that initializes the network and then executes bash. Here's their simplified inittab file: id:2:initdefault: l2:2:wait:/etc/init.d/rcS 1:2435:respawn:/bin/bash 2:6:wait:/etc/init.d/umountfs In this version of the /etc/inittab file, id:2:initdefault: tells init the default level to enter. l2:2:wait:/etc/init.d/rcS tells init to run the /etc/init.d/rcS script before entering runlevel 2, and then to wait for completion. 1:2435:respawn:/bin/bash tells init to run /bin/bash and respawn it if bash terminates. 2:6:wait:/etc/init.d/umountfs tells init that upon entering runlevel 6, someone is rebooting the target board, to run the umountfs script and wait for its completion. Required Files for bashThe bash shell requires libraries to execute. On tbdev1, the engineers again used ldd to discover bash's shared library dependencies. Here is the output of ldd: root@tbdev1[516]: ldd /bin/bash libncurses.so.5 => /lib/libncurses.so.5 (0x40016000) libdl.so.2 => /lib/libdl.so.2 (0x40055000) libc.so.6 => /lib/libc.so.6 (0x40059000) /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000) In addition to init, inittab, rcS, umountfs, ld-linux.so.2, and libc.so.6, the new root filesystem needs bash, libdl.so.2, and libncurses.so.5. The engineers poked around in the /bin, /sbin, and /usr/bin directories and found that the following programs would also be necessary in order to have a functional system: cat, ls, mount, umount, ps, df, kill, ping, chmod, touch, rm, ifconfig, route, telnet, and gdbserver. They checked for shared library and configuration file dependencies, and after a little trial and error, they compiled the root filesystem file list shown in Table 4.1. These files exist in a directory structure that consists of these directories: /bin, /dev, /etc, /etc/init.d, /lib, /proc, /sbin, /tmp, /usr, and /usr/bin. With this root filesystem, the kernel calls init, which initializes the network and then executes bash. A user can then ping other network computers and run helloworld. This booted system will fulfill all seven PBRs. TIP The Linux program ldd outputs a list of shared libraries required by a program or library. When adding programs to the root filesystem, you should use ldd to determine whether additional libraries are required.
The root Filesystem Binary Files: Compile or Download?The engineers determined that it would be easy to find all these files for the i386 platforms. They could just copy them from the tbdev1 computer. The ARM and PowerPC versions would require cross-compiling. Although this list is short compared to the Debian base installation of 10,734 files, cross-compiling all these executables would require significant effort. The most current source code would have to be located and downloaded. The individual makefiles would require modification for cross-compiling and library linking. Finally, the code would need to be compiled. The engineers thought that there must be an easier way, that someone else had probably already done this ARM and PowerPC cross-compiling. During their initial research, the engineers found that the Debian Linux site (www.debian.org) contains source and compiled binaries for Alpha, ARM, i386, m68k, PowerPC, and SPARC processors. This availability of compiled binaries looked promising, until the engineers investigated the source code versions. The Debian software distribution lags behind current versions of open-source software. Debian publicly acknowledges this and distributes software "when it's time." In addition, the Debian versions of PowerPC binaries are compiled for microprocessors that have floating-point units. The Trailblazer RPX-CLLF target board uses a Motorola MPC860 that doesn't have a floating-point unit. They decided to look elsewhere. Recently, MontaVista software released Hard Hat Linux (HHL), version 2 using kernel version 2.4.2.1 MontaVista distributes two varieties of HHLv2: the Journeyman and the Professional editions. The Journeyman edition caught the engineers' attention because it supports i386, ARM, and PowerPC, specifically the MPC8xx processors and it's free. The Journeyman CD contents as well as CD images are publicly available at the MontaVista FTP site (ftp.mvista.com/pub/Journeyman). TIP Some PowerPC microprocessors don't have a floating-point unit and don't execute floating-point instructions gracefully. When executing PowerPC programs, you should make sure they have been compiled correctly for your microprocessor. MontaVista's HHLv2 isn't just a repackaging of the open-source GNU software. MontaVista's engineers are considered leading developers in architecture porting issues and development activities. They have incorporated much of their work (via patches) into the MontaVista distribution. All their architecture-specific work for the kernel and other GNU software is incorporated into the HHLv2 source and binaries. The Project Trailblazer engineers, who aren't architecture experts, were excited that they wouldn't have to become experts just to get their target platforms booted. The engineers decided that instead of reinventing the wheel, they could use MontaVista's Journeyman product, including the Journeyman cross-compiled versions of the programs for their ARM and PowerPC root filesystem. After a successful booting of both the ARM and PowerPC target platforms, they decided to use Journeyman for i386 root filesystem as well. This decision simplified their lives in ways they didn't imagine. Building a root filesystem for all the target platforms eventually involved running a script that would download the Journeyman Red Hat Package Management (RPM) binaries, extract the files, copy various binary programs to the new root filesystem, and create all the configuration files. This script, called buildrootfilesystem, is shown in Listing 4.1 and can be found at www.embeddedlinuxinterfacing.com/chapters/04/buildrootfilesystem. Listing 4.1 The buildrootfilesystem Script[View full width] #!/bin/bash # buildrootfilesystem v0.2 10/23/01 # www.embeddedlinuxinterfacing.com # # The original location of this script is # http://www.embeddedlinuxinterfacing.com/chapters/04/buildrootfilesystem # # Copyright (C) 2001 by Craig Hollabaugh # # This program is free software; you can redistribute it and/or modify it # under the terms of the GNU Library General Public License as published by # the Free Software Foundation; either version 2 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License # for more details. # # You should have received a copy of the GNU Library General Public License # along with this program; if not, write to the Free Software Foundation, # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # umask 022 SRCFILELOC=/root/cross BUILDLOC=$SRCFILELOC/builds case "$1" in "ppc" ) ARCH=powerpc TARGET=powerpc-linux MVRPMLOC=ftp://ftp.mvista.com/pub/Journeyman/cd2/ppc_8xx/apps/ #MVRPMLOC=http://www.embeddedlinuxinterfacing.com/ftp.mvista.com/pub/ Journeyman/cd2/ ppc_8xx/apps/ TEMPFSLOC=$BUILDLOC/$ARCH-rootrpms/opt/hardhat/devkit/ppc/8xx/target/ ;; "arm" ) ARCH=arm TARGET=arm-linux MVRPMLOC=ftp://ftp.mvista.com/pub/Journeyman/cd1/arm_sa_le/apps/ #MVRPMLOC=http://www.embeddedlinuxinterfacing.com/ftp.mvista.com/pub/ Journeyman/cd1/ arm_sa_le/apps/ TEMPFSLOC=$BUILDLOC/$ARCH-rootrpms/opt/hardhat/devkit/arm/sa_le/target/ ;; "i386" ) ARCH=i386 TARGET=i386 MVRPMLOC=ftp://ftp.mvista.com/pub/Journeyman/cd1/x86_586/apps/ #MVRPMLOC=http://www.embeddedlinuxinterfacing.com/ftp.mvista.com/pub/ Journeyman/cd1/ x86_586/apps/ TEMPFSLOC=$BUILDLOC/$ARCH-rootrpms/opt/hardhat/devkit/x86/586/target/ ;; * ) echo -n "Usage " `basename $0` echo " i386|arm|ppc [ramdisk]" exit 1 ;; esac # # Step 1 - Determine what packages to download # echo Step 1 - Determine what packages to download PACKAGES="glibc-2 bash procps textutils fileutils shellutils sysvinit netbase libncurses libstdc mount telnet-client net-tools ping gdbserver modutils" echo packages are $PACKAGES echo Step 1 - Complete echo .htm# # Step 2 - Create build and new target root filesystem directories # echo Step 2 - Create build and new target root filesystem directories if [ ! -e /tftpboot ] then mkdir /tftpboot fi if [ ! -e $SRCFILELOC ] then mkdir $SRCFILELOC fi if [ ! -e $BUILDLOC ] then mkdir $BUILDLOC fi ROOTFSLOC=/tftpboot/$ARCH-rootfs echo Creating root file system for $ARCH rm -rf $ROOTFSLOC mkdir $ROOTFSLOC if [ ! -e $BUILDLOC/$ARCH-rootrpms ] then mkdir $BUILDLOC/$ARCH-rootrpms fi cd $ROOTFSLOC mkdir dev etc etc/init.d bin sbin lib usr usr/bin proc tmp chmod 755 . dev etc etc/init.d bin sbin lib usr usr/bin proc tmp echo Step 2 - Complete echo .htm# # Step 3 - Download the packages # echo Step 3 - Download the packages cd $BUILDLOC/$ARCH-rootrpms lynx -dump $MVRPMLOC | grep ftp > /tmp/rpmlist for i in $PACKAGES do a=`grep $i /tmp/rpmlist` rpmurl=`echo $a | cut -d " " -f 2` # echo $rpmurl rpm=`basename $rpmurl` # echo $rpm if [ ! -f $BUILDLOC/$ARCH-rootrpms/$rpm ] then echo Getting $rpm wget $rpmurl else echo Have $rpm fi done echo Step 3 - Complete echo .htm# # Step 4 - Extract the package's contents into a temporary directory # echo Step 4 - Extract the package\'s contents into a temporary directory cd $BUILDLOC/$ARCH-rootrpms # this is the old way, too slow because it converts RPMs everytime #alien -t *rpm #find . -name "*tgz" -exec tar zxvf {} \; #rm -rf *tgz IFS=' ' for rpm in `ls *rpm` do if [ ! -f $rpm.extracted ] then alien -t $rpm tgz=`ls *tgz` tar zxvf $tgz rm -rf $tgz touch $rpm.extracted fi done echo Step 4 - Complete echo .htm# # Step 5 - Copy the required programs # echo Step 5 - Copy the required programs echo .htm#lib files cd $TEMPFSLOC/lib cp -av ld* $ROOTFSLOC/lib cp -av libc-* $ROOTFSLOC/lib cp -av libc.* $ROOTFSLOC/lib cp -av libutil* $ROOTFSLOC/lib cp -av libncurses* $ROOTFSLOC/lib cp -av libdl* $ROOTFSLOC/lib cp -av libnss_dns* $ROOTFSLOC/lib cp -av libnss_files* $ROOTFSLOC/lib cp -av libresolv* $ROOTFSLOC/lib cp -av libproc* $ROOTFSLOC/lib cp -av librt* $ROOTFSLOC/lib cp -av libpthread* $ROOTFSLOC/lib #libm and libstdc are needed by telnet-client cp -av libm* $ROOTFSLOC/lib cd $ROOTFSLOC/usr ln -s http://lib lib cd $TEMPFSLOC/usr/lib cp -av libstdc* $ROOTFSLOC/usr/lib #sbin files cd $TEMPFSLOC/sbin cp -av init ifconfig route $ROOTFSLOC/sbin #bin files cd $TEMPFSLOC/bin cp -av bash cat ls mount umount ps $ROOTFSLOC/bin cp -av df kill ping chmod touch rm $ROOTFSLOC/bin cp -av echo $ROOTFSLOC/bin cd $ROOTFSLOC/bin ln -s bash sh #usr/bin files cd $TEMPFSLOC/usr/bin cp -av telnet gdbserver $ROOTFSLOC/usr/bin #helloworld cd $ROOTFSLOC/tmp cat > helloworld.c << ENDOFINPUT #include <stdio.h> int main(void) { int i; for (i = 1; i < 10; i++) { printf("Hello world %d times!\n",i); } } ENDOFINPUT if [ $ARCH == "i386" ] then gcc -g -o helloworld-$TARGET helloworld.c else $TARGET-gcc -g -o helloworld-$TARGET helloworld.c fi file helloworld-$TARGET chown -R root.root $ROOTFSLOC/* echo Step 5 - Complete echo .htm# # Step 6 - Strip the required programs # echo Step 6 - Strip the required programs echo .htm# strip it, strip it good if [ $ARCH == "i386" ] then strip -s -g $ROOTFSLOC/lib/* strip -s -g $ROOTFSLOC/bin/* strip -s -g $ROOTFSLOC/sbin/* strip -s -g $ROOTFSLOC/usr/bin/* strip -s -g $ROOTFSLOC/usr/lib/* else $TARGET-strip -s -g $ROOTFSLOC/lib/* $TARGET-strip -s -g $ROOTFSLOC/bin/* $TARGET-strip -s -g $ROOTFSLOC/sbin/* $TARGET-strip -s -g $ROOTFSLOC/usr/bin/* $TARGET-strip -s -g $ROOTFSLOC/usr/lib/* fi echo Step 6 - Complete echo .htm# # Step 7 - Create configuration files # echo Step 7 - Create configuration files echo .htm#etc files cd $TEMPFSLOC/etc cp -av protocols services $ROOTFSLOC/etc cd $ROOTFSLOC/etc cat > fstab << endofinput # <file system> <mount point> <type> <options> <dump> <pass> proc /proc proc defaults 0 0 endofinput if [ $# == 2 ] && [ $2 == "ramdisk" ] then echo "/dev/ram / ext2 defaults 0 0" >> fstab else echo "192.168.1.11:/tftpboot/$ARCH-rootfs / nfs defaults 1 1" >> fstab fi cat > inittab << endofinput id:2:initdefault: l2:2:wait:/etc/init.d/rcS 1:2:respawn:/bin/bash 2:6:wait:/etc/init.d/umountfs endofinput cat > init.d/rcS << endofinput #!/bin/bash /bin/mount -n -o remount,rw / # clear out mtab >/etc/mtab /bin/mount -a echo Starting Network /sbin/ifconfig lo 127.0.0.1 netmask 255.0.0.0 broadcast 127.255.255.255 /sbin/route add -net 127.0.0.0 netmask 255.0.0.0 lo endofinput case "$ARCH" in "powerpc" ) cat >> init.d/rcS << endofinput /sbin/ifconfig eth0 192.168.1.22 netmask 255.255.255.0 /sbin/route add default gw 192.168.1.254 eth0 endofinput ;; "arm" ) cat >> init.d/rcS << endofinput /sbin/ifconfig eth0 192.168.1.21 netmask 255.255.255.0 /sbin/route add default gw 192.168.1.254 eth0 endofinput ;; "i386" ) cat >> init.d/rcS << endofinput /sbin/ifconfig eth0 192.168.1.23 netmask 255.255.255.0 /sbin/route add default gw 192.168.1.254 eth0 endofinput ;; esac chmod 755 init.d/rcS cat > init.d/umountfs << endofinput /bin/echo umounting filesystems and remounting / as readonly /bin/umount -f -a -r /bin/mount -n -o remount,ro / endofinput chmod 755 init.d/umountfs cat > resolv.conf << endofinput domain trailblazerdev nameserver 192.168.1.1 endofinput echo Step 7 - Complete echo .htm# # Step 8 - Create devices in /dev directory # echo Step 8 - Create devices in /dev directory #dev files cd $ROOTFSLOC/dev mknod -m 0666 tty c 5 0 mknod -m 0666 tty0 c 4 0 mknod -m 0666 ttyS0 c 4 64 ln -s ttyS0 console mknod -m 0666 null c 1 3 mknod -m 660 ram b 1 1 chown root.disk ram echo Step 8 - Complete echo .htm# # Step 9 - Prepare the root filesystem for operation on the target board # echo Step 9 - Prepare the root filesystem for operation on the target board if [ $# == 2 ] then if [ $2 == "ramdisk" ] then echo Building $ARCH root filesystem ramdisk rm -rf /tmp/tmpmnt mkdir /tmp/tmpmnt rm -rf /tmp/ramrootfs dd if=/dev/zero of=/tmp/ramrootfs bs=1k count=8192 mke2fs -F -m 0 -i 2000 /tmp/ramrootfs mount -o loop -t ext2 /tmp/ramrootfs /tmp/tmpmnt cd /tmp/tmpmnt cp -av $ROOTFSLOC/* . cd /tmp umount /tmp/tmpmnt cat /tmp/ramrootfs | gzip -9 > /tftpboot/$ARCH-ramdisk.gz if [ $ARCH == "powerpc" ] then cp /tftpboot/$ARCH-ramdisk.gz /usr/src/$TARGET/arch/ppc/boot/images/ramdisk.image.gz echo Copying /tftpboot/$ARCH-ramdisk.gz to /usr/src/$TARGET/arch/ppc/boot/images/ ramdisk.image.gz fi rm -rf /tmp/ramrootfs rm -rf /tmp/tmpmnt echo Your $ARCH root filesystem ramdisk is /tftpboot/$ARCH-ramdisk.gz fi fi if [ $ARCH == "i386" ] then echo echo -n This script is about to work with your /dev/hdc1 partition, echo is this OK? yes/no?" " read hdc1ok if [ $hdc1ok == "yes" ] then mkdir $ROOTFSLOC/boot #dev files cd $ROOTFSLOC/dev rm -rf console mknod console c 5 1 chmod 666 console mknod hda b 3 0 mknod hda1 b 3 1 mknod hda2 b 3 2 chmod 666 hda* #/boot files cp /boot/boot.b $ROOTFSLOC/boot cp /usr/src/linux/arch/i386/boot/bzImage $ROOTFSLOC/boot/bzImage TMPMOUNT=/tmp/tmpmnt cat > /tmp/lilo.conf.1 << ENDOFINPUT # this is a special lilo.conf file used to install lilo on the /dev/hdc drive # when the machine is booted from the /dev/hda drive. Don't run lilo with this # conf file if you booted the machine from this second drive. disk=/dev/hdc bios=0x80 # This tells LILO to treat hdc as hda boot=/dev/hdc # install boot on hdc map=$TMPMOUNT/boot/map # location of the map file install=$TMPMOUNT/boot/boot.b # boot file to copy to boot sector prompt # show LILO boot: prompt timeout=10 # wait a second for user input image=$TMPMOUNT/boot/bzImage # kernel location label=linux # kernel label root=/dev/hda1 # root image location after you # finalize disk location read-only # mount root as read only ENDOFINPUT cat > $ROOTFSLOC/etc/fstab << endofinput # <file system> <mount point> <type> <options> <dump> <pass> /dev/hda1 / ext2 defaults,errors=remount-ro 0 1 proc /proc proc defaults 0 0 endofinput e2fsck -y /dev/hdc1 rm -rf $TMPMOUNT mkdir $TMPMOUNT mount /dev/hdc1 $TMPMOUNT echo -n "Do you want to erase the /dev/hdc1 partition, continue yes/no? " read deleteall if [ $deleteall == "yes" ] then # this deletes everything on your partition, WATCH OUT!! rm -rf $TMPMOUNT/* mkdir $TMPMOUNT/lost+found fi cp -av $ROOTFSLOC/* $TMPMOUNT /sbin/lilo -C /tmp/lilo.conf.1 cd / umount $TMPMOUNT rm -rf $TMPMOUNT e2fsck -y /dev/hdc1 fi #hdc1ok fi echo Step 9 - Complete echo echo Your $ARCH root filesystem is $ROOTFSLOC |
Top |