ELBE: automated building of Ubuntu images for a Raspberry Pi 3B

Building embedded Linux systems

ELBETypical embedded Linux systems include a wide number of software components, which all need to be compiled and integrated together. Two main approaches are used in the industry to integrate such embedded Linux systems: build systems such as Yocto/OpenEmbedded, Buildroot or OpenWrt, and binary distributions such as Debian, Ubuntu or Fedora. Of course, both options have their own advantages and drawbacks.

One of the benefits of using standard binary distributions such as Debian or Ubuntu is their widespread use, their serious and long-term security maintenance and their large number of packages. However, they often lack appropriate tools to automate the process of creating a complete Linux system image that combines existing binary packages and custom packages.

In this blog post, we introduce ELBE (Embedded Linux Build Environment), which is a build system designed to build Debian distributions and images for the embedded world. While ELBE was initially focused on Debian only, Bootlin contributed support for building Ubuntu images with ELBE, and this blog post will show as an example how to build an Ubuntu image with ELBE for a Raspberry Pi 3B.

ELBE base principle

When you first run ELBE, it creates a Virtual Machine (VM) for building root filesystems. This VM is called initvm. The process of building the root filesystem for your image is to submit and XML file to the initvm, which triggers the building of an image.

The ELBE XML file can contain an archive, which can contain configuration files, and additional software. It uses pre-built software in the form of Debian/Ubuntu packages (.deb). It is also possible to use custom repositories to get special packages into the root filesystem. The resulting root file system (a customized Debian or Ubuntu distribution) can still be upgraded and maintained through Debian’s tools such as APT (Advanced Package Tool). This is the biggest difference between ELBE and other build systems like the Yocto Project and Buildroot.

Bootlin contributions

As mentioned in this blog post introduction, Bootlin contributed support for building Ubuntu images to ELBE, which led to the following upstream commits:

Build an Ubuntu image for the Raspberry Pi 3B

We are now going to illustrate how to use ELBE by showing how to build an image for the popular RaspberryPi 3B platform.

Add required packages

This was tested on Ubuntu 20.04. Install the below packages if needed, and make sure you are in the libvirt, libvirt-qemu and kvm groups:

$ sudo apt install python3 python3-debian python3-mako \
  python3-lxml python3-apt python3-gpg python3-suds \
  python3-libvirt qemu-utils qemu-kvm p7zip-full \
  make libvirt-daemon libvirt-daemon-system \
  libvirt-clients python3-urwid
$ sudo adduser youruser libvirt 
$ sudo adduser youruser libvirt-qemu
$ sudo adduser youruser kvm
$ newgrp libvirt
$ newgrp libvirt-qemu
$ newgrp kvm

Prepare ELBE initvm

First, you need to clone ELBE’s git reposority:

git clone https://github.com/Linutronix/elbe.git

We need to use the v13.2 version because our latest contributions for Ubuntu support made it to 13.2:

$ cd elbe
$ git checkout v13.2

To create the initvm:

$ PATH=$PATH:$(pwd)
$ elbe initvm create --devel

The --devel parameter allows to use ELBE from the current working directory into the initvm.

If the command fails with the Signature with unknown key: message you need to add these keys to apt. Use the following command where XXX is the key to be added:

$ sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys XXX

Creating your initvm should take at least 10 to 20 minutes.

In case you rebooted your computer or stopped the VM, you will need to start it:

$ elbe initvm start

Create an ELBE project for our Ubuntu image.

To begin with, we will base our image on the armhf-ubuntu example. We create an ELBE pbuilder project and not a simple ELBE project because we later want to build our own Linux kernel package for our board:

$ elbe pbuilder create --xmlfile=examples/armhf-ubuntu.xml \
  --writeproject rpi.prj --cross

The project identifier is written to rpi.prj. We save the identifier to a shell variable to simplify the next ELBE commands:

$ PRJ=$(cat rpi.prj)

Build the Linux package

As explained earlier we want to use ELBE to build our package for the Linux kernel. ELBE uses the standard Debian tool pbuilder to build packages. Therefore, we need to have debianized sources (i.e sources with the appropriate Debian metadata in a debian/ subfolder) to build a package with pbuilder.

First clone the Linux repositories:

$ git clone -b rpi-5.10.y https://github.com/raspberrypi/linux.git
$ cd linux

Debianize the Linux repositories. We use the elbe debianize command to simplify the generation of the debian folder:

$ elbe debianize

Fill the settings in the UI as follows (make sure you reduce the font size if you don’t see the Confirm button):

Make sure you set Name to rpi. Otherwise, you won’t get the output file names we use in the upcoming instructions.

The debianize command helps to create the skeleton of the debian folder in the sources. It has been pre-configured for a few packages like bootloaders or the Linux kernel, to create the rules to build these packages. It may need further modifications to finish the packaging process. Take a look a the manual to have more information on debianization. In our case, we need to tweak the debian/ folder with the two following steps to cross-build the Raspberry Linux kernel without error.

Append the below lines to the debian/rules file (use tabs instead of spaces):

override_dh_strip:
	dh_strip -Xscripts

override_dh_shlibdeps:
	dh_shlibdeps -Xscripts

Remove the following line from the debian/linux-image-5.10-rpi.install file:

./lib/firmware/*

Update the source format:

$ echo "1.0" > debian/source/format

The Linux kernel sources are now ready, we can run elbe pbuilder to compile them:

$ mkdir ../out
$ elbe pbuilder build --project $PRJ --cross --out ../out

According to how fast your system is, this can run for hours!

If everything ends well without error the out/ directory has been filled with output files:

$ ls ../out
linux-5.10-rpi_1.0_armhf.buildinfo
linux-5.10-rpi_1.0_armhf.changes
linux-5.10-rpi_1.0.dsc
linux-5.10-rpi_1.0.tar.gz
linux-headers-5.10-rpi_1.0_armhf.deb
linux-image-5.10-rpi_1.0_armhf.deb
linux-libc-dev-5.10-rpi_1.0_armhf.deb

Update the Ubuntu XML image description

Now we have our Linux kernel packaged we can move on to the image generation. Since we started from examples/armhf-ubuntu.xml, we will modify this file to fit our needs.

We begin by adding the Linux kernel package to the XML image description in the pkg-list node:


<pkg-list>
...
	<pkg>linux-image-5.10-rpi</pkg>
...
</pkg-list>

We also have to add the Device Tree to the boot/ directory because the Linux kernel package installs all the Device Trees into the /usr/lib directory.

This change is part of the rootfs modifications, therefore it is described under the finetuning XML node. We also rename the kernel image to kernel.img:


<finetuning>
...
	<cp path="/usr/lib/linux-image-5.10-rpi/bcm2710-rpi-3-b.dtb">/boot/bcm2710-rpi-3-b.dtb</cp>
	<cp path="/usr/lib/linux-image-5.10-rpi/overlays">/boot/overlays</cp>
	<mv path="/boot/vmlinuz-5.10-rpi">/boot/kernel.img</mv>
...
</finetuning>

We want to use an SD card on our Raspberry Pi, so we have to describe the partitioning of our image. For this purpose, we add the images and the fstab XML nodes to the target XML node:


<target>
...
	<images>
		<msdoshd>
			<name>sdcard.img</name>
			<size>1500MiB</size>
				<partition>
					<size>50MiB</size>
					<label>boot</label>
					<bootable/>
				</partition>
				<partition>
					<size>remain</size>
					<label>rfs</label>
				</partition>
		</msdoshd>
	</images>
	<fstab>
		<bylabel>
			<label>rfs</label>
			<mountpoint>/</mountpoint>
			<fs>
				<type>ext2</type>
			</fs>
		</bylabel>
		<bylabel>
			<label>boot</label>
			<mountpoint>/boot</mountpoint>
			<fs>
				<type>vfat</type>
			</fs>
		</bylabel>
	</fstab>
...
</target>

The Raspberry Pi board also needs firmware binaries and configurations file to boot properly. We will use the overlay directory to add these Raspberry firmware files to the image:

$ mkdir -p overlay/boot
$ cd overlay/boot
$ wget https://github.com/raspberrypi/firmware/raw/1.20210201/boot/bootcode.bin
$ wget https://github.com/raspberrypi/firmware/raw/1.20210201/boot/start.elf
$ wget https://github.com/raspberrypi/firmware/raw/1.20210201/boot/fixup.dat
$ echo "console=ttyAMA0,115200 console=tty1 root=/dev/mmcblk0p2 rootwait" > cmdline.txt
$ echo "dtoverlay=miniuart-bt" > config.txt

ELBE stores the overlay uuencoded in the XML file using the chg_archive command:

$ elbe chg_archive examples/armhf-ubuntu.xml overlay

The archive node got created in the XML file.

To tell ELBE that the XML file has changed, you need to send it to the initvm:

$ elbe control set_xml $PRJ examples/armhf-ubuntu.xml

Then build the image with ELBE:

$ elbe control build $PRJ
$ elbe control wait_busy $PRJ

Finally, if the build completes successfully, you can retrieve the image file from the initvm:

$ elbe control get_files $PRJ
$ elbe control get_file $PRJ sdcard.img.tar.gz

Now you can flash the SD card image:

$ tar xf sdcard.img.tar.gz
$ dd if=sdcard.img of=/dev/sdX bs=1M

And boot the board with root and foo as login and password:

Ubuntu 18.04.1 LTS myUbuntu ttyAMA0

myUbuntu login: root
Password: 
Welcome to Ubuntu 18.04.1 LTS (GNU/Linux 5.10-rpi armv7l)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.

root@myUbuntu:~# 

Note: Ubuntu cannot be built for Raspberry A, B, B+, 0 and 0W according to https://wiki.ubuntu.com/ARM/RaspberryPi, as Ubuntu targets the ARMv7-A architecture, while the older RaspberryPi use an ARMv6 processor.

Further details

Building a small Debian root filesystem with Multistrap

There are several ways to build a root filesystem for an embedded Linux system: Buildroot and Open Embedded are the usual solutions to do this. They allow to fine tune the contents of your filesystem. The drawback is, in both cases, that you need to build everything from sources and this can take from tens of minutes to several hours.

Sometimes you don’t need all this flexibility and you just want to have a ready-to-use root filesystem, to which you just add a few extra programs. In this case using a distribution is a good solution. So let’s see what we need:

  • A binary distribution
  • Available on several architectures
  • Ability to generate a “small” root filesystem
  • A large choice of packages

Oh, I think it is a pretty good description of Debian!

Emdebian is a project to adapt Debian to embedded devices. A good description from the Debian wiki is:

“In short, what EmDebian does is wrap around the regular Debian package building tools to provide a more fine grained control over package selection, size, dependencies and content to enable creation of very small and efficient Debian packages for use on naturally resource limited embedded targets.”

And so, pretty recently (2009), Emdebian released Multistrap which is similar to Debootstrap but more appropriate for embedded devices. It seems better by the way it builds a system:

It works in a completely different way by simply using apt and dpkg, rather than avoiding to use them, which is how Debootstrap works.

And also more appropriate by its goals:

It is focused on producing rootfs images for devices, as opposed to chroots for existing machines

Practical case: build a root filesystem for the USB A9263 board from Calao Systems (arm926ejs based board).

A drawback of Multistrap is its limitation to Debian, but in fact it is also usable on any distribution based on Debian. In our case, we ran it on an Ubuntu 10.04 system.

First, let’s install Multistrap:

$sudo apt-get install multistrap dpkg-dev

Multistrap needs a config file. For our needs we just use the example one given by Embedian. Let’s name it multistrap.conf:

[General]
noauth=true
unpack=true
debootstrap=Grip
aptsources=Grip

[Grip]
# space separated package list
source=http://www.emdebian.org/grip
suite=lenny

Grip is the name of the lightweight Debian distro built by Emdebian.

Now we can run Multistrap:

$ multistrap -a armel -d $PWD/RFS -f multistrap.conf
em_multistrap 0.0.8 using multistrap.conf
Using foreign architecture: armel
em_multistrap building armel multistrap on 'amd64'
INF: Setting ./lib64 -> ./lib symbolic link.
Getting package lists: apt-get  -o Apt::Architecture=armel -o Apt::Get::AllowUnauthenticated=true -o Apt::Get::Download-Only=true -o Apt::Install-Recommends=false -o Dir=/home/mike/celf/multistrap/RFS/ -o Dir::Etc=/home/mike/celf/multistrap/RFS/etc/apt/ -o Dir::Etc::SourceList=/home/mike/celf/multistrap/RFS/etc/apt/sources.list.d/multistrap.sources.list -o Dir::State=/home/mike/celf/multistrap/RFS/var/lib/apt/ -o Dir::State::Status=/home/mike/celf/multistrap/RFS/var/lib/dpkg/status -o Dir::Cache=/home/mike/celf/multistrap/RFS/var/cache/apt/ update
Get:1 http://www.emdebian.org lenny Release.gpg [197B]
Ign http://www.emdebian.org/grip/ lenny/main Translation-en_US
Get:2 http://www.emdebian.org lenny Release [21.4kB]
Ign http://www.emdebian.org lenny Release
Ign http://www.emdebian.org lenny/main Packages
Ign http://www.emdebian.org lenny/main Sources
Ign http://www.emdebian.org lenny/main Packages
Ign http://www.emdebian.org lenny/main Sources
Get:3 http://www.emdebian.org lenny/main Packages [293kB]
Get:4 http://www.emdebian.org lenny/main Sources [351kB]
Fetched 665kB in 0s (6,280kB/s)                     
Reading package lists... Done
W: GPG error: http://www.emdebian.org lenny Release: The following signatures couldn't be verified because the public key is not available: NO_PUBKEY B5B7720097BB3B58
W: Duplicate sources.list entry http://www.emdebian.org/grip/ lenny/main Packages (/home/mike/celf/multistrap/RFS/var/lib/apt/lists/www.emdebian.org_grip_dists_lenny_main_binary-armel_Packages)
Use of uninitialized value within %packages in join or string at /usr/sbin/em_multistrap line 294.
Use of uninitialized value within %keyrings in join or string at /usr/sbin/em_multistrap line 296.
apt-get -y  -o Apt::Architecture=armel -o Apt::Get::AllowUnauthenticated=true -o Apt::Get::Download-Only=true -o Apt::Install-Recommends=false -o Dir=/home/mike/celf/multistrap/RFS/ -o Dir::Etc=/home/mike/celf/multistrap/RFS/etc/apt/ -o Dir::Etc::SourceList=/home/mike/celf/multistrap/RFS/etc/apt/sources.list.d/multistrap.sources.list -o Dir::State=/home/mike/celf/multistrap/RFS/var/lib/apt/ -o Dir::State::Status=/home/mike/celf/multistrap/RFS/var/lib/dpkg/status -o Dir::Cache=/home/mike/celf/multistrap/RFS/var/cache/apt/ install balloon3-config base-files base-passwd bash bsdutils coreutils debianutils diff dpkg e2fslibs e2fsprogs findutils gcc-4.3-base grep grip-config gzip hostname initscripts libacl1 libattr1 libblkid1 libc6 libcap1 libcomerr2 libdevmapper1.02.1 libgcc1 liblocale-gettext-perl libncurses5 libpam-modules libpam-runtime libpam0g libselinux1 libsepol1 libslang2 libss2 libstdc++6 libtext-charwidth-perl libtext-iconv-perl libtext-wrapi18n-perl libuuid1 libvolume-id0 login lsb-base makedev mawk mktemp mount ncurses-base ncurses-bin passwd perl-base procps sed sysv-rc sysvinit sysvinit-utils tar tzdata util-linux zlib1g
Reading package lists... Done
Building dependency tree... Done
The following extra packages will be installed:
  apt debconf debconf-i18n debian-archive-keyring dhcp3-client dhcp3-common gnupg gpgv ifupdown libbz2-1.0 libdb4.6 libncursesw5 libnewt0.52 libpopt0
  libreadline5 libssl0.9.8 libusb-0.1-4 lzma module-init-tools nano net-tools netbase ntpdate readline-common udev wget whiptail
The following NEW packages will be installed:
  apt balloon3-config base-files base-passwd bash bsdutils coreutils debconf debconf-i18n debian-archive-keyring debianutils dhcp3-client dhcp3-common diff
  dpkg e2fslibs e2fsprogs findutils gcc-4.3-base gnupg gpgv grep grip-config gzip hostname ifupdown initscripts libacl1 libattr1 libblkid1 libbz2-1.0 libc6
  libcap1 libcomerr2 libdb4.6 libdevmapper1.02.1 libgcc1 liblocale-gettext-perl libncurses5 libncursesw5 libnewt0.52 libpam-modules libpam-runtime libpam0g
  libpopt0 libreadline5 libselinux1 libsepol1 libslang2 libss2 libssl0.9.8 libstdc++6 libtext-charwidth-perl libtext-iconv-perl libtext-wrapi18n-perl
  libusb-0.1-4 libuuid1 libvolume-id0 login lsb-base lzma makedev mawk mktemp module-init-tools mount nano ncurses-base ncurses-bin net-tools netbase
  ntpdate passwd perl-base procps readline-common sed sysv-rc sysvinit sysvinit-utils tar tzdata udev util-linux wget whiptail zlib1g
0 upgraded, 87 newly installed, 0 to remove and 0 not upgraded.
Need to get 15.4MB of archives.
After this operation, 48.4MB of additional disk space will be used.
WARNING: The following packages cannot be authenticated!
  libstdc++6 libbz2-1.0 readline-common libncurses5 libreadline5 libusb-0.1-4 zlib1g gpgv gnupg debian-archive-keyring apt debianutils dhcp3-common
  libattr1 libacl1 libselinux1 coreutils lzma dpkg perl-base liblocale-gettext-perl libtext-iconv-perl libtext-charwidth-perl libtext-wrapi18n-perl
  debconf-i18n debconf dhcp3-client sed ncurses-bin lsb-base module-init-tools libssl0.9.8 wget hostname net-tools ifupdown mawk libncursesw5 nano netbase
  libcap1 ntpdate libpam-runtime libpam0g libdb4.6 libpam-modules passwd libvolume-id0 udev libslang2 libnewt0.52 libpopt0 whiptail grip-config
  gcc-4.3-base libgcc1 libc6 base-passwd base-files bash diff libcomerr2 e2fslibs libuuid1 libblkid1 libss2 e2fsprogs findutils grep gzip login mktemp
  mount libsepol1 sysvinit-utils initscripts sysv-rc sysvinit tar tzdata util-linux balloon3-config bsdutils libdevmapper1.02.1 makedev ncurses-base procps
Authentication warning overridden.
Get:1 http://www.emdebian.org/grip/ lenny/main libstdc++6 4.3.2-1.1em1 [268kB]
Get:2 http://www.emdebian.org/grip/ lenny/main libbz2-1.0 1.0.5-1em1 [37.2kB]
Get:3 http://www.emdebian.org/grip/ lenny/main readline-common 5.2-3.1em1 [3,202B]
Get:4 http://www.emdebian.org/grip/ lenny/main libncurses5 5.7+20081213-1em1 [165kB]
Get:5 http://www.emdebian.org/grip/ lenny/main libreadline5 5.2-3.1em1 [108kB]
Get:6 http://www.emdebian.org/grip/ lenny/main libusb-0.1-4 2:0.1.12-13em1 [13.7kB]
Get:7 http://www.emdebian.org/grip/ lenny/main zlib1g 1:1.2.3.3.dfsg-12em1 [48.1kB]
Get:8 http://www.emdebian.org/grip/ lenny/main gpgv 1.4.9-3+lenny1em1 [139kB]
Get:9 http://www.emdebian.org/grip/ lenny/main gnupg 1.4.9-3+lenny1em1 [533kB]
Get:10 http://www.emdebian.org/grip/ lenny/main debian-archive-keyring 2010.08.28~lenny1em1 [17.9kB]
Get:11 http://www.emdebian.org/grip/ lenny/main apt 0.7.20.2+lenny2em1 [514kB]
Get:12 http://www.emdebian.org/grip/ lenny/main debianutils 2.30em1 [23.4kB]
Get:13 http://www.emdebian.org/grip/ lenny/main dhcp3-common 3.1.1-6+lenny4em1 [157kB]
Get:14 http://www.emdebian.org/grip/ lenny/main libattr1 1:2.4.43-2em1 [7,706B]
Get:15 http://www.emdebian.org/grip/ lenny/main libacl1 2.2.47-2em1 [14.0kB]
Get:16 http://www.emdebian.org/grip/ lenny/main libselinux1 2.0.65-5em1 [50.0kB]
Get:17 http://www.emdebian.org/grip/ lenny/main coreutils 6.10-6em1 [1,162kB]
Get:18 http://www.emdebian.org/grip/ lenny/main lzma 4.43-14em1 [51.0kB]
Get:19 http://www.emdebian.org/grip/ lenny/main dpkg 1.14.29em1 [405kB]
Get:20 http://www.emdebian.org/grip/ lenny/main perl-base 5.10.0-19lenny2em1 [905kB]
Get:21 http://www.emdebian.org/grip/ lenny/main liblocale-gettext-perl 1.05-4em1 [11.0kB]
Get:22 http://www.emdebian.org/grip/ lenny/main libtext-iconv-perl 1.7-1+b1em1 [11.2kB]
Get:23 http://www.emdebian.org/grip/ lenny/main libtext-charwidth-perl 0.04-5+b1em1 [6,656B]
Get:24 http://www.emdebian.org/grip/ lenny/main libtext-wrapi18n-perl 0.06-6em1 [4,444B]
Get:25 http://www.emdebian.org/grip/ lenny/main debconf-i18n 1.5.24em1 [2,882B]
Get:26 http://www.emdebian.org/grip/ lenny/main debconf 1.5.24em1 [110kB]
Get:27 http://www.emdebian.org/grip/ lenny/main dhcp3-client 3.1.1-6+lenny4em1 [185kB]
Get:28 http://www.emdebian.org/grip/ lenny/main sed 4.1.5-6em1 [23.8kB]
Get:29 http://www.emdebian.org/grip/ lenny/main ncurses-bin 5.7+20081213-1em1 [70.8kB]
Get:30 http://www.emdebian.org/grip/ lenny/main lsb-base 3.2-20em1 [5,888B]
Get:31 http://www.emdebian.org/grip/ lenny/main module-init-tools 3.4-1em1 [44.5kB]
Get:32 http://www.emdebian.org/grip/ lenny/main libssl0.9.8 0.9.8g-15+lenny8em1 [713kB]
Get:33 http://www.emdebian.org/grip/ lenny/main wget 1.11.4-2+lenny2em1 [116kB]
Get:34 http://www.emdebian.org/grip/ lenny/main hostname 2.95em1 [5,808B]
Get:35 http://www.emdebian.org/grip/ lenny/main net-tools 1.60-22em1 [156kB]
Get:36 http://www.emdebian.org/grip/ lenny/main ifupdown 0.6.8+nmu1em1 [18.9kB]
Get:37 http://www.emdebian.org/grip/ lenny/main mawk 1.3.3-11.1em1 [51.2kB]
Get:38 http://www.emdebian.org/grip/ lenny/main libncursesw5 5.7+20081213-1em1 [187kB]
Get:39 http://www.emdebian.org/grip/ lenny/main nano 2.0.7-5em1 [83.6kB]
Get:40 http://www.emdebian.org/grip/ lenny/main netbase 4.34em1 [11.6kB]
Get:41 http://www.emdebian.org/grip/ lenny/main libcap1 1:1.10-14em1 [7,574B]
Get:42 http://www.emdebian.org/grip/ lenny/main ntpdate 1:4.2.4p4+dfsg-8lenny3em1 [36.1kB]
Get:43 http://www.emdebian.org/grip/ lenny/main libpam-runtime 1.0.1-5+lenny1em1 [7,786B]
Get:44 http://www.emdebian.org/grip/ lenny/main libpam0g 1.0.1-5+lenny1em1 [41.0kB]
Get:45 http://www.emdebian.org/grip/ lenny/main libdb4.6 4.6.21-11em1 [531kB]
Get:46 http://www.emdebian.org/grip/ lenny/main libpam-modules 1.0.1-5+lenny1em1 [160kB]
Get:47 http://www.emdebian.org/grip/ lenny/main passwd 1:4.1.1-6+lenny1em1 [267kB]
Get:48 http://www.emdebian.org/grip/ lenny/main libvolume-id0 0.125-7+lenny3em1 [18.2kB]
Get:49 http://www.emdebian.org/grip/ lenny/main udev 0.125-7+lenny3em1 [145kB]
Get:50 http://www.emdebian.org/grip/ lenny/main libslang2 2.1.3-3em1 [266kB]
Get:51 http://www.emdebian.org/grip/ lenny/main libnewt0.52 0.52.2-11.3+lenny1em1 [36.7kB]
Get:52 http://www.emdebian.org/grip/ lenny/main libpopt0 1.14-4em1 [22.3kB]
Get:53 http://www.emdebian.org/grip/ lenny/main whiptail 0.52.2-11.3+lenny1em1 [11.7kB]
Get:54 http://www.emdebian.org/grip/ lenny/main grip-config 0.1.2em1 [11.5kB]
Get:55 http://www.emdebian.org/grip/ lenny/main gcc-4.3-base 4.3.2-1.1em1 [5,496B]
Get:56 http://www.emdebian.org/grip/ lenny/main libgcc1 1:4.3.2-1.1em1 [23.7kB]
Get:57 http://www.emdebian.org/grip/ lenny/main libc6 2.7-18lenny4em1 [4,410kB]
Get:58 http://www.emdebian.org/grip/ lenny/main base-passwd 3.5.20em1 [11.5kB]
Get:59 http://www.emdebian.org/grip/ lenny/main base-files 5lenny7em1 [49.2kB]
Get:60 http://www.emdebian.org/grip/ lenny/main bash 3.2-4em1 [364kB]
Get:61 http://www.emdebian.org/grip/ lenny/main diff 2.8.1-12em1 [59.6kB]
Get:62 http://www.emdebian.org/grip/ lenny/main libcomerr2 1.41.3-1em1 [6,366B]
Get:63 http://www.emdebian.org/grip/ lenny/main e2fslibs 1.41.3-1em1 [91.3kB]
Get:64 http://www.emdebian.org/grip/ lenny/main libuuid1 1.41.3-1em1 [10.8kB]
Get:65 http://www.emdebian.org/grip/ lenny/main libblkid1 1.41.3-1em1 [21.6kB]
Get:66 http://www.emdebian.org/grip/ lenny/main libss2 1.41.3-1em1 [10.9kB]
Get:67 http://www.emdebian.org/grip/ lenny/main e2fsprogs 1.41.3-1em1 [234kB]
Get:68 http://www.emdebian.org/grip/ lenny/main findutils 4.4.0-2em1 [160kB]
Get:69 http://www.emdebian.org/grip/ lenny/main grep 2.5.3~dfsg-6em1 [128kB]
Get:70 http://www.emdebian.org/grip/ lenny/main gzip 1.3.12-6+lenny1em1 [44.0kB]
Get:71 http://www.emdebian.org/grip/ lenny/main login 1:4.1.1-6+lenny1em1 [50.6kB]
Get:72 http://www.emdebian.org/grip/ lenny/main mktemp 1.5-9em1 [5,772B]
Get:73 http://www.emdebian.org/grip/ lenny/main mount 2.13.1.1-1em1 [69.3kB]
Get:74 http://www.emdebian.org/grip/ lenny/main libsepol1 2.0.30-2em1 [96.4kB]
Get:75 http://www.emdebian.org/grip/ lenny/main sysvinit-utils 2.86.ds1-61em1 [17.8kB]
Get:76 http://www.emdebian.org/grip/ lenny/main initscripts 2.86.ds1-61em1 [33.6kB]
Get:77 http://www.emdebian.org/grip/ lenny/main sysv-rc 2.86.ds1-61em1 [13.7kB]
Get:78 http://www.emdebian.org/grip/ lenny/main sysvinit 2.86.ds1-61em1 [46.8kB]
Get:79 http://www.emdebian.org/grip/ lenny/main tar 1.20-1+lenny1em1 [148kB]
Get:80 http://www.emdebian.org/grip/ lenny/main tzdata 2010j-0lenny1em1 [749kB]
Get:81 http://www.emdebian.org/grip/ lenny/main util-linux 2.13.1.1-1em1 [293kB]
Get:82 http://www.emdebian.org/grip/ lenny/main balloon3-config 0.6 [2,400B]
Get:83 http://www.emdebian.org/grip/ lenny/main bsdutils 1:2.13.1.1-1em1 [17.0kB]
Get:84 http://www.emdebian.org/grip/ lenny/main libdevmapper1.02.1 2:1.02.27-4em1 [44.1kB]
Get:85 http://www.emdebian.org/grip/ lenny/main makedev 2.3.1-88em1 [15.8kB]
Get:86 http://www.emdebian.org/grip/ lenny/main ncurses-base 5.7+20081213-1em1 [16.4kB]
Get:87 http://www.emdebian.org/grip/ lenny/main procps 1:3.2.7-11em1 [160kB]
Fetched 15.4MB in 3s (4,819kB/s)
Download complete and in download only mode
I: Calculating obsolete packages
I: Extracting apt_0.7.20.2+lenny2em1_armel.deb...
 -> Processing conffiles for apt
I: Extracting balloon3-config_0.6_all.deb...
I: Extracting base-files_5lenny7em1_armel.deb...
 -> Processing conffiles for base-files
I: Extracting base-passwd_3.5.20em1_armel.deb...
I: Extracting bash_3.2-4em1_armel.deb...
 -> Processing conffiles for bash
I: Extracting bsdutils_1%3a2.13.1.1-1em1_armel.deb...
I: Extracting coreutils_6.10-6em1_armel.deb...
I: Extracting debconf-i18n_1.5.24em1_all.deb...
I: Extracting debconf_1.5.24em1_all.deb...
 -> Processing conffiles for debconf
I: Extracting debian-archive-keyring_2010.08.28~lenny1em1_all.deb...
I: Extracting debianutils_2.30em1_armel.deb...
I: Extracting dhcp3-client_3.1.1-6+lenny4em1_armel.deb...
 -> Processing conffiles for dhcp3-client
I: Extracting dhcp3-common_3.1.1-6+lenny4em1_armel.deb...
I: Extracting diff_2.8.1-12em1_armel.deb...
I: Extracting dpkg_1.14.29em1_armel.deb...
 -> Processing conffiles for dpkg
I: Extracting e2fslibs_1.41.3-1em1_armel.deb...
I: Extracting e2fsprogs_1.41.3-1em1_armel.deb...
 -> Processing conffiles for e2fsprogs
I: Extracting findutils_4.4.0-2em1_armel.deb...
I: Extracting gcc-4.3-base_4.3.2-1.1em1_armel.deb...
I: Extracting gnupg_1.4.9-3+lenny1em1_armel.deb...
I: Extracting gpgv_1.4.9-3+lenny1em1_armel.deb...
I: Extracting grep_2.5.3~dfsg-6em1_armel.deb...
I: Extracting grip-config_0.1.2em1_all.deb...
I: Extracting gzip_1.3.12-6+lenny1em1_armel.deb...
I: Extracting hostname_2.95em1_armel.deb...
I: Extracting ifupdown_0.6.8+nmu1em1_armel.deb...
 -> Processing conffiles for ifupdown
I: Extracting initscripts_2.86.ds1-61em1_armel.deb...
 -> Processing conffiles for initscripts
I: Extracting libacl1_2.2.47-2em1_armel.deb...
I: Extracting libattr1_1%3a2.4.43-2em1_armel.deb...
I: Extracting libblkid1_1.41.3-1em1_armel.deb...
I: Extracting libbz2-1.0_1.0.5-1em1_armel.deb...
I: Extracting libc6_2.7-18lenny4em1_armel.deb...
 -> Processing conffiles for libc6
I: Extracting libcap1_1%3a1.10-14em1_armel.deb...
I: Extracting libcomerr2_1.41.3-1em1_armel.deb...
I: Extracting libdb4.6_4.6.21-11em1_armel.deb...
I: Extracting libdevmapper1.02.1_2%3a1.02.27-4em1_armel.deb...
I: Extracting libgcc1_1%3a4.3.2-1.1em1_armel.deb...
I: Extracting liblocale-gettext-perl_1.05-4em1_armel.deb...
I: Extracting libncurses5_5.7+20081213-1em1_armel.deb...
I: Extracting libncursesw5_5.7+20081213-1em1_armel.deb...
I: Extracting libnewt0.52_0.52.2-11.3+lenny1em1_armel.deb...
I: Extracting libpam-modules_1.0.1-5+lenny1em1_armel.deb...
 -> Processing conffiles for libpam-modules
I: Extracting libpam-runtime_1.0.1-5+lenny1em1_all.deb...
 -> Processing conffiles for libpam-runtime
I: Extracting libpam0g_1.0.1-5+lenny1em1_armel.deb...
I: Extracting libpopt0_1.14-4em1_armel.deb...
I: Extracting libreadline5_5.2-3.1em1_armel.deb...
I: Extracting libselinux1_2.0.65-5em1_armel.deb...
I: Extracting libsepol1_2.0.30-2em1_armel.deb...
I: Extracting libslang2_2.1.3-3em1_armel.deb...
I: Extracting libss2_1.41.3-1em1_armel.deb...
I: Extracting libssl0.9.8_0.9.8g-15+lenny8em1_armel.deb...
I: Extracting libstdc++6_4.3.2-1.1em1_armel.deb...
I: Extracting libtext-charwidth-perl_0.04-5+b1em1_armel.deb...
I: Extracting libtext-iconv-perl_1.7-1+b1em1_armel.deb...
I: Extracting libtext-wrapi18n-perl_0.06-6em1_all.deb...
I: Extracting libusb-0.1-4_2%3a0.1.12-13em1_armel.deb...
I: Extracting libuuid1_1.41.3-1em1_armel.deb...
I: Extracting libvolume-id0_0.125-7+lenny3em1_armel.deb...
I: Extracting login_1%3a4.1.1-6+lenny1em1_armel.deb...
 -> Processing conffiles for login
I: Extracting lsb-base_3.2-20em1_all.deb...
I: Extracting lzma_4.43-14em1_armel.deb...
I: Extracting makedev_2.3.1-88em1_all.deb...
I: Extracting mawk_1.3.3-11.1em1_armel.deb...
I: Extracting mktemp_1.5-9em1_armel.deb...
I: Extracting module-init-tools_3.4-1em1_armel.deb...
 -> Processing conffiles for module-init-tools
I: Extracting mount_2.13.1.1-1em1_armel.deb...
I: Extracting nano_2.0.7-5em1_armel.deb...
 -> Processing conffiles for nano
I: Extracting ncurses-base_5.7+20081213-1em1_all.deb...
 -> Processing conffiles for ncurses-base
I: Extracting ncurses-bin_5.7+20081213-1em1_armel.deb...
I: Extracting net-tools_1.60-22em1_armel.deb...
I: Extracting netbase_4.34em1_all.deb...
 -> Processing conffiles for netbase
I: Extracting ntpdate_1%3a4.2.4p4+dfsg-8lenny3em1_armel.deb...
 -> Processing conffiles for ntpdate
I: Extracting passwd_1%3a4.1.1-6+lenny1em1_armel.deb...
 -> Processing conffiles for passwd
I: Extracting perl-base_5.10.0-19lenny2em1_armel.deb...
I: Extracting procps_1%3a3.2.7-11em1_armel.deb...
 -> Processing conffiles for procps
I: Extracting readline-common_5.2-3.1em1_all.deb...
I: Extracting sed_4.1.5-6em1_armel.deb...
I: Extracting sysv-rc_2.86.ds1-61em1_all.deb...
I: Extracting sysvinit-utils_2.86.ds1-61em1_armel.deb...
I: Extracting sysvinit_2.86.ds1-61em1_armel.deb...
I: Extracting tar_1.20-1+lenny1em1_armel.deb...
 -> Processing conffiles for tar
I: Extracting tzdata_2010j-0lenny1em1_all.deb...
I: Extracting udev_0.125-7+lenny3em1_armel.deb...
 -> Processing conffiles for udev
I: Extracting util-linux_2.13.1.1-1em1_armel.deb...
 -> Processing conffiles for util-linux
I: Extracting wget_1.11.4-2+lenny2em1_armel.deb...
 -> Processing conffiles for wget
I: Extracting whiptail_0.52.2-11.3+lenny1em1_armel.deb...
I: Extracting zlib1g_1%3a1.2.3.3.dfsg-12em1_armel.deb...
I: Unpacking complete.
Get:1 http://www.emdebian.org lenny Release.gpg [197B]
Ign http://www.emdebian.org/grip/ lenny/main Translation-en_US
Get:2 http://www.emdebian.org lenny Release [21.4kB]
Ign http://www.emdebian.org lenny Release
Ign http://www.emdebian.org lenny/main Packages
Ign http://www.emdebian.org lenny/main Sources
Ign http://www.emdebian.org lenny/main Packages
Ign http://www.emdebian.org lenny/main Sources
Hit http://www.emdebian.org lenny/main Packages
Hit http://www.emdebian.org lenny/main Sources
Fetched 198B in 0s (2,020B/s)
Reading package lists... Done
W: GPG error: http://www.emdebian.org lenny Release: The following signatures couldn't be verified because the public key is not available: NO_PUBKEY B5B7720097BB3B58

Multistrap system installed successfully in /home/mike/celf/multistrap/RFS/.

Let’s explain the parameters:

  • armel is the architecture, for our example: arm in little endian
  • -d $PWD/RFS is the output directory containing the root filesystem. Be careful to pass an absolute path.
  • -f multistrap.conf is the name of the configuration file

If you look at RFS/dev, you will see that there are no device files in it. This will be a problem at boot time, unless you build a kernel with the below options:

CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y

Thanks to these parameters, the kernel will automatically mount a tmpfs filesystem on /dev, and will populate it with devices present on the system.

Now we have a root filesystem, but we still need to run the package configuration scripts to make it usable. The packages were installed, but their configuration scripts couldn’t be executed, because they can only run on the target architecture.

The easiest way to do this is to use NFS. On the host side, you need to export the root filesystem directory through NFS. On the target side you have to select /bin/sh for the init process. A typical kernel command could be:

console=ttyS0,115200 root=/dev/nfs nfsroot=192.168.0.1:/path/RFS rw ip=192.168.0.20 init=/bin/sh

Then boot your board. You should reach a command line.

First mount /proc:

mount -t proc nodev /proc

Then configure your packages using this command line:

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin  dpkg --configure -a 

You will get a few questions about localization for tzdata, then the packages will be configured.

Finally go back to your host to change the RFS/etc/inittab file by uncommenting the below line and modifying it according to your serial console configuration (usually 115200). For example, replace

#T0:23:respawn:/sbin/getty -L ttyS0 9600 vt100

by

T0:23:respawn:/sbin/getty -L ttyS0 115200 vt100

As we built this root filesystem as a normal user we may have problems with some setuid programs, which need to be owned by the root user. So let’s change the ownership of some directories:

sudo chown root:root -R RFS/bin RFS/usr/bin RFS/sbin RFS/usr/sbin

The last trick is to delete the root password by modifying RFS/etc/passwd, by replacing

root:x:0:0:root:/root:/bin/bash

by

root::0:0:root:/root:/bin/bash

You can now reboot your system without the init=/bin/sh kernel parameter. We now have a ready to use embedded Linux root filesystem with the power of Debian.

Other things to fix and do:

  • Configure the /etc/resolv.conf file to be able to connect to the Internet.
  • Configure the gateway in the ip= kernel parameter (ip=client-ip:server-ip:gw-ip:netmask:hostname:device:autoconf). For example:
    bootargs=console=ttyS0,115200n8 root=/dev/nfs ip=192.168.2.100::192.168.2.1:255.255.255.0:emdebian:eth0:off nfsroot=192.168.2.1:/home/mike/work/celf/
  • Install other packages such as mtd-utils and vim
  • Install kernel modules (either manually or through a kernel package)
  • Add a new user
  • Create a minimum /etc/fstab file

You now have a Debian system for which it is very easy to add new software, and which can be configured in a very familiar way. That’s great to make product prototypes, small, low-power and secure servers for home or office use, and in some cases, even real products.

Issues with changing Ethernet device names

Beagle boardNo eth0 interface in your system!

If you are using Ubuntu or Debian on ARM, as I am doing at the moment, you may be surprised to see that your system has an eth1 interface (or even eth5), but no eth0!.

This also happens on x86, of course, and probably with other distributions. I faced and understood this problem when I migrated a KVM virtual machine from a regular network interface card to a virtio one. eth0 was gone, and because my network settings were bound to this interface, I couldn’t connect to my virtual machine anymore.

In recent releases of Ubuntu, this happens because of some udev rules, which try to make sure that each network card always gets bound to the same ethx device. So, every time the system see a new network card, the /etc/udev/rules.d/70-persistent-net.rules file gets updated as follows:

# This file was automatically generated by the /lib/udev/write_net_rules
# program, run by the persistent-net-generator.rules rules file.
#
# You can modify it, as long as you keep each rule on a single
# line, and change only the value of the NAME= key.

# PCI device 0x14e4:0x167a (tg3)
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="00:18:8b:87:52:12", ATTR{type}=="1",
KERNEL=="eth*", NAME="eth0"

# USB device 0x:0x (asix)
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="00:25:00:eb:eb:26", ATTR{type}=="1", KERNEL=="eth*",  NAME="eth1"

This can happen if you replace a USB to Ethernet dongle by another one with a different MAC address, or if you reuse a filesystem built on another board. Even if the board and Ethernet device are the same, you will get eth1 instead of eth0. This can be annoying if you configured your network through /etc/network/interfaces or if you bring up your network with your own scripts.

A clean solution for this problem is simply to remove the /etc/udev/rules.d/70-persistent-net.rules file. The next time you boot, you will get back to a normal eth0 network device.

I checked that these persistent net naming rules are not part of udev releases. In other distributions, you could have udev without such rules.