How to Build Your Own Tailor-made IoT Linux OS (Part 2)

Configuring and Building Your Linux System

--

This is the 2nd of part of this series, to get to the beginning please see Part 1.

Installing Mandatory Packages

You need to install the necessary packages that Buildroot needs to work in your Linux system (you can get the exhaustive list of software and libraries that Buildroot needs to work in the docs/manual/prerequisite.txt file of Buildroot). you can run the following command:

Installing Buildroot

Buildroot Directory Structure

The Buildroot directory structure and their description:

  • The arch directory: storage for configurations for all the supported architectures
  • The board directory: storage for default configurations for different hardware platforms
  • The configs directory: where to allocate generic configurations for kernel and packages
  • The dl directory: where to copy sources and repositories, as the previous step before compiling
  • The docs directory: contains documentation about Buildroot
  • The Linux directory: contains sources and configurations to generate the Linux kernel
  • The toolchain directory: contains the recipes to generate the toolchain that works with our platform
  • The output/images directory: where to generate binary images; both RFS and Linux kernel will copy here

Configuring and Building Your Linux System

Now, it’s time to configure the Buildroot script for a Raspberry Pi 3 model B configuration. This will set up all the basic configuration options to work properly with our hardware. Buildroot already has some preconfigured settings for some of the most popular boards, including RPI, that you can use, but in this case to learn what to do, create your own Buildroot configuration.

The next steps will briefly and individually cover everything involved in the creation of system images. Buildroot performs the following steps when you execute it: first, it downloads all the related source files, compiles the toolchain that will be used to cross-compile the kernel and the rest of the applications, compiles the Linux kernel, and generates a basic RFS using the BusyBox tool.
Finally, it compiles the configuration files and the third-party applications, and the system deploys according to the user configuration.

Configuring

Inside the Buildroot directory, as non-root user run:

Please make sure to edit your configuration to match the one shown below.

Target Options
For a Raspberry Pi 3 board, set the following target options parameters:

Build Options

Toolchain

In software, a toolchain is a set of programming tools used to perform a complex software development task or to create a software product, which is typically another computer program or a set of related programs.

A toolchain is a set of distinct software development tools that link (or chain) together at specific stages such as GCC, binutils and glibc (a portion of the GNU Toolchain). Optionally, a toolchain may contain other tools such as a debugger or a compiler for a specific programming language, such as C++. Quite often, the toolchain used for embedded development is a cross-toolchain, or more commonly known as a cross compiler. All the programs (like GCC) run on a host system of a specific architecture (such as x86), but they produce binary code (executables) to run on a different architecture (for example, ARM). This is cross-compilation and is the typical way of building embedded software. It’s possible to compile natively, running GCC on your target (source).

More simply, the toolchain is the building block (set of libraries and software) to compile the Linux system for a CPU architecture different from yours.

Selecting toolchain on Buildroot

In this case, you’ll use the built-in toolchain that comes with Buildroot, with the right kernel headers for the image.

Kernel Headers — What Are They?
The header files define an interface: they specify how the functions in the source file are defined.
They are used so that a compiler can check if the usage of a function is correct as the function signature (return value and parameters) is present in the header file. For this task the actual implementation of the function is not necessary.
You could do the same with the complete kernel sources but you will install a lot of unnecessary files. Example: if I want to use the function int foo(double param);

In a program, I do not need to know how the implementation of foo is, I just need to know that it accepts a single param (double) and returns an integer.

Make sure your settings are similar:

System Configuration

Entering fun stuff, configure a name, password, and other parameters for your OS:

Custom scripts
Custom scripts will configure (before and after building the image/kernel) certain parameters and change files, for example, if you use a specific board, you can configure its kernel boot parameters to enable/disable certain features that are specific to that chip.

Root filesystem overlay
A filesystem overlay is a tree of files that copies directly over the target filesystem after it’s built. Filesystem overlays are simply directories that copy over the root filesystem at the end of the build.
You may use this feature to prepare files and directories that will be part of the final root filesystem (for example a /etc/wpa_supplicant.conf file containing the default Wi-Fi and password to connect to).

Kernel

Kernel version
Use the following kernel version (Choose “Custom tarball” and enter the following:

In-tree device tree source file names
Enter the following:

Target packages

Here you’ll choose which packages and binaries to install by default on the system.

Under Hardware Handling > Firmware, select:

Under Networking Applications, select:

And select iw and iputils packages, as well.

Other than that, feel free to add any package you’d like :)

Filesystem Images

Go with a hardcoded 120MB of / root partition space, using ext4 format.

Bootloaders

Skip for now, we’ll come back to this later.

Building

Save and exit, it’s a good idea to copying your .config file to a separated space.
Now it’s time for compilation, under the buildroot-2020.02.6 directory; run:

Note: There is another way to better keep all your board-specific configs, packets and patches in a separate directory outside and setup BR2_EXTERNAL during the build. That allows updating buildroot easily and also keep modifications organized in one place.

This step takes a lot of time to complete. Keep in mind that Buildroot scripts need to download all of the related resources, including the Linux kernel, and then compile everything. The first time you run this command, it will take even longer than usual just because the cross-compiling toolchain needs to be built, so be prepared to wait to have your system image.

If something fails for any reason and you can’t understand why it’s a good practice to run make clean to remove any files from the previous compilation that might interfere with your new settings (Buildroot can be problematic when recompiling over and over).

Compilation flow (source)

From a high-level point of view, here is the workflow that Buildroot automates:

Buildroot compilation flow (source)
  1. Buildroot builds the toolchain, which consists of the cross-compilers and other tools it needs to compile the target system (green boxes).
  2. The source code (blue boxes) for each piece of software downloads from the internet.
  3. Using Buildroot scripts (gray boxes), the source is unpacked, patched, configured, compiled, and installed into the target output directory that forms the root filesystem (“rootfs”) for the target (purple boxes).
  4. Extra files, such as on-device configuration files, also copy into the target output directory.
  5. Finally, scripts assemble the final firmware image from the generated rootfs.

After a while, you should see something like this:

These lines indicate the SD card image (sdcard.img) for the Pi has generated. It built the image from the root filesystem in output/target/, which you can inspect:

On the output/image directory you may see the firmware files:

Looks good. Now, burn this image to the Pi’s SD card and start connecting the Pi to the computer.

Flashing the New Firmware

When Buildroot has completed the build process, the results are stored in the output/images directory. You will find the generated files in the output/images directory. The zImage file is the Linux kernel image that will load in the memory.

The sdcard.img file is the RFS (root file system) itself; you need to deflate this file in the SD card that will allocate your system. Also, the bootloader files generate inside the output/images/rpi-firmware directory; these files do not generate with the make command but download just as a binary format because of the closed nature of the GPU of Raspberry Pi.

The bootcode.bin file is the original bootloader that comes with Raspberry Pi.

The sdcard.img is the binary image you will flash to an SD memory card.

Use https://www.balena.io/etcher/ for the flashing process (supports Mac, Linux, and Windows), which is much easier than using the dd command.

Enabling UART output on the Raspberry Pi:

To enable the serial interface on the RPI board, add the following line to the config.txt file on /boot partition, so the file looks like this:

Mazel Tov! You’ve Built Your First Custom-made IoT Linux System

Save all changes, if you’re running on Linux, then type in the sync command to make sure all changes are flushed.

Eject the SD card safely, put it into the Raspberry Pi, make sure the UART cable is plugged in, and that using screen/putty you’ve connected to your COM or /dev/ttyS* port.
Turn on the board, and you should receive an output similar to the following:

Further reading

  1. How Linux Works by Brian Ward is a great book about many topics in Linux. It’s relevant for desktop and server Linux, as well as embedded. It covers basics such as shell commands yet still goes into important, complex topics like the X11 window system and the DBus messaging bus. It’s the right amount of detail to give you a good mental picture of how everything works, while still being approachable.
  2. The very prolific team at Bootlin, a French company that does embedded Linux development maintains Bootlin’s Buildroot training. You can pay the company to give your entire team training using this material. If you’re willing to read through its slide decks, it’s very thorough, although there’s no lecture accompanying it.
  3. Packpub book is useful for hands-on training.

The Buildroot user manual is the place to learn about hacking on Buildroot. The various areas of the build system are well-described and the reference manual for writing new packages is superb. The downside is that because it is a user manual, not a tutorial, it’s quite dense. You’ll definitely get familiar with it as you use Buildroot going forward.

Now that we have our system up and running, let’s see how we can further improve it and get more control, check out the last part.

--

--