Creating Ubuntu/Debian rootfs from scratch for i.MX6/7 boards - Part I.

Published on June 10, 2019

For those who don't like a preconfigured, premade linux distribution images or they just want to know how its made we'd like to give some info here. The downloadable system images might be too big, including many unnecessary packages, demos. A Yocto linux or a Buildroot image is user configured by nature, users have to select options and packages, then they have to build the image from sources. This guide describes a similarly customizable Ubuntu/Debian rootfs creating process. There is a big difference you can see immediately : when you create a distribution image you don't build it from sources. The guys at debian.org or canonical.com (ubuntu.com) have already built and tested more than 20,000 debian packages per distribution for many architectures, you don't have to repeat it. Instead of that you'll select and download debian packages from repositories, and then you'll configure them. Obviously this is a way faster procedure, you don't need many hours to create a system image. I'm going to show an example of creating Ubuntu Xenial 16.04 with LXDE desktop, and a Debian Stretch 9.0 console (headless) system.

1. Installing prerequisites

The rootfs creating process will have 5 stages:

  • we create a base system image
  • we create a preliminary configuration
  • we install all the needed debian packages, and optionally desktop packages
  • we finish the configuration, the image is done now
  • we optionally convert and compress the image to prepare faster transferring and writing on SD card.

We have to install the following packages on the host PC (i386, amd64, ia64):

~/$ sudo apt-get -y install gnupg u-boot-tools git git-svn lzop execstack > pbuilder devscripts debootstrap qemu binfmt-support ubuntu-dev-tools > libncurses5-dev pigz pixz qemu-user-static ~/$ if [ `lsb_release -sc` != "stretch" ] ; then sudo apt-get -y install module-init-tools ; fi

As you can see in the last line, you  have to install module-init-tools only when you are not working with Debian Stretch. Of course you don't need to write an if line, I just wanted to show that line was conditional. 

2. Creating base image

We'll create a base image with pbuilder. Its already been installed in the previous paragraph. Let's separate the Ubuntu and Debian threads, there are differences and common parts in the process.

2.1 Ubuntu Xenial for instance:
~/$ export ROOTFS_DISTRO=xenial ~/$ export PB_MIRROR=https://ports.ubuntu.com/ubuntu-ports ~/$ export PB_COMPONENTS="main restricted universe multiverse"
  1. Set the distribution name (lowercase). It could be any valid and  living Ubuntu distribution name. Boundary Devices supports only trusty (14.04) and xenial (16.04) at the moment and in general only the LTS distributions in the future.
  2. This is the main Ubuntu apt repository for armhf.  Please check this link if you're looking for mirrors.
  3. The list of official Ubuntu components.
2.2 Debian Stretch for instance:
~/$ export ROOTFS_DISTRO=stretch ~/$ export PB_MIRROR=https://ftp.debian.org/debian ~/$ export PB_COMPONENTS="main non-free contrib"
  1. Set the distribution name (lowercase). It could be any valid and living Debian distribution name. Boundary Devices supports only jessie (8.x) and stretch (9.x) at the moment, the next will be named busty (10.x) in about 2 years.
  2. This is the main Debian apt repository for armhf. Pleas check this link if you are looking for mirrors.
  3. The list of official Debian components.
2.3 Common:

You need to prepare a pbuilder configuration file. Create a template file with the following content :

~$ cat .pbuilderrc.in ARCH="armhf" APTCACHE=@WORKDIR@/pbuilder/@DISTRO@/cache APTCACHEHARDLINK=yes COMPRESSPROG="pigz" BUILDPLACE=@WORKDIR@/pbuilder/@DISTRO@/build BUILDRESULT=@WORKDIR@/pbuilder/@DISTRO@/result HOOKDIR="" DEBOOTSTRAP="qemu-debootstrap" DEBOOTSTRAPOPTS=( '--variant=@VARIANT@' '--arch=armhf' )

And then type the following commands to create a base image:

~/$ export WORKDIR=~ ~/$ export ROOTFS_DIR=$WORKDIR/$ROOTFS_DISTRO-rootfs ; if [ "x$ROOTFS_DIR" != "x" ] ; then sudo rm -rf $ROOTFS_DIR/* ; else mkdir $ROOTFS_DIR ; fi ~/$ export PB_CONFIG=$HOME/.pbuilderrc ~/$ cp -f $PB_CONFIG.in $PB_CONFIG ~/$ sed "s,@WORKDIR@,$WORKDIR,g" -i $PB_CONFIG ~/$ sed "s,@DISTRO@,$ROOTFS_DISTRO,g" -i $PB_CONFIG ~/$ sed "s,@VARIANT@,minbase,g" -i $PB_CONFIG ~/$ sudo pbuilder --create --configfile $PB_CONFIG --no-targz --buildplace $ROOTFS_DIR > --components $PB_COMPONENTS > --mirror $PB_MIRROR > --othermirror"deb-src $PB_MIRROR $ROOTFS_DISTRO $PB_COMPONENTS" > --extrapackages "sudo apt-utils"

Let's see line by line:

  1. Set the working directory. I use the $HOME directory in these examples, you can use whatever you prefer.
  2. Set the ROOTFS_DIR and make it empty or create it, if doesn't exist. This is the place where we create our new system. I always check the variables before deleting, because sudo rm -rf is a life dangerous command, if you mistype a variable name you'll delete / , you can destroy the whole system in seconds. Always check the sudo rm -rf command four times maybe five.
  3. Set the pbuilder config file name. Its a standard config file at $HOME/.pbuilderrc .
  4. Copy the template to the real config file.
  5. Modify template, set working directory.
  6. Modify template, set distribution name.
  7. Modify template, set debootstrap variant parameter as your choice. It can be the following:
    • minbase - it will install the system required packages + apt . This is the smallest system. This is the recommended, because installing new packages is always easier than removing them.
    • buildd - it will install the system required packages + build-essential packages . Choose this if you want to develop and build packages.
    • system required packages are the packages necessary to proper functioning, see debian-package-priorities and man debootsrap for the --variant option.
  8. We run pbuilder with the set parameters, it will mount/unmount the necessary directories, it will call debootstrap and downloads the packages automatically, extracts and configures them in the ROOTFS_DIR. It takes about 10 minutes maybe less with good internet connection.
  9. - 12. Command continuation lines.

Now you have a base system image. Its not configured and it has no kernel, obviously it doesn't work yet, but now we can start to configure it, and we can install more and more packages. We can modify the image with single chroot command, or if we want to run a shell script, we can use pbuilder execute to run multiple commands. Let's define these:

~/$ export CHROOT_QEMU_CMD=sudo chroot $ROOTFS_DIR /usr/bin/qemu-arm-static -cpu cortex-a9 ~/$ export PB_EXEC_CMD=sudo pbuilder --execute --configfile $PB_CONFIG --no-targz --buildplace $ROOTFS_DIR --bindmounts "/tmp /mnt"

All the scripts you want to run must be created or copied in /mnt directory. That directory will be mounted up and will be visible for pbuilder, as you can see in the above line.

3. Preliminary configuration