Scheda Linux

Getting a Linux-based board up and running can be a daunting task even for an avid Linux user and microcontroller expert. There are several moving parts, and an in-depth knowledge of the Linux ecosystem is pretty much a must.

Never fear though, if you already have some experience using Linux as a development platform, this post will give you all the pointers required for getting that login prompt to show up on your board, in the simplest possible way.

A world of possibilities

When deciding to create a custom Linux distribution for our board, we immediately find ourselves at a fork in the road: should we build it completely from scratch, or look for something that could help us on the perilous journey ahead?

The former path is a rewarding, although often excruciatingly long, experience. It gives the user an insight on the inner workings of its machine, from which he can later benefit in several ways. But it’s also a task that requires a good amount of pre-existing knowledge of an operating system’s internals, and it is definitely not the most efficient way of setting up a custom Linux distro. If you are up to the task however, LFS is that way.

The latter option is instead a regularly travelled road. Several tools have been perfected in the last decade or so to deal with the custom distro problem. Some of them are easier to use, but lack flexibility, while others can do almost anything, given enough time and expertise. They are called Linux build systems, and they take it upon themselves to do all the heavy lifting for you: downloading all the required source code, getting a toolchain, building all the pieces and putting them together. All you need to do is feed them some configuration files and watch them spin!

We will take the safe route and choose one of these tools for our first build. Despite their variety, nowadays it basically boils down to two contenders:

There are other alternatives, such as OpenWRT or LTIB, but they are often too specific to a certain field (the former) or lack some essential features (the latter).

For a beginner, Buildroot is definitely the right choice, and it’s what we will focus on in the following sections. Yocto is the logical step forward for when Buildroot just isn’t enough.

Nuts and bolts

Before getting started with Buildroot, it’s nice to have a general overview of all that is required to have a functioning Linux system. Some pieces may vary depending on the architecture, but usually you’ll always need:

Once you have these three components, the final step is putting them together and finally load them, in a way that depends on your custom board. To give you a better grasp on these concepts, we will use Raspberry Pi as our example target board throughout this post.

Preparing the build environment

To get started, we will need a machine running Linux, which is the only OS officially supported by Buildroot. Any Linux flavor will do (within reason) as long as you can install all the mandatory tools required for Buildroot to run.

Next, we will need the Buildroot source code. You have two options here: getting a tarball or checking out the gitrepository. I recommend cloning the repository: you get version control for free and you can easily undo any breaking changes while experimenting. It basically boils down to:

~ $ git clone git://git.buildroot.net/buildroot
~ $ cd buildroot/

It’s also good practice to start developing from a stable tag, in order to always be able to backtrace to a known point in history:

~/buildroot $ git checkout 2018.08

Let’s now have a peek at the contents of the buildroot/ directory:

~/buildroot $ ls
arch/     docs/     toolchain/  CHANGES           DEVELOPERS
board/    fs/       support/    Config.in         Makefile
boot/     linux/    system/     Config.in.legacy  Makefile.legacy
configs/  package/  utils/      COPYING           README

There’s a lot going on in here, but we can notice a couple of things:

Also, Buildroot has an excellent documentation that you should check out if you want to know more about what the rest of the source code does.

Configuring Buildroot

As said before, these build systems need some kind of configuration in order to know what to build for which target, and we’ve just encountered a configs/ directory while diving into the code. Turns out that this directory contains many pre-baked configurations for several products: evaluation kits, development and consumer boards, etc. Among these, there’s also a nice raspberrypi_defconfig that we can use as a starting point for our distribution.

Buildroot configurations follow the Kconfig format, used also by the Linux kernel. If you want to know more about this format, you can start from here. Also, try not to start from scratch when developing support for your own custom hardware. It’s almost always better to start from a configuration for a similar evaluation kit or development board and work your way up from there.

In Buildroot, you can apply a configuration file by running make <config-file-name>, provided that the file name ends in _defconfig and is located in the configs/ directory. In our case this will do the trick:

~/buildroot $ make raspberrypi_defconfig
...
#
# configuration written to ~/buildroot/.config
#

It’s now time to take a look at the configuration we’ve just applied!

Tweaking the base distribution

Although we could simply open the configuration file with a text editor and have a look around, Buildroot provides some nicer tools to view and modify the applied configuration.

For the command-line enthusiasts, the make menuconfig command will open an interactive curses-based configurator which you can navigate to modify your active configuration. This is extremely useful for when you are logged on your build machine remotely. For the more GUI-inclined, the same result can be achieved using make xconfig or make gconfig to open a Qt or GTK configurator, respectively.

In any case, you will be presented with a list of menus:

    Target options  --->
    Build options  --->
    Toolchain  --->
    System configuration  --->
    Kernel  --->
    Target packages  --->
    Filesystem images  --->
    Bootloaders  --->
    Host utilities  --->
    Legacy config options  --->

The menus are pretty self-explanatory, and the best way to learn is by delving into them and read the docs about the different options (which you can do by pressing ? on most of them). Some major pointers:

Let’s say that we want to change our custom distribution hostname to rpi and install an OpenSSH server. We will also increase the filesystem size to 100MB in order to accommodate for this.

The default hostname (buildroot) can be changed using the System hostname option:

System configuration  --->
    (rpi) System hostname

OpenSSH can be installed by navigating the Target packages menus and selecting openssh:

Target packages  --->
    Networking applications --->
        [*] openssh

Finally, resizing the final root filesystem image can be achieved by modifying the exact size option in the Filesystem images section (by default 60M):

Filesystem images  --->
    (100M) exact size

That’s it. You can just save and exit the configurator to apply the changes (just press ESC a bunch of times and select <Yes> when prompted to save if you are using menuconfig).

In Buildroot, all configuration changes are stored locally in a .config file in the root directory. This file is temporary, and it’s not meant to be version-controlled. To permanently store configuration changes and share them with other people on your team, you can run the make savedefconfig command. This will overwrite the configuration file that you originally used to generate the configuration (in our case raspberrypi_defconfig) with the newly applied changes, ready to be committed to version control.

Now, it’s finally time to build the distribution!

Building the system image

Depending on the number of selected packages, and whether or not we are building toolchain, kernel and bootloaders from scratch, the build time can vary from several minutes to several hours. A build can be initiated simply by issuing:

~/buildroot $ make

Do note that Buildroot does not support top-level parallel make using -jN, so it should never be used as to avoid strange issues.

Once it’s done, you will find that two new directories have been created:

We are particularly interested in the contents of the output/images/ directory, where all the final build outputs will be placed. Its content mostly depends on the target architecture and some configuration options, but you will usually always find:

In some cases, Buildroot will also produce for you an SD card or USB image, already formatted and partitioned in such a way that the processor is able to boot from it. This is the case for the RPi: you can find the bootable SD card image under the name sdcard.img.

Deploying to the board

# WARNING: THIS IS DANGEROUS AND CAN DESTROY YOUR DISK, USE WISELY
~/buildroot $ sudo dd if=output/images/sdcard.img of=<sd-device>

where <sd-device> is the device under /dev corresponding to your SD card (depending on your machine, this could be something in the /dev/sdX or /dev/mmcblkX format).

Once this step is over, you can insert the SD card into your RPi and power it up. After the initial boot phase, you should see a login prompt on the serial port: just put in root as username to log into your brand new system!

Further tweaking and tuning

Remember that board/ directory we introduced but never actually used? Turns out it can be used to store anything and everything specific to any of the boards supported by Buildroot.

As an example, if we venture into board/raspberrypi/ we can notice some interesting stuff:

As you can see, it’s all stuff related to RPi which wouldn’t fit anywhere else in the source code, so it’s all grouped in the board/ directory.

Further uses for a board directory are storing kernel or bootloader configurations and/or patches, pre-built assets, and so on. The sky is the limit! For additional info about project-specific customizations, you can refer to the dedicated section in the docs.

Conclusions

That’s it! Buildroot makes it extremely easy to build a completely customized Linux distribution without any knowledge of what’s going on under the hood.

And since the best way to learn is by doing, go ahead and try to further modify this base distribution, or even start your own brand new RPi project by applying everything that you’ve learned!