Chapter 1
Chapter 1
[ 19 ]
Building the System Chapter 1
Bootloader
Linux cannot be started on an embedded device without a small amount of machine specific code
to initialize the system. Linux requires the bootloader code to do very little, although several
bootloaders do provide extensive additional functionality. The minimal requirements are:
• Configuration of the memory system.
• Loading of the kernel image and the Device Tree at the correct addresses.
• Optional loading of an initial RAM disk at the correct memory address.
• Setting of the kernel command-line and other parameters (e.g., Device Tree, machine type).
It is also usually expected that the bootloader initializes a serial console for the kernel in addition
to these basic tasks.
There are different bootloader options that come in all shapes and sizes. U-Boot is the standard
bootloader for ARM Linux. U-Boot source code is located at https://source.denx.de/u-boot/u-boot.
These are some of the main U-Boot features:
1. Small: U-Boot is a bootloader, and its primary purpose in the system is to load an
operating system. That means that U-Boot is necessary to perform a certain task, but it is
not worth spending significant resources on. Typically, U-Boot is stored in the relatively
small NOR flash memory, which is expensive compared to the much larger NAND
devices normally used to store the operating system and the application. A usable and
useful configuration of U-Boot, including a basic interactive command interpreter, support
for download over Ethernet and the capability to program the flash should fit in no more
than 128 KB.
2. Fast: The end user is not interested in running U-Boot. In most embedded systems, they
are not even aware that U-Boot exists. The user wants to run some application code, and
they want to do that as soon as possible after switching on the device. Initialize devices
only when they are needed within U-Boot, i.e., don't initialize the Ethernet interface(s)
unless U-Boot performs a download over Ethernet; don't initialize any IDE or USB devices
unless U-Boot actually tries to load files from these, etc.
3. Portable: U-Boot is a bootloader, but it is also a tool used for board bring-up, for
production testing and for other activities that are very closely related to hardware
development. So far, it has been ported to several hundreds of different boards on about
30 different processor families.
4. Configurable: U-Boot is a powerful tool with many, many extremely useful features.
The maintainer or user of each board will have to decide which features are important
[ 20 ]
Chapter 1 Building the System
and what shall be included with their specific board configuration to meet the current
requirements and restrictions.
5. Debuggable: U-Boot is not only a tool in itself, it is often also used for hardware bring-up,
so debugging U-Boot often means that you don't know if you are tracking down a problem
in the U-Boot software or in the hardware you are running on. Code that is clean and easy
to understand and debug is all the more important to everyone. One important feature of
U-Boot is to enable output to the (usually serial) console as soon as possible in the boot
process, even if this causes tradeoffs in other areas like memory footprint. All initialization
steps shall print some "begin doing this" message before they actually start and some
"done" message when they complete. The purpose of this is that you can always see which
initialization step was running if any problems occur. This is important not only during
software development, but also for the service people dealing with broken hardware in the
field. U-Boot should be debuggable with simple JTAG or BDM equipment. It should use a
simple, single-threaded execution model.
[ 21 ]
Building the System Chapter 1
Linux kernel
Linux is a clone of the operating system Unix, written from scratch by Linus Torvalds with
assistance from a loosely-knit team of hackers across the Net. It aims towards POSIX and Single
UNIX Specification compliance.
It has all the features you would expect in a modern, fully-fledged Unix implementation, including
true multitasking, virtual memory, shared libraries, demand loading, shared copy-on-write
executables, proper memory management and multistack networking including IPv4 and IPv6.
Although originally developed for 32-bit x86-based PCs (386 or higher), today Linux also runs on a
multitude of other processor architectures, in both 32-bit and 64-bit variants.
The Linux kernel is the lowest level of software running on a Linux system. It is charged with
managing the hardware, running user programs and maintaining the overall security and integrity
of the whole system. It is this kernel, which, after its initial release by Linus Torvalds in 1991,
jump-started the development of Linux as a whole. The kernel is a relatively small part of the
software on a full Linux system (many other large components come from the GNU project, the
GNOME and KDE desktop projects, the X.org project and many other sources), but the kernel is
the core which determines how well the system will work and is the piece which is truly unique to
Linux.
The kernel, which forms the core of the Linux system, is the result of one of the largest cooperative
software projects ever attempted. Regular two-to-three month releases deliver stable updates to
Linux users, each with significant new features, added device support and improved performance.
The rate of change in the kernel is high and increasing, with over 10,000 patches going into each
recent kernel release. Each of these releases contains the work of more than 1,600 developers
representing over 200 corporations.
As kernels move from the mainline into the stable category, two things can happen:
1. They can reach End of Life after a few bugfix revisions, which means that kernel
maintainers will release no more bugfixes for this kernel version, or
2. They can be put into longterm maintenance, which means that maintainers will provide
bugfixes for this kernel revision for a much longer period of time.
If the kernel version you are using is marked EOL, you should consider upgrading to the next
major version, as there will be no more bugfixes provided in the kernel version you are using.
Linux kernel is released under GNU GPL version 2 and is therefore Free Software as defined by
the Free Software Foundation. You may read the entire copy of the license in the COPYING file
distributed with each release of the Linux kernel.
[ 22 ]
Chapter 1 Building the System
[ 23 ]
Building the System Chapter 1
In addition to the official versions of the kernel, there are many third-parties (chip-vendors,
sub-communities) that supply and maintain their own version of the kernel sources by forking
from the official kernel tree. The intent is to separately develop support for a particular piece of
hardware or subsystem and to integrate this support to the official kernel at a later point. This
process is called mainlining and describes the task to integrate the new feature or hardware
support to the upstream (official) kernel. These are called Distribution kernels.
It is easy to tell if you are running a Distribution kernel. Unless you downloaded, compiled and
installed your own version of kernel from www.kernel.org, you are running a Distribution kernel. To
find out the version of your kernel, run uname -r after booting the processor.
[ 24 ]
Chapter 1 Building the System
You will work with the longterm kernel 5.4.y releases to develop all the drivers throughout this
book.
The C runtime library (C-standard library) defines macros, type definitions and functions for
string handling, mathematical functions, input/output processing, memory allocation and several
other functions that rely on OS services. The runtime library provides applications with access to
OS resources and functions by abstracting the OS System call interface.
Several C runtime libraries are available: glibc, uClibc, eglibc, dietlibc, newlib. The choice of the C
library must be made at the time of the cross-compiling toolchain generation, as the GCC compiler
is compiled against a specific C library.
The GNU C library, glibc, is the default C library used for example in the Yocto project. The
GNU C Library is primarily designed to be a portable and high performance C library. It follows
all relevant standards including ISO C11 and POSIX.1-2008. It is also internationalized and has
[ 25 ]
Building the System Chapter 1
one of the most complete internationalization interfaces known. You can find the glibc manual at
https://www.gnu.org/software/libc/manual/.
[ 26 ]
Chapter 1 Building the System
Root filesystem
The root filesystem is where all the files contained in the file hierarchy (including device nodes) are
stored. The root filesystem is mounted as /, containing all the libraries, applications and data.
The folder structure of the root filesystem is defined by FHS (Filesystem-Hierarchy-Standard). The
FHS defines the names, locations and permissions for many file types and directories. It thereby
ensures compatibility between different Linux distributions and allows applications to make
assumptions about where to find specific system files and configurations.
An embedded Linux root filesystem, usually includes the following:
• /bin: Commands needed during bootup that might be used by normal users (probably
after bootup).
[ 27 ]
Building the System Chapter 1
• /sbin: Like /bin, but the commands are not intended for normal users, although they may
use them if necessary and allowed; /sbin is not usually in the default path of normal users,
but will be in root's default path.
• /etc: Configuration files specific to the machine.
• /home: Like My Documents in Windows.
• /root: The home directory for user root. This is usually not accessible to other users on the
system.
• /lib: Essential shared libraries and kernel modules.
• /dev: Device files. These are special virtual files that help the user interface with the
various devices on the system.
• /tmp: Temporary files. As the name suggests, programs running often store temporary files
in here.
• /boot: Files used by the bootstrap loader. Kernel images are often kept here instead of in
the root directory. If there are many kernel images, the directory can easily grow too large,
and it might be better to keep it in a separate filesystem.
• /mnt: Mount point for mounting a filesystem temporarily.
• /opt: Add-on application software packages.
• /usr: Secondary hierarchy.
• /var: Variable data.
• /sys: Exports information about devices and drivers from the kernel device model to user
space, and it is also used for configuration.
• /proc: Represent the current state of the kernel.
[ 28 ]
Chapter 1 Building the System
4. The kernel runs low level kernel initialization, enabling MMU, creating the initial table of
memory pages and setting up caches. This is done in arch/arm/kernel/head.s. The file head.s
contains CPU architecture specific, but platform independent initialization code. Then, the
system switches to the non architecture specific kernel startup function start_kernel().
5. The kernel runs start_kernel() located in init/main.c that:
• Initializes the kernel core (e.g., memory, scheduling, interrupts).
• Initializes statically compiled drivers.
• Mounts the root filesystem based on bootargs passed to the kernel from U-Boot.
• Executes the first user process, init. The init process, by default, is /init for initramfs
and /sbin/init for a regular filesystem. The three init programs that you usually find on
embedded Linux devices are BusyBox init, System V init and systemd.
[ 29 ]
Building the System Chapter 1
Raspberry Pi OS
Raspberry Pi OS is the recommended operating system for normal use on a Raspberry Pi. Raspberry
Pi OS is a free operating system based on Debian, optimised for the Raspberry Pi hardware.
Raspberry Pi OS comes with over 35,000 packages: precompiled software bundled in a nice format
for easy installation on your Raspberry Pi. Raspberry Pi OS is a community project under active
development, with an emphasis on improving the stability and performance of as many Debian
packages as possible.
You will install on a Micro SD a Raspberry Pi OS image based on kernel 5.4.y. Go to https://
downloads.raspberrypi.org/raspios_full_armhf/images/raspios_full_armhf-2020-12-04/ and download the
2020-12-02-raspios-buster-armhf-full.zip image.
To write the compressed image on the Micro SD card, you will download and install Etcher. This
tool, which is an Open Source software, is useful since it allows to get a compressed image as
input. More information and extra help is available on the Etcher website at https://etcher.io/.
Once the image is installed on the Micro SD card, you will insert the Micro SD into the SD card
reader on your host PC, then you will enable UART, SPI and I2C peripherals in the programmed
Micro SD. Open a terminal application on your host PC, and type the following commands:
~$ lsblk
~$ mkdir ~/mnt
~$ mkdir ~/mnt/fat32
~$ mkdir ~/mnt/ext4
~$ sudo mount /dev/mmcblk0p1 ~/mnt/fat32
See the files in the fat32 partition. Check that config.txt is included:
~$ ls -l ~/mnt/fat32/
[ 30 ]
Chapter 1 Building the System
You can also update previous settings (after booting the Raspberry Pi board) through the
Raspberry Pi Configuration application found in Preferences on the menu.
The Interfaces tab is where you turn these different connections on or off so that the Pi recognizes
that you’ve linked something to it via a particular type of connection.
[ 31 ]
Building the System Chapter 1
[ 32 ]
Chapter 1 Building the System
2. Connect your screen to the single Raspberry Pi’s HDMI port. You can also connect a
mouse to a USB port and keyboard in the same way.
3. Connect the Ethernet port on Raspberry Pi to an Ethernet socket on your host PC.
[ 33 ]
Building the System Chapter 1
4. The serial console is a helpful tool for debugging your board and reviewing system log
information. To access the serial console, connect a USB to TTL Serial Cable to the device
UART pins as shown below.
5. Plug the USB power supply into a socket and connect it to your Raspberry Pi’s power port.
[ 34 ]
Chapter 1 Building the System
You should see a red LED light up, which indicates that the Raspberry Pi board is connected to
power. As it starts up, you will see raspberries appear in the top left-hand corner of your screen.
After a few seconds the Raspberry Pi OS Desktop will appear.
For the official Raspberry Pi OS, the default user name is pi, with password raspberry.
To find out the version of the booted kernel, run uname -r on the Raspberry Pi terminal:
pi@raspberrypi:~$ uname -r
5.4.79-v7+
Reset the board. You can disconnect your screen from the Raspberry Pi’s HDMI port during the
development of the labs (you will only need to connect a screen in the LAB 10.3).
pi@raspberrypi:~$ sudo reboot
To see Linux boot messages on the console, add loglevel=8 in the file cmdline.txt under /boot.
pi@raspberrypi:~$ sudo nano /boot/cmdline.txt
For example:
pi@raspberrypi:~$ echo 8 > /proc/sys/kernel/printk
Now, every kernel message will appear on your console, as all priority higher than 8 (lower loglevel
values) will be displayed. Please note that after reboot, this configuration is reset. To keep the
[ 35 ]
Building the System Chapter 1
configuration permanently, just append the following kernel.printk value to the /etc/sysctl.conf file,
then reboot the processor:
kernel.printk = 8 4 1 3
pi@raspberrypi:~$ sudo nano /etc/sysctl.conf
pi@raspberrypi:~$ sudo reboot
Raspbian has the SSH server disabled by default. You have to start the service on the Pi:
pi@raspberrypi:~# sudo /etc/init.d/ssh restart
Now, verify that you can ping your Linux host machine from the Raspberry Pi. Exit the ping
command by typing "Ctrl-c":
pi@raspberrypi:~# ping 10.0.0.1
You can also ping from Linux host machine to the target. Exit the ping command by typing
"Ctrl-c".
~$ ping 10.0.0.10
[ 36 ]
Chapter 1 Building the System
By default, the root account is disabled, but you can enable it by using this command and giving it
a password, for example "pi":
pi@raspberrypi:~$ sudo passwd root
Now, you can log into your pi as the root user. Open the sshd_config file, and change
PermitRootLogin to yes (also comment the line out). After editing the file, type "Ctrl+x", then type
"yes", and press "enter" to exit:
pi@raspberrypi:~$ sudo nano /etc/ssh/sshd_config
On your host PC, create the linux_rpi3 folder, where you are going to download the kernel sources:
~$ mkdir linux_rpi3
~$ cd linux_rpi3/
Get the kernel sources. The git clone command below will download the current active branch
without any history. Omitting the --depth=1 will download the entire repository, including the full
history of all branches, but this takes much longer and occupies much more storage.
~/linux_rpi3$ git clone --depth=1 -b rpi-5.4.y https://github.com/raspberrypi/linux
Compile the kernel, modules and Device Tree files. First, apply the default configuration:
~/linux_rpi3/linux$ KERNEL=kernel7
~/linux_rpi3/linux$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- bcm2709_defconfig
Configure the following kernel settings that will be needed during the development of the labs:
~/linux_rpi3/linux$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
Device drivers >
[*] SPI support --->
<*> User mode SPI device driver support
[ 37 ]
Building the System Chapter 1
For the LAB 12.1, you will need the functions that enable the triggered buffer support. If they are
not defined accidentally by another driver, there's an error thrown out while linking. To solve
this problem, you can select, for example, the HTS221 driver, which includes this triggered buffer
support:
Device drivers >
<*> Industrial I/O support > Humidity sensors --->
<*> STMicroelectronics HTS221 sensor Driver
Having built the kernel, you need to copy it onto your Raspberry Pi device and also install the
modules. Insert the previously programmed Micro SD into the SD card reader on your host PC,
and execute the following commands:
~$ lsblk
~$ mkdir ~/mnt
~$ mkdir ~/mnt/fat32
~$ mkdir ~/mnt/ext4
~$ sudo mount /dev/mmcblk0p1 ~/mnt/fat32
~$ sudo mount /dev/mmcblk0p2 ~/mnt/ext4/
~$ cd linux_rpi3/linux
~/linux_rpi3/linux$ sudo env PATH=$PATH make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
INSTALL_MOD_PATH=~/mnt/ext4 modules_install
[ 38 ]
Chapter 1 Building the System
Insert the Micro SD card you’ve set up in the Micro SD card slot on the underside of your
Raspberry Pi, and connect the Raspberry Pi´s UART (through a USB to serial adapter) to a Linux
PC's USB. On Linux PC, launch Minicom utility as shown below (for debugging purpose):
~$ sudo minicom –s
If you modify and compile the kernel or Device Tree files later, you can copy them to the
Raspberry Pi remotely using the secure copy protocol (SCP). You need to connect previously an
Ethernet cable between the Raspberry Pi board and your host PC.
~/linux_rpi3/linux$ scp arch/arm/boot/zImage [email protected]:/boot/kernel7.img
Copy the following .dtb file if you are using the Raspberry Pi 3 Model B board:
~/linux_rpi3/linux$ scp arch/arm/boot/dts/bcm2710-rpi-3-b.dtb [email protected]:/boot/
Copy the following .dtb file if you are using the Raspberry Pi 3 Model B+ board:
~/linux_rpi3/linux$ scp arch/arm/boot/dts/bcm2710-rpi-3-b-plus.dtb [email protected]:/boot/
[ 39 ]