+++ title = 'Minimalist Gentoo for the Raspberry Pi' date = 2012-12-17T02:55:00Z +++ I've spent the last several days working on a minimalist build of Gentoo Linux for my [Raspberry Pi](http://www.raspberrypi.org/). By minimalist, I mean only the absolute smallest set of packages required to boot and log in. I intend to build another [MPD Appliance](https://plus.google.com/u/0/111156619169863248480/posts/TB7pB6xuU5Y), or something similar, with it, so I don't need a full-blown Gentoo installation. The bare minimum packages are: * busybox * coreutils * grep * findutils * net-tools * e2fsprogs * dosfstools * module-init-tools * sed * file * less * kbd * shadow * gzip * bzip2 * procps In addition, I installed the following to make my life easier: * bash * iproute2 * ntp * vim # Staging Areas Cross-compiling the system will take place in three stages: * Sysroot * Build root * Deployment root We'll take advantage of Portage's `buildpkg` *FEATURES* flag so that we don't have to compile everything three times. ## Sysroot This stage is where the toolchain will be built and installed. Because of how crossdev works, build-time dependencies of all of our required software will also have to be installed here. The toolchain will look for headers and shared objects in the directory hierarchy under the sysroot when when compiling and linking. Unfortunately, that means all the dependencies will end up being installed here as well as in the build root. ## Build Root This stage is where we'll actually build all the software we want to install on the Raspberry Pi. This intermediate stage is necessary because some software has runtime dependencies on toolchain components like glibc, and we can't install those in the sysroot because it will break the cross-compiling toolchain. ## Deployment Root This stage is the final destination for the packages we built in the build root: the SD card (or QEMU disk image, if you don't have a Raspberry Pi yet). While it isn't strictly necessary to separate the build and deployment roots, it can make it easier to correct problems that may arise, and speed things up if you're building for more than one device. # Crossdev The first thing you'll need to do is set up [Crossdev](http://www.gentoo.org/proj/en/base/embedded/cross-development.xml). The Raspberry Pi's System-on-a-Chip is a [Broadcom BCM2835](http://www.broadcom.com/products/BCM2835), which contains an [ARM1176JZF-S](http://infocenter.arm.com/help/topic/com.arm.doc.ddi0301h/DDI0301H_arm1176jzfs_r0p7_trm.pdf) CPU. I'll be using the GNU standard C library, so the toolchain tuple will be `armv6j-hardfloat-linux-gnueabi`. ```sh crossdev -S -t armv6j-hardfloat-linux-gnueabi ``` Make sure to use the `-S` option for crossdev; the current unstable versions of the toolchain do not work together and GCC fails to build. This will create the "sysroot" stage in `/usr/armv6j-hardfloat-linux-gnueabi`, where we'll install the build-time dependencies for the software we want on the Pi. # Configuration Now that we have a working toolchain for cross compiling, we need to configure Portage to build the software how we want. We'll create a directory structure just like that of `/etc/portage` and fill it with a few important files. ```sh mkdir -p configroot/etc/portage/ cp /usr/armv6j-hardfloat-linux-gnueabi/etc/portage/make.conf configroot/etc/portage ``` You will probably want to modify the `make.conf` file crossdev produces. For example, I made these changes: * Remove `~arm` from *ACCEPT_KEYWORDS* * Add the GCC flags to *MARCH_TUNE* for the Raspberry Pi, per the [RPi Wiki](http://elinux.org/RPi_Software#ARM). Note, `-Ofast` doesn't work very well, and I had trouble compiling several packages with it. Using `-Os` or `-O4` work just fine, though. * Add `cxx`, `unicode`, and `ipv6` *USE* flags. I also removed the `make-symlinks` *USE* flag because I will also install Bash and a few other packages that collide with Busybox when that's set. Next, you'll need to pick a profile. Crossdev defaults to the *embedded* profile, which is fine. You could also use the *arch/arm/armv6j* profile, but then you'll need to add some extra variables in `configroot/etc/portage/profile/make.defaults`, like `KERNEL` and `USERLAND`. ```sh ln -s /usr/portage/profiles/embedded configroot/etc/portage/make.profile ``` You may also want to make `package.use`, `package.mask`, and/or `package.keywords` directories and populate them to your liking. I had to add `=sys-apps/coreutils-8.20` to `package.mask` due to [GNU Bug #12741](http://debbugs.gnu.org/cgi/bugreport.cgi?bug=12741), for example. Finally, we need to prevent Portage from installing any of the toolchain components in the SYSROOT (they're already there) while we build the rest of the dependencies. You'll need a `package.provided` directory in `configroot/etc/portage/profile`, and it should contain the full atom for each of the four packages built by crossdev (binutils, gcc, glibc, and linux-headers): ```sh mkdir -p configroot/etc/portage/profile/package.provided touch configroot/etc/portage/profile/package.provided/crossdev ``` To find the versions of the cross toolchain, use `equery`: ```sh equery list 'cross-armv6j-hardfloat-linux-gnueabi/*' ``` Then, put each atom (with the proper category, not the cross category) and version on a separate line in `package.provided/crossdev`. Something like this: ``` sys-devel/binutils-2.22-r1 sys-devel/gcc-4.5.4 sys-libs/glibc-2.15-r3 sys-kernel/linux-headers-3.6 ``` Now, Portage won't pull in any of those packages when building the dependency tree for our packages. Now, set the PORTAGE_CONFIG environment variable to tell Portage to use the settings in this directory, instead of the one in /usr/armv6j-hardfloat-linux-gnueabi: ```sh export PORTAGE_CONFIGROOT=${PWD}/configroot ``` # Install Build Dependencies No that we've got our cross toolchain, sysroot, and configuration ready to go, it is time to install the build dependencies for our packages. Put the list of packages you want to install in a variable (i.e. `install_pkgs="busybox coreutils …"`), and then install just their dependencies: ```sh armv6j-hardfloat-linux-gnueabi-emerge --onlydeps --buildpkg --oneshot --ask $install_pkgs ``` # Installing in the Build Root Once all the build dependencies are installed, it is time to start the build root stage. The build root will be almost identical to the deployment root, so we'll everything in the build root first, so Portage will build a binary package and speed up the final step. Before installing anything, you need to remove the `package.provided/crossdev` file you created earlier. Since we're no longer installing things in the sysroot, we do want any toolchain components to be installed, if necessary. ```sh rm configroot/etc/portage/profile/package.provided/crossdev ``` Next, set the `ROOT` environment variable to the absolute path of your build root directory: ```sh export ROOT=/home/dustin/raspberrypi ``` Remember, the build root is **not** your Raspberry Pi's SD card, so don't use that path just yet. ## Installing baselayout Baselayout needs to be installed in two passes. Baselayout needs to be the first package installed on the system, or it will fail to create directories and symbolic links correctly. To ensure it gets installed before anything else, we explicitly install it, without dependencies: ```sh armv6j-hardfloat-linux-gnueabi-emerge --nodeps --buildpkg --ask baselayout ``` This basically creates an empty directory structure and some symlinks for compatibility. Once that's done, we'll go ahead and install the rest of the base system: ```sh armv6j-hardfloat-linux-gnueabi-emerge --onlydeps --buildpkg --usepkg --ask baselayout ``` This will pull in the rest of the core system packages, including sysvinit, OpenRC, etc. ## Installing Selected Packages Once baselayout is installed, it is time to install the rest of the core packages: ```sh armv6j-hardfloat-linux-gnueabi-emerge --buildpkg --usepkg --ask $install_pkgs ``` You'll notice that most of the packages being installed at this point are binaries. That's because we've already compiled them in the sysroot, so we don't need to do it again. # Installing in the Deployment Root Finally! Now we actually get to install stuff on the SD card! Make sure you've partitioned and formatted the SD card correctly. See the [Raspberry Pi Gentoo Wiki Page](http://wiki.gentoo.org/wiki/Raspberry_Pi#Preparing_the_SD_card) for details. Mount the SD card partition you've designated as the root partition, and then reset the `ROOT` environment variable to point to it: ```sh mkdir /mnt/raspberrypi mount /dev/mmcblk0p2 /mnt/raspberrypi export ROOT=/mnt/raspberrypi/ ``` Before installing anything, there are a few empty directories we need to make manually. They aren't created by any package, but are critical to boot the system: ```sh mkdir $ROOT/{boot,dev,proc,root,sys,tmp} ``` Then, install the packages. Everything should just be binary merges at this point, so it won't take too long. ```sh armv6j-hardfloat-linux-gnueabi-emerge --usepkg --ask $install_pkgs ``` # Finishing Up ## libgcc_s.so.1 On ARM, Bash (and possibly other packages) depend on the libgcc runtime. This confused me for a while, because on my other minimalist system (which runs on an Atom 230), I didn't need to install GCC and Bash worked fine. Fortunately, all you need is `libgcc_s.so.1` to make it happy, not the whole GCC installation. You can copy the one from the cross toolchain: ```sh cp /usr/lib/gcc/armv6j-hardfloat-linux-gnueabi/4.5.4/libgcc_s.so.1 $ROOT/lib/ ``` ## Time Zone You need to set the time zone, just as you would on a full system: ```sh echo 'America/Chicago' > $ROOT/etc/timezone ln -snf /usr/share/zoneinfo/America/Chicago $ROOT/etc/localtime ``` ## Root Password Setting the root password can be tricky. Although `passwd` has a `--root` option, it doesn't seem to work in any situation I've tried. Normally, I'd recommend blanking the password and forcing it to be set at first log in, but since the Raspberry Pi has no idea what time it is initially, password expiration doesn't work. Thus, you'll just have to blank the password and hope you remember to set it to something secure on your own. ```sh sed -i 's/^root:.*/root::::::::/' $ROOT/etc/shadow ``` ## Services ### swclock Since the Raspberry Pi has no real-time clock, the *hwclock* service just complains. We'll remove it and add the *swclock* service instead. While not an accurate way of keeping time (setting the clock based on the mtime of a file created at last shutdown), it will hopefully at least get the clock in the right decade. ```sh rm $ROOT/etc/runlevels/boot/hwclock ln -s /etc/init.d/swclock $ROOT/etc/runlevels/boot/ ``` ### Network If you have a Model B device and intend to use the Ethernet port, you can have it start at boot: ```sh ln -s net.lo $ROOT/etc/init.d/net.eth0 ln -s /etc/init.d/net.eth0 $ROOT/etc/runlevels/default ``` ### NTP If you installed NTP, you'll want it to start at boot as well, so the time on the device is accurate: ```sh ln -s /etc/init.d/ntp-client $ROOT/etc/runlevels/default ln -s /etc/init.d/ntpd $ROOT/etc/runlevels/default ``` ## Firmware, Kernel, and Modules Clone the Raspberry Pi firmware project on [Github](https://github.com/raspberrypi/firmware). This will get you the latest GPU firmware and bootloader, as well as a precompiled Linux kernel with modules. You can always compile your own kernel later, if you want. ```sh git clone git://github.com/raspberrypi/firmware.git ``` Mount the first partition of your SD card and copy the firmware there: ```sh mount /dev/mmcblk0p1 /mnt/raspberrypi/boot cp firmware/boot/* /mnt/raspberrypi/boot ``` Copy the pre-compiled kerne modules to the `/lib/` directory on your SD card's root partition: ```sh cp -a firmware/modules $ROOT/lib/ ``` ## /etc/inittab You may want to make a couple of changes to `/etc/inittab`. First, I don't like the new *agetty* behavior of clearing the screen before displaying the login prompt, at least on the first TTY; it makes it difficult to see error messages during the boot process. To change it, add `--noclear` to the `c1` definition: ```sh sed -i 's/^c1\(.*\)agetty 38400\(.*\)/c1\1agetty --noclear 38400\2/' $ROOT/etc/inittab ``` Also, the default inittab sets up a serial console on `/dev/ttyS0`, but that port doesn't exist on a Raspberry Pi. You can either comment out that line, or change it to use the UART port on the Pi: ```sh sed -i 's/ttyS0/ttyAMA0/g' $ROOT/etc/inittab ``` ## /etc/fstab Finally, you need to make sure the `fstab` file in the deployment root is correct. For the Raspberry Pi, the SD card's block device will always be `/dev/mmcblk0`. Each partition will be numbered, starting with 1, and prefixed with a "p". The first partition would be `/dev/mmcblk0p1`, etc. Make sure you set the "type" column to `vfat` for the boot partition. # That's It... ...but don't get in a hurry! Make sure you sync all filesystem changes before you remove the SD card from your computer, since SD cards report writes as complete before actually committing them to the flash. ```sh sync ; sync ; sync umount /mnt/raspberrypi/boot umount /mnt/raspberrypi ``` Now you can safely remove the SD card and pop it in your Raspberry Pi. Congratulations, and good luck!