EmbeddedLinux -BootLoader

This is a three part series of blogs which explains the complete procedure to cross compile

1. Bootloader.
2. Kernel/O.S.
3. File system.
This will be done for ARM processor based development platform.

In short this blog series explains how to setup an embedded linux machine that suits your needs.

Development environment prerequisites

1. Linux machine running any flavor of ubuntu, fedora or arch linux.
2. Internet connection.

Hardware needed

1. ARM based development board.
a. This is very important as the build process and the cross compiler we choose depends on the type of processor. For this blog series we are using Banana Pro development  platform which is based on ARMv7 architecture.
2. 4/8 GB Micro SD Card.
3. USB to Serial adaptor.

What is Bootloader

There are so many answers to this question, but if you look at the core of all the answers it would contain some kind of initialization. In short this is the piece of software which is executed as soon as you turn on your hardware device. The hardware device can be anything, from your mobile phones, routers, microwave ovens, smart tv, and to the world’s fastest supercomputer. After all everything has a beginning right.

The reason I said there are so many ways to answer this question, is because the use case of each device is different, and we need to choose the bootloader carefully, which initializes the device. So much research and decision making time is spent on this to make sure that the devices which are initialized are absolutely needed. Everyone like their devices to boot up fast.

In embedded systems the bootloader is a special piece of software whose main purpose is to load the kernel and hand over the control to it. To achieve this it needs to initialize the needed peripherals which helps the device to carry out its intended functionality. In other words, it initializes the absolutely needed peripherals alone and hands over the control to the O.S aka kernel.

Das U-Boot — the Universal Boot Loader

U-Boot is the most popular boot loader in linux based embedded devices. It is released as open source under the GNU GPLv2 license. It supports a wide range of micro processors like MIPS, ARM, PPC, Blackfin, AVR32 and x86. It even supports FPGA based nios platforms. If your hardware design is based out of any of these processors and if you are looking for a bootloader the best bet is to try U-Boot first. It also supports different methods of booting which is pretty much needed on fallback situations.

For example, it has support to boot from USB, SD Card, NOR and NAND flash (non volatile memory). It also has support to boot linux kernel from the network using TFTP. The list of filesystems supported by U-Boot is huge. So you are covered in all aspects that is needed from a bootloader and more so.

Last but not least, it has a command line interface which gives you a very easy access to it and try many different things before finalizing your design. You can configure U-Boot for various boot methods like MMC, USB, NFS or NAND based and it allows you to do test the physical RAM of any issues. Now its upto the designer to pick what device he wants and then use U-Boot to his advantage.

Stages in boot loading

For starters, U-Boot is both a first stage and second stage bootloader. When U-Boot is compiled we get two images, first stage (u-boot-sunxi-with-spl.bin) and second stage (u-boot.img) images. It is loaded by the system’s ROM code (this code resides inside the SoC and it is already preprogrammed ) from a supported boot device. The ROM code checks for the various bootable devices that is available. And starts execution from the device which is capable of booting. This can be controlled through jumpers or through some pull up/pull down resistors. Since each platform is different and it is advised to look into the platforms datasheet for more details.

Stage 1 bootloader is sometimes called a small SPL (Secondary Program Loader). SPL would do initial hardware configuration and load the rest of U-Boot i.e second stage loader. Regardless of whether the SPL is used, U-Boot performs both first-stage and second-stage booting.

In first stage, U-Boot initializes the memory controller and SDRAM (DDR clones). This is needed as rest of the code execution depends on this. Depending upon the list of devices supported by the platform it initializes the rest. For example, If your platform has capability to boot through USB and there is no support for network connectivity then U-Boot can be programmed to do exactly the same.

If you are planning to use linux kernel then setting up of the memory controller is the only mandatory thing expected by linux kernel. If memory controller is not initialized properly then linux kernel won’t be able to boot.

Hardware Screenshot

20161230_084909.jpg

The above diagram shows the serial port header details of that target. You should connect your pinouts from USB to TTL Serial Cable (if you are using one) to these pins in the target to see the output log using any tool like minicom.

Downloading the source

U-Boot source code is maintained using git revision control. Using git we can clone the latest source code from the repo.

Vostro git # git clone git://git.denx.de/u-boot.git

Brief about the directories and the functionality it provides

arch –> Contains architecture specific code. This is the piece of code which initializes the CPU and board specific peripheral devices.

board → Source in both arch and board directory work in tandem to initialize the memory and other devices.

cmd –> Contains code which adds command line support to carry out different activity depending on the developer’s requirement. For example, command line utilities are provided to erase NAND flash and reprogram it. We will be using similar commands in the next blog.

configs –> Contains the platform level configuration details. This is very much platform specific. The configs are much like a static mapping with reference to the platform’s datasheet.

drivers –> This directory needs a special mention as it has support for a lot of devices:
Each sub directory under the driver directory corresponds to a particular device type. This structure is followed in accordance with the linux kernel. For example, network drivers are all accumulated inside the net directory. This makes sure the code is not bloated and it is much easier for us to navigate and make the needed changes.

fs –> Contains the code which adds support for filesystem’s. As said before U-Boot has rich filesystem support. It supports both read only file system like cramfs and also journalling file system like jffs2 which is used on NAND flash based devices.

include –> This is a very important directory in U-Boot. It not only contains the header files but also the files which define platform specific information like supported baud rates, starting RAM address, stack size, default command line arguments and etc.

lib –> Contains support for library files They provide the needed helper functions used by U-Boot.

net –> Contains support for networking protocols like ARP, TFTP, Ping, Bootp and etc.

scripts and tools –> Contains helper scripts to create images and binaries. It contains scripts to create a patch file (hopefully with some useful fixes) in the correct format if we are planning to send it the development community.

With the source code available and some understanding about the directory structure let us do what we actually want to do i.e create a bootloader

Since the target board which we are using is based on ARM processor we will need a cross compiler which helps us to create binaries to run on that processor. There are a lot of options for this.

Cross compiling bootloader for ARM based target platform

For cross compiling we can need a toolchain which helps us to compile binaries that can be executed on the target platform. The toolchain which is being used was downloaded from codesourcery (quite old) website. The tar file which was downloaded is:

arm-2013.05-24-arm-none-linux-gnueabi-i686-pc-linux-gnu.tar.bz2

As seen the toolchain comes compressed as tar file and needs to be unzipped using the below command:

Vostro u-boot # cd /Sourcefiles/crosstool/

Vostro crosstool # tar xf gcc-linaro-6.1.1-2016.08-x86_64_arm-linux-gnueabihf.tar.xz

Pls checkout other toolchains also especially the ones provided by linaro. Linaro provides the latest cross tool for ARM based processors and it is very easily available for downloads.

Setup the environment variables

With the pre build tool chain, we need to set up a few environment variables like path of the toolchain before proceeding to compile U-Boot. Below are the shell commands that we need to issue.

export PATH=<UNZIPPED_TOOLCHAIN_LOCATION>/arm-2013.05/bin:$PATH;
export CROSS_COMPILE=arm-none-linux-gnueabi-
export ARCH=arm;

In the work space the initial path points to “/Sourcefiles/crosstool/” as this is the directory where the toolchain has been unzipped.

The exact command from our machine is :

Vostro u-boot # export PATH=/Sourcefiles/crosstool/arm-2013.05/bin:$PATH
Vostro u-boot # export CROSS_COMPILE=arm-none-linux-gnueabi-
Vostro u-boot # export ARCH=arm
Vostro u-boot #

Pls double check the above commands so that it suits your workspace.

Config File

With everything setup its time to choose the proper config file and start the compilation. The board which is being used is Banana Pro which is based on Allwinner’s A20 SoC. So look for similar kind of name in configs directory. The file which corresponds to this board is “Bananapro_defconfig

From command line execute the below command:

Vostro u-boot # make Bananapro_defconfig
HOSTCC scripts/basic/fixdep
HOSTCC scripts/kconfig/conf.o
SHIPPED scripts/kconfig/zconf.tab.c
SHIPPED scripts/kconfig/zconf.lex.c
SHIPPED scripts/kconfig/zconf.hash.c
HOSTCC scripts/kconfig/zconf.tab.o
HOSTLD scripts/kconfig/conf
#
# configuration written to .config
Vostro u-boot #

There are a lot of things that happened in the background when the above command was executed. As of now i doesn’t want to do go much deeper into that as it will itself take a separate blog to explain.

The .config file has been which is used by U-Boot in the build process. For those who want to know more, pls open the “.config” file and check it. Modifications can be done here to the config file directly but let us discuss about this later.

Start the build

To start the build give the most used/abused command in the embedded linux programmer’s life which is make.

Vostro u-boot # make -j 8
scripts/kconfig/conf –silentoldconfig Kconfig
CHK include/config.h
UPD include/config.h
CC examples/standalone/hello_world.o
LD examples/standalone/hello_world
OBJCOPY examples/standalone/hello_world.srec
OBJCOPY examples/standalone/hello_world.bin
LDS u-boot.lds
LD u-boot
OBJCOPY u-boot-nodtb.bin
./scripts/dtc-version.sh: line 17: dtc: command not found
./scripts/dtc-version.sh: line 18: dtc: command not found
*** Your dtc is too old, please upgrade to dtc 1.4 or newer
Makefile:1383: recipe for target ‘checkdtc’ failed
make: *** [checkdtc] Error 1
Vostro u-boot #

If you are compiling U-Boot for the first time then there are chances that you may get the above error. Since the build machine which is being used didn’t have the device-tree-compiler package installed, we got the above error.

Dependency installation (if any)

Vostro u-boot # apt-cache search dtc
device-tree-compiler – Device Tree Compiler for Flat Device Trees
Vostro u-boot # apt install device-tree-compiler

After the installation we need to give make again to continue the compilation.

Again make

Vostro u-boot # make

LD spl/drivers/i2c/built-in.o
LD spl/lib/built-in.o
LD spl/drivers/serial/built-in.o
LD spl/drivers/mmc/built-in.o
LD spl/drivers/built-in.o
LD spl/u-boot-spl
OBJCOPY spl/u-boot-spl-nodtb.bin
COPY spl/u-boot-spl.bin
MKSUNXI spl/sunxi-spl.bin
CAT u-boot-sunxi-with-spl.bin
./scripts/check-config.sh u-boot.cfg \
./scripts/config_whitelist.txt . 1>&2
Vostro u-boot #

Simple ls -l will show the first stage bootloader and second stage bootloader.

Vostro u-boot # ls -l
total 38984
drwxr-xr-x 2 root root 4096 Nov 24 18:58 api
drwxr-xr-x 18 root root 4096 Nov 24 18:58 arch
drwxr-xr-x 221 root root 4096 Nov 24 18:58 board
drwxr-xr-x 3 root root 12288 Nov 24 19:04 cmd
drwxr-xr-x 5 root root 12288 Nov 24 19:04 common
-rw-r–r– 1 root root 2260 Nov 24 18:58 config.mk
drwxr-xr-x 2 root root 61440 Nov 24 18:58 configs
drwxr-xr-x 2 root root 4096 Nov 24 19:04 disk
drwxr-xr-x 8 root root 12288 Nov 24 18:58 doc
drwxr-xr-x 51 root root 4096 Nov 24 19:04 drivers
drwxr-xr-x 2 root root 4096 Nov 24 19:04 dts
drwxr-xr-x 4 root root 4096 Nov 24 18:58 examples
drwxr-xr-x 12 root root 4096 Nov 24 19:04 fs
drwxr-xr-x 29 root root 16384 Nov 24 19:04 include
-rw-r–r– 1 root root 1863 Nov 24 18:58 Kbuild
-rw-r–r– 1 root root 12443 Nov 24 18:58 Kconfig
drwxr-xr-x 12 root root 4096 Nov 24 19:04 lib
drwxr-xr-x 2 root root 4096 Nov 24 18:58 Licenses
-rw-r–r– 1 root root 11796 Nov 24 18:58 MAINTAINERS
-rw-r–r– 1 root root 54561 Nov 24 18:58 Makefile
drwxr-xr-x 2 root root 4096 Nov 24 19:04 net
drwxr-xr-x 6 root root 4096 Nov 24 18:58 post
-rw-r–r– 1 root root 223795 Nov 24 18:58 README
drwxr-xr-x 5 root root 4096 Nov 24 18:58 scripts
-rw-r–r– 1 root root 17 Nov 24 18:58 snapshot.commit
drwxr-xr-x 12 root root 4096 Nov 24 19:04 spl
-rw-r–r– 1 root root 63465 Nov 24 19:04 System.map
-rwxr-xr-x 1 root root 32094130 Aug 18 10:46 tags
drwxr-xr-x 10 root root 4096 Nov 24 19:04 test
drwxr-xr-x 15 root root 4096 Nov 24 19:04 tools
-rwxr-xr-x 1 root root 2639690 Nov 24 19:04 u-boot
-rw-r–r– 1 root root 463707 Nov 24 19:04 u-boot.bin
-rw-r–r– 1 root root 11334 Nov 24 19:04 u-boot.cfg
-rw-r–r– 1 root root 6869 Nov 24 19:04 u-boot.cfg.configs
-rw-r–r– 1 root root 34547 Nov 24 19:04 u-boot.dtb
-rw-r–r– 1 root root 463707 Nov 24 19:04 u-boot-dtb.bin
-rw-r–r– 1 root root 463771 Nov 24 19:04 u-boot-dtb.img
-rw-r–r– 1 root root 463771 Nov 24 19:04 u-boot.img
-rw-r–r– 1 root root 2476 Nov 24 19:04 u-boot.lds
-rw-r–r– 1 root root 413116 Nov 24 19:04 u-boot.map
-rw-r–r– 1 root root 429160 Nov 24 19:04 u-boot-nodtb.bin
-rw-r–r– 1 root root 1287618 Nov 24 19:04 u-boot.srec
-rw-r–r– 1 root root 496539 Nov 24 19:04 u-boot-sunxi-with-spl.bin
-rw-r–r– 1 root root 127201 Nov 24 19:04 u-boot.sym

u-boot-sunxi-with-spl.bin is the first stage bootloader and u-boot.img is the second stage bootloader.

With the bootloader available it’s time to partition the Micro SD card, load these images and test it on the target.

Partition

We are using an 16GB Micro SD card and using “gparted” (gui based partition tool) to partition it. It is a much easier approach to use gparted and create the filesystems. We have created two partitions:
1. FAT16 of size 50MB.
2. EXT4 of size more than 3GB.

Choosing the size of the partition is an availability as well as a personal choice. Pls see the image below to get a clear picture of the partitions in the Micro SD card.

bananapropng.png

After the creating the partitions in the Micro SD card, remove the card from the build machine and insert it again. In most of the modern distro’s partitions in the Micro SD card get auto mounted which will confirm us that the partitions are created correctly.It will help us to cross verify the created partitions.

Copy the images:

Now it’s time to copy the builded images into the Micro SD card. When the Micro SD card is inserted into the build machine it was automatically mounted since we have formatted properly.

To copy the stage one boot loader issue the below command on your terminal:

Vostro u-boot # dd if=u-boot-sunxi-with-spl.bin of=/dev/mmcblk0 bs=1024 seek=8

Now copy u-boot.img (second stage bootloader) file into the fat16 partition of the micro SD card.

Vostro u-boot # cp u-boot.img /<mounted fat16 partition>

With the above commands we have loaded the first stage bootloader as well as the second stage bootloader into the bootable Micro SD card.

Output Log

Below is the output from the serial port while loading U-Boot which was compiled by us.

##########################
#### Lets get Started ####
##########################

U-Boot SPL 2016.11-00138-g136179b-dirty (Dec 05 2016 – 21:27:56)
DRAM: 1024 MiB
CPU: 912000000Hz, AXI/AHB/APB: 3/2/2
Trying to boot from MMC1
U-Boot 2016.11-00138-g136179b-dirty (Dec 05 2016 – 21:27:56 +0530) Allwinner Technology

CPU: Allwinner A20 (SUN7I)
Model: LeMaker Banana Pro
I2C: ready
DRAM: 1 GiB
MMC: SUNXI SD/MMC: 0
*** Warning – bad CRC, using default environment

Setting up a 720x576i composite-pal console (overscan 32×20)
In: serial
Out: vga
Err: vga
SCSI: SATA link 0 timeout.
AHCI 0001.0100 32 slots 1 ports 3 Gbps 0x1 impl SATA mode
flags: ncq stag pm led clo only pmp pio slum part ccc apst
Net: eth0: ethernet@01c50000
starting USB…
USB0: USB EHCI 1.00
USB1: USB OHCI 1.0
USB2: USB EHCI 1.00
USB3: USB OHCI 1.0
scanning bus 0 for devices… 1 USB Device(s) found
scanning bus 2 for devices… 1 USB Device(s) found
Hit any key to stop autoboot: 0
=>
=>
=>
=>

As you can clearly see this is the U-Boot which we compiled and loaded into the target (Check for string Lets get started in the log,  first line of the output).

First stage bootloader checks and loads the u-boot.img into RAM and hands over the control to it which is the second stage bootloader. As shared before U-Boot also provides us with a cli which can be used to set up various parameters like ip address, load addresses and a lot more which the developer can use for tweaking and testing purposes.

To the second stage bootloader we need to provide proper kernel image to load and proceed with the next step of bootstrapping. We shall discuss about this in next blog.

Advertisements