From Fedora Project Wiki

Rfsbuild

A utility for creating Root File Systems.
Tony Egan Jan-08


There's a Quickstart below for the impatient.
Also, all the download links are collected at the bottom of this document.

Introduction

Rfsbuild is intended to help developers create Root File Systems for embedded systems. This initial release is aimed at the ARM architecture. It was tested using qemu-system-arm(0.8.2) emulating an ARM-Versatile PB board and using packages from the Fedora ARM repositories.

Development Environment

There are two typical environments for embedded system development. One, a native environment where the compilation and build and sometimes even the source editing takes place on the target processor. This requires a target which is powerful enough in terms of speed and resources to support such development.

The other is a cross-development environment where a cross-compiler and build tools are run on a host (typically an x86 PC) to generate code which is then downloaded to the target system. This leverages the power of the PC for the development work and requires nothing from the target except the resources to run the final application.

Rfsbuild can be used in either of these development environments.

Components

The main rfsbuild utility is a python program which can run on any architecture that supports Python 2.4 and yum. The main tools used by rfsbuild are yum and QEMU. Yum which is also written in Python is a package management utility used by some major Linux distributions, most notably Fedora. Yum depends on a number of other utilities and libraries including rpm, cpio, libsqlite, etc.. In fact yum requires a fairly capable linux environment in order to function.

QEMU is an emulator which runs on Linux and allows binary code for several different architectures to run on an Intel-based system. The first release of rfsbuild was tested using qemu-system-arm(0.8.2) emulating an ARM-Versatile PB system. This can support the Linux environment required by yum.

Operation

Rfsbuild builds the target RFS from RPM packages. Obviously the RPMs must have been built for the target architecture. RPMs are available from many repositories on the Internet. The ARM RPMs used for testing rfsbuild were produced by the hard work of Lennert Buytenhek and others and were obtained here:

  • Fedora Core 6
http://ftp.linux.org.uk/pub/linux/arm/fedora/pub/fedora/linux/core/6/arm/os/
http://ftp.linux.org.uk/pub/linux/arm/fedora/pub/fedora/linux/core/updates/6/arm
http://ftp.linux.org.uk/pub/linux/arm/fedora/pub/fedora/linux/extras/6/arm
  • Fedora 8
http://ftp.linux.org.uk/pub/linux/arm/fedora/pub/fedora/linux/releases/8/Everything/arm/os/Packages
http://ftp.linux.org.uk/pub/linux/arm/fedora/pub/fedora/linux/updates/8/arm

You will also likely be developing your own RPMs containing your embedded application. You can make these available to rfsbuild in a local repository. Rfsbuild supports NFS access to local repositories saving you the trouble of setting up web access. This is discussed further below.

Rfsbuild takes a list of packages and a list of repositories where the packages can be found. It passes these on to yum which installs the RPM packages into the target RFS. Yum takes care of all the dependency checking, downloading and installation of the packages. Yum must execute on the target architecture in order to do this properly hence the need for a native system or an emulated system (QEMU) on which to execute yum.

Here is a sample invocation of rfsbuild on a PC host:

rfsbuild --repo=c6,http://ftp.linux.org.uk/pub/linux/arm/fedora/pub/fedora/linux/core/6/arm/os/
--repo=c6u,http://ftp.linux.org.uk/pub/linux/arm/fedora/pub/fedora/linux/core/updates/6/arm
--repo=c6e,http://ftp.linux.org.uk/pub/linux/arm/fedora/pub/fedora/linux/extras/6/arm --package=bash --package=nfs-utils
--server=./startQEMU --rfspath=/newrfs --initrd=0 --verbosity=2

The repositories, specified by --repo, are the Fedora ARM repositories mentioned earlier. Each one is prefixed by an arbitrary unique name, e.g. c6e, which yum will use in its reporting. RPMs are specified by --package. You can specify as many repositories and packages as you like. Keep in mind that dependencies will be handled automatically. So when you specify --package=bash, yum will check its dependencies and install all the other packages required by bash. More on this later.

The --server=./startQEMU parameter specifies a script which starts the qemu-system-arm emulator. This is a bash script in this release, but it could be any type of executable. There are some requirements for how it invokes QEMU which we will discuss below. Rfsbuild doesn't check the return from this script, but it may in future revisions.

Rfsbuild uses NFS mounted directories to share information between the PC host and QEMU. The --rfspath=/newrfs parameter must specify an exported directory which can be mounted via NFS from QEMU. The new RFS will be built in a unique subdirectory of this directory, (e.g. /newrfs/build-WuCZTU-12311901/install_root). Existing RFS sub-directories will not be deleted or overwritten by rfsbuild.

The --initrd parameter specifies that an init ramdisk image should be created from the new RFS. It will appear at /newrfs/build-WuCZTU-12311901/initrd.gz for the example above. The zero means that rfsbuild will automatically set the size of the initrd.

The --verbosity parameter tries to reassure the user that yum is actually doing something.

When it starts, rfsbuild first prints some informative messages then it copies itself to the directory specified by --rfspath and creates a run script rfsb which will be used by QEMU to start rfsbuild in the emulated environment. Rfsb is created in rfspath. Rfsbuild then invokes the script specified by --server.

The program specified by --server is responsible for starting QEMU. The startQEMU script provided tries to do this in a general way so that it can easily be adapted to different setups. Most of the parameters affecting the networking are specified at the top of the file. You need to edit this to suit your network. By the way the startQEMU script uses sudo. You should make sure you have a line like:

egan ALL=(ALL) NOPASSWD: ALL

to your /etc/sudoers file.

Installing Rfsbuild

Simply download the RPM to your x86 host and enter:

  sudo rpm -ivh rfsbuild-0.9.0-beta.noarch.rpm

The RPM installs /usr/bin/rfsbuild and some miscellaneous support files in /usr/share/rfsbuild

  • Copy startQEMU and qemu-ifup from /usr/share/rfsbuild to a work

directory where you can modify them and where you will invoke rfsbuild.

  • Download the ARM kernel and initrd to the same work directory.
  • zImage -- kernel used by QEMU
  • initrd.gz -- initrd used by QEMU

You may want to rename these and create links using the original names in your work directory. Then when you want to test a new initrd or a a new kernel you can just change the links and keep the originals. Keep in mind that startQEMU will look for the files "zImage" and "initrd.gz".

  • Create a directory for your new RFS and export it via NFS.
  mkdir /newrfs

  echo "/newrfs *(rw,async,no_root_squash)" >> /etc/exports

Starting QEMU

Please see the local Architectures/ARM/HowToQemu and Fabrice Bellard's QEMU site for details of how to set up QEMU to emulate ARM. I have attached an ARM kernel and initrd which are convenient for rfsbuild development. It is assumed that your QEMU installation is able to run the kernel and initrd provided and is able to access the host via the network so that it can NFS mount directories. It must also have access to the repositories specified by the rfsbuild command. I.e. it must be able to access the internet.

A key feature of startQEMU which must not be affected by any modifications is its method of passing the path of the rfsb run script on the kernel command line. When QEMU starts running the kernel and initrd provided, the root user is automatically logged in and the /root/startrfsb script extracts this information from /proc/cmdline. Startrfsb then mounts the directory specified on the command line and invokes the rfsb script. The rfsb script then starts rfsbuild in the QEMU environment. It sounds more complicated than it is.

You can test ./startQEMU by invoking it directly on the x86 host without any parameters. QEMU should start, boot Linux and automatically logon root and run the startrfsb script. This should fail harmlessly and leave you in QEMU at the Linux shell prompt.

To exit QEMU you can enter [Ctl-a] [c] to get to the QEMU monitor and then [q] to quit. If this fails you can always open another shell on the host and enter:

killall -9 qemu-system-arm

To test NFS from QEMU, make sure NFS is enabled on the host and /newrfs is exported. Then invoke:

./startQEMU ip:/newrfs/abcde

Where ip is the IP address of your x86 host. QEMU should start, boot Linux and automatically logon root and run the startrfsb script. The startrfsb script should extract the NFS path from the kernel command line and mount the ip:/newrfs directory. The script should then fail harmlessly and leave you in QEMU Linux at the shell prompt. This is assuming there is no abcde script in /newrfs.

Then you can check that the NFS mount is working in QEMU :

df -a /

If NFS is not working it is likely that QEMU networking is not set up properly or NFSD on the host is not successfully exporting /newrfs.

Kill QEMU again.

At this point you should have ./startQEMU & NFS working. If not, you need to do some troubleshooting before continuing.

Local Repositories

At some point you will probably want to create your own RPMs in a repository on a local machine. Once you have created the RPMS just copy them to a directory on your host, e.g. /repo and then enter:

createrepo /repo
You may need to do this from ARM Linux.

You can then follow the procedure for making this repository accessible via http so that it can be accessed from anywhere. Rfsbuild supports a simpler alternative by using a naming convention to specify local repositories which are accessible via NFS. On the host ensure that the repository directory is exported via NFS. Then the --repo parameter in the rfsbuild command will look like:

--repo=nfs10.0.0.105,file:///repo

The three letters "nfs" are required, followed by the IP address of the NFS server, followed by ,file :// followed by the exported path to the repo. When rfsbuild sees this name it will create an alias nfs1 and mount the repository via NFS prior to invoking yum.

Keystone Package

This idea comes from David Zeuthen's livecd-creator program, as does a lot of the code in rfsbuild. We saw above how to specify packages when invoking rfsbuild. This can get a bit unwieldy when there are a lot of packages. It would be nice if we could specify one package which in turn specified all the packages we want to install. That is the first function of a Keystone Package.

The other issue we haven't discussed is the ever present need to fix, tweak, modify and customize the RFS after the packages have been installed. RPMs already support post-install scripts but an embedded application often requires different modifications than the desktop application most RPMs are designed for. Different target devices usually require different post install modifications to the RFS. This is also handled by the keystone package.

A Keystone Package is an RPM which encapsulates a set of requirements for a particular RFS. So, you could have a Keystone Package for your router and another Keystone Package for your NAS device and another for your music server. Each would specify the specific set of packages and modifications required for each of those unique devices.

Keystone Packages take advantage of the version control, distribution and install mechanisms already in place for RPMs. You just give a user the appropriate version Keystone Package for his device and it encapsulates all the information needed to generate the RFS for that device perfectly. You don't even have to give him the package. If it is available in a repository on the Internet he just needs to specify it as a parameter to rfsbuild, and it will automatically be downloaded from the repository and installed.

The mechanism is very simple. A Keystone Package contains one or more special conf scripts which are installed in a dedicated directory, /etc/rfsbuild. The package can contain miscellaneous other files also as needed just like any RPM but the conf scripts are special.

During execution rfsbuild first invokes yum to install the packages specified on the command line. Yum performs its normal dependency checking so more packages than those explicitly specified using --package arguments may be installed at this time.

When this is finished rfsbuild invokes any conf scripts it finds in /etc/rfsbuild with the parameter pkgadd. See the 10-fedora-rfsbuild-mini-glibc.conf script included in the rfsbuild package. If there are multiple conf scripts they will be invoked in alphabetical order. This call to the conf scripts will return a set of additional packages to be installed. Rfsbuild invokes yum a second time to install these. Again, yum checks dependencies and installs all required packages.

When the second run of yum is complete rfsbuild invokes the conf scripts in /etc/rfsbuild again but this time with the parameter post. This causes the commands listed in the post section of the conf scripts to be executed in a chroot context using the new RFS as root. I.e the new RFS appears to be the root directory while these post commands are executed. So any command specified in the post section must be available in the new RFS. This is where fixes, tweaks and modifications to the RFS are performed.

The sample Keystone Package included in the rfsbuild RPM, fedora-rfsbuild-mini-glibc.rpm uses the conf script 10-fedora-rfsbuild-mini-glibc.conf. This Keystone Package was used to build the initrd supplied here for use with rfsbuild. So, to recreate the RFS and initrd you would simply need to invoke rfsbuild as follows:

rfsbuild --repo=nfs10.0.0.105,file:///repo
--repo=f8,http://ftp.linux.org.uk/pub/linux/arm/fedora/pub/fedora/linux/releases/8/Everything/arm/os/Packages
--repo=f8u,http://ftp.linux.org.uk/pub/linux/arm/fedora/pub/fedora/linux/updates/8/arm
--package=fedora-rfsbuild-mini-glibc --package=findutils --server=./startQEMU --rfspath=/newrfs --initrd=0
--verbosity=2

This assumes you have put fedora-rfsbuild-mini-glibc.rpm in your local NFS exported repository 10.0.0.105:/repo.



Quickstart

For those who like to live dangerously.... :-)

ARM Host

If you have an ARM host that is already running Linux and supports python and yum then you can simply install rfsbuild on that machine and run it locally to build ARM RFSs. E.g.:

rfsbuild  --repo=/repo
--repo=c6,http://fedora-arm.wantstofly.org/pub/fedora/linux/core/6/arm/os
--repo=c6u,http://fedora-arm.wantstofly.org/pub/fedora/linux/core/updates/6/arm
--repo=c6e,http://fedora-arm.wantstofly.org/pub/fedora/linux/extras/6/arm
--package=fedora-rfsbuild-mini-glibc
--rfspath=/newrfs --initrd=0 --verbosity=2

Here the /repo directory is a local repository which contains the fedora-rfsbuild-mini-glibc keystone package. /newrfs is where we want the new RFS to be built. We are using ARM packages from the fedora-arm repositories. Since the server parameter is absent the build will be performed locally.


X86 Host and ARM QEMU server

How to set up an x86 host and an ARM QEMU environment so that rfsbuild can be invoked on the host to initiate a build under QEMU.

  • Install rfsbuild on the X86 host if you haven't already done so.
    This will create a directory /usr/share/rfsbuild containing the following:
  • README
  • startQEMU -- support utility for starting QEMU
  • qemu-ifup -- network configuration used by QEMU
  • 10-fedora-rfsbuild-mini-glibc.conf -- sample conf script
  • fedora-rfsbuild-mini-glibc.rpm -- sample keystone rpm
  • fedora-rfsbuild-mini-glibc.spec -- sample spec file
  • startrfsb -- this is installed in the /root directory of QEMU ARM linux. It starts a build when QEMU boots Linux.
  • Copy startQEMU and qemu-ifup to a work directory where you can modify them and where you will invoke rfsbuild.
  • Download the ARM kernel and initrd to the same work directory.
  • zImage -- kernel used by QEMU
  • initrd.gz -- initrd used by QEMU
  • Install QEMU and get it working with startQEMU and the zImage and initrd.gz you downloaded. This includes setting up networking via the TUN/TAP interface. You can find some help at [1] and [2] .

AT this stage invoking ./startQEMU with no parameters should start QEMU, boot Linux and automatically logon root and try to start an RFS build. This should fail harmlessly and leave you in QEMU Linux at the shell prompt.

To exit QEMU you can enter Ctl-a c to get to the QEMU monitor and then q to quit. If this fails you can always open another shell on the host and enter:

killall -9 qemu-system-arm
  • Create the RFS directory:
mkdir /newrfs
  • Export /newrfs via NFS. (Edit /etc/exports and restart NFS daemons)

Check that it is accessible from QEMU.

./startQEMU 10.0.0.105:/newrfs/abcde

Replace 10.0.0.105 with the IP address of your PC. This should behave the same as described above except you should see 10.0.0.105:/newrfs mounted on /newrfs when you check under QEMU:

df -a /

If NFS is not working it is likely that QEMU networking is not set up properly or NFSD on the host is not successfully exporting /newrfs.

Kill QEMU again.

At this point you should have ./startQEMU & NFS working. If not, you need to do some troubleshooting before continuing.

  • Finally, invoke a test run of rfsbuild:
rfsbuild
--repo=f8,http://ftp.linux.org.uk/pub/linux/arm/fedora/pub/fedora/linux/releases/8/Everything/arm/os/Packages
--repo=f8u,http://ftp.linux.org.uk/pub/linux/arm/fedora/pub/fedora/linux/updates/8/arm --package=setup
--server=./startQEMU --rfspath=/newrfs --verbosity=2

This should create a skeleton RFS with only the setup package installed.


Miscellaneous Options

  • --exclude is used to omit packages from package groups. Yum, and consequently rfsbuild will not exclude a package required by dependencies.
  • --cache specifies a yum cache to use. It's not expected to be empty.
  • --cacheonly tells yum to use cache only i.e. yum -C
  • --initramfs create an initramfs archive. Not working.
  • --runscript the name of the script that rfsbuild creates in /newrfs for QEMU to run. The default is rfsb
  • --label the suffix of the RFS build directory. The default is constructed from the date. Not working correctly yet since QEMU is not initializing the date correctly.


For those of you (like me) who don't like repeatedly typing long command lines I've included a sample script in the RPM called go. Simply invoke it as:

./go fc8

This will re-create the sample initrd.

By the way the samples are just that "samples" to show you how rfsbuild works. You will see some harmless error messages as you run the sample commands given above. The sample conf script performs some very crude reductions on the RFS. Clean up and more structured pruning are left as an exercise for the reader. Useful revisions to the scripts and suggestions for improvement will be welcomed on the Fedora ARM mailing list.


Downloads