Book Image

Linux: Embedded Development

By : Alexandru Vaduva, Alex Gonzalez, Chris Simmonds
Book Image

Linux: Embedded Development

By: Alexandru Vaduva, Alex Gonzalez, Chris Simmonds

Overview of this book

Embedded Linux is a complete Linux distribution employed to operate embedded devices such as smartphones, tablets, PDAs, set-top boxes, and many more. An example of an embedded Linux distribution is Android, developed by Google. This learning path starts with the module Learning Embedded Linux Using the Yocto Project. It introduces embedded Linux software and hardware architecture and presents information about the bootloader. You will go through Linux kernel features and source code and get an overview of the Yocto Project components available. The next module Embedded Linux Projects Using Yocto Project Cookbook takes you through the installation of a professional embedded Yocto setup, then advises you on best practices. Finally, it explains how to quickly get hands-on with the Freescale ARM ecosystem and community layer using the affordable and open source Wandboard embedded board. Moving ahead, the final module Mastering Embedded Linux Programming takes you through the product cycle and gives you an in-depth description of the components and options that are available at each stage. You will see how functions are split between processes and the usage of POSIX threads. By the end of this learning path, your capabilities will be enhanced to create robust and versatile embedded projects. This Learning Path combines some of the best that Packt has to offer in one complete, curated package. It includes content from the following Packt products: ? Learning Embedded Linux Using the Yocto Project by Alexandru Vaduva ? Embedded Linux Projects Using Yocto Project Cookbook by Alex González ? Mastering Embedded Linux Programming by Chris Simmonds
Table of Contents (6 chapters)

In this chapter, you will be presented with one of the most important components necessary for using a Linux system in an embedded environment. Here, I am referring to the bootloader, a piece of software that offers the possibility of initializing a platform and making it ready to boot a Linux operating system. In this chapter, the benefits and roles of bootloaders will be presented. This chapter mainly focuses on the U-Boot bootloaders, but readers are encouraged to have a look at others, such as Barebox, RedBoot, and so on. All these bootloaders have their respective features and there isn't one in particular that suits every need; therefore, experimentation and curiosity are welcome when this chapter. You have already been introduced to the the Yocto Project reference in the last chapter; hence, you will now be able to understand how this development environment works with various bootloaders, and especially the ones available inside a Board Support Package (BSP).

The main purpose of this chapter is to present the main properties of embedded bootloaders and firmware, their booting mechanisms, and the problems that appear when firmware is updated or modified. We will also discuss the problems related to safety, installation, or fault tolerance. With regard to bootloader and firmware notions, we have multiple definitions available and a number of them refer to traditional desktop systems, which we are not interested in.

A firmware usually represents a fixed and small program that is used on a system to control hardware. It performs low-level operations and is usually stored on flash, ROM, EPROM, and so on. It is not changed very often. Since there have been situations where this term has confused people and was sometimes used only to define hardware devices or represent data and its instructions, it was avoided altogether. It represents a combination of the two: computer data and information, along with the hardware device combined in a read-only piece of software available on the device.

The bootloader represents the piece of software that is first executed during system initialization. It is used to load, decompress, and execute one or more binary applications, such as a Linux kernel or a root filesystem. Its role involves adding the system in a state where it can execute its primary functions. This is done after loading and starting the correct binary applications that it receives or has already saved on the internal memory. Upon initializing, the hardware bootloader may need to initialize the phase-locked loop (PLL), set the clocks, or enable access to the RAM memory and other peripherals. However, these initializations are done on a basic level; the rest are done by kernels drivers and other applications.

Today, a number of bootloaders are available. Due to limited space available for this topic, and also the fact that their number is high, we will only discuss the most popular ones. U-Boot is one of the most popular bootloaders available for architectures, such as PowerPC, ARM, MIPS, and others. It will constitute the primary focus of this chapter.

The first time that electricity runs into a development board processor, a great number of hardware components need to be prepared before running a program. For each architecture, hardware manufacturer, and even processor, this initialization process is different. In most cases, it involves a set of configurations and actions are different for a variety of processors and ends up fetching the bootstrap code from a storage device available in the proximity of the processor. This storage device is usually a flash memory and the bootstrap code is the first stage of the bootloader, and the one that initializes the processor and relevant hardware peripherals.

The majority of the available processors when power is applied to them go to a default address location, and after finding the first bytes of binary data, start executing them. Based on this information, the hardware designers define the layout for the flash memory and the address ranges that could later be used to load and boot the Linux operating system from predictable addresses.

In the first stage of initialization, the board init is done, usually in the assembler language specific to the processor and after this is finished, the entire ecosystem is prepared for the operating system booting process. The bootloader is responsible for this; it is the component that offers the possibility to load, locate, and execute primary components of the operating system. Additionally, it can contain other advanced features, such as the capability to upgrade the OS image, validate an OS image, choose between several OS images, and even the possibility to upgrade itself. The difference between the traditional PC BIOS and an embedded bootloader is the fact that in an embedded environment, the bootloader is overwritten after the Linux kernel starts execution. It, in fact, ceases to exist after it offers control to the OS image.

Bootloaders need to carefully initialize peripherals, such as flash or DRAM, before they are used. This is not an easy task to do. For example, the DRAM chips cannot be read or written in a direct method - each chip has a controller that needs to be enabled for read and write operations. At the same time, the DRAM needs to be continually refreshed because the data will be lost otherwise. The refresh operation, in fact, represents the reading of each DRAM location within the time frame mentioned by the hardware manufacturer. All these operations are the DRAM controller's responsibility, and it can generate a lot of frustration for the embedded developer because it requires specific knowledge about the architecture design and DRAM chip.

A bootloader does not have the infrastructure that a normal application has. It does not have the possibility to only be called by its name and start executing. After being switched on when it gains control, it creates its own context by initializing the processor and necessary hardware, such as DRAM, moves itself in the DRAM for faster execution, if necessary and finally, starts the actual execution of code.

The first element that poses as a complexity is the compatibility of the start up code with the processor's boot sequence. The first executable instructions need to be at a predefined location in the flash memory, which is dependent of the processor and even hardware architecture. There is also the possibility for a number of processors to seek for those first executable instructions in several locations based on the hardware signals that are received.

Another possibility is to have the same structure on many of the newly available development boards, such as the Atmel SAMA5D3-Xplained:

The role of the bootloader

For the Atmel SAMA5D3-Xplained board and others similar to it, the booting starts from an integrated boot code available in the ROM memory called BootROM on AT91 CPUs, which loads the first stage bootloader called AT91Bootstrap on SRAM and starts it. The first stage bootloader initializes the DRAM memory and starts the second stage bootloader, which is U-Boot in this case. More information on boot sequence possibilities can be found in the boot sequence header available, which you'll read about shortly.

The lack of an execution context represents another complexity. Having to write even a simple "Hello World" in a system without a memory and, therefore, without a stack on which to allocate information, would look very different from the well-known "Hello World" example. This is the reason why the bootloader initializes the RAM memory to have a stack available and is able to run higher-level programs or languages, such as C.

As mentioned previously, the bootloader is the component that is first run after initializing the system, and prepares the entire ecosystem for the operating system boot process. This process differs from one architecture to the other. For example, for the x86 architecture, the processor has access to BIOS, a piece of software available in a nonvolatile memory, which is usually a ROM. Its role starts out after resetting the system when it is executed and initializes the hardware components that will later be used by the first stage bootloader. It also executes the first stage of the bootloader.

The first stage bootloader is very small in terms of dimensions - in general, it is only 512 bytes and resides on a volatile memory. It performs the initialization for the full bootloader during the second stage. The second stage bootloaders usually reside next to the first stage ones, they contain the most number of features and do most of the work. They also know how to interpret various filesystem formats, mostly because the kernel is loaded from a filesystem.

For x86 processors, there are more bootloader solutions that are available:

For most embedded systems, this booting process does not apply, although there are some that replicate this behavior. There are two types of situations that will be presented next. The first one is a situation where the code execution starts from a fixed address location, and the second one refers to a situation where the CPU has a code available in the ROM memory that is called.

Delving into the bootloader cycle

The right-hand side of the image is presented as the previously mentioned booting mechanism. In this case, the hardware requires a NOR flash memory chip, available at the start address to assure the start of the code execution.

A NOR memory is preferred over the NAND one because it allows random address access. It is the place where the first stage bootloader is programmed to start the execution, and this doesn't make it the most practical mechanism of booting.

Although it is not the most practical method used for the bootloader boot process, it is still available. However, it somehow becomes usable only on boards that are not suitable for more potent booting options.

There are many open source bootloaders available today. Almost all of them have features to load and execute a program, which usually involves the operating system, and its features are used for serial interface communication. However, not all of them have the possibility to communicate over Ethernet or update themselves. Another important factor is represented by the widespread use of the bootloader. It is very common for organizations and companies to choose only one bootloader for the diversity of boards, processors, and architectures that they support. A similar thing happened with the Yocto Project when a bootloader was chosen to represent the official supported bootloader. They, and other similar companies, chose U-Boot bootloader, which is quite well known in the Linux community.

The U-Boot bootloader, or Das U-Boot as its official name, is developed and maintained by Wolfgang Denx with the support of the community behind it. It is licensed under GPLv2, its source code is freely available inside a git repository, as shown in the first chapter, and it has a two month intervals between releases. The release version name is shown as U-boot vYYYY.MM. The information about U-Boot loader is available at http://www.denx.de/wiki/U-Boot/ReleaseCycle.

The U-Boot source code has a very well defined directory structure. This can be easily seen with this console command:

tree -d -L 1
.
├── api
├── arch
├── board
├── common
├── configs
├── disk
├── doc
├── drivers
├── dts
├── examples
├── fs
├── include
├── lib
├── Licenses
├── net
├── post
├── scripts
├── test
└── tools
19 directories

The arch directory contains architecture-specific files and directories-specific to each architecture, CPU or development board. An api contains external applications that are independent of a machine or architecture type. A board contains inside boards with specific names of directories for all board-specific files. A common is a place where misc functions are located. A disk contains disk drive handling functions, and documentation is available inside the doc directory. Drivers are available in the drivers directory. The filesystem-specific functionality is available inside the fs directory. There are still some directories that would need mentioning here, such as the include directory, which contains the header files; the lib directory contains generic libraries with support for various utilities, such as the flatten device tree, various decompressions, a post (Power On Self-Test) and others, but I will let them be discovered by the reader's curiosity, one small hint would be to inspect the README file in the Directory Hierachy section.

Moving through the U-Boot sources, which were downloaded in the previous chapter inside the ./include/configs file, configuration files can be found for each supported board. These configuration file is an .h file that contains a number of CONFIG_ files and defines information on memory mapping, peripherals and their setup, command line output, such as the boot default addresses used for booting a Linux system, and so on. More information on the configuration files could be found inside the README file in the Configuration Options, section or in a board specific configuration file. For Atmel SAMA5D3-Xplained, the configuration file is include/configs/sama5d3_xplained.h. Also, there are two configurations available for this board in the configs directory, which are as follows:

These configurations are used to define the board Secondary Program Loader (SPL) initialization method. SPL represents a small binary built from the U-Boot source code that is placed on the SRAM memory and is used to load the U-Boot into the RAM memory. Usually, it has less than 4 KB of memory, and this is how the booting sequence looks:

The U-Boot bootloader

Before actually starting the build for the U-Boot source code for a specific board, the board configuration must be specified. For the Atmel SAMA5_Xplained development board, as presented in the preceding image, there are two available configurations that could be done. The configuration is done with the make ARCH=arm CROSS_COMPILE=${CC} sama5d3_xplained_nandflash_defconfig command. Behind this command, the include/config.h file is created. This header include definitions that are specific for the chosen board, architecture, CPU, and also board-specific header includes. The defined CONFIG_* variable read from the include/config.h file includes determining the compilation process. After the configuration is completed, the build can be started for the U-Boot.

Another example that can be very useful when inspected relates to the other scenario of booting an embedded system, one that requires the use of a NOR memory. In this situation, we can take a look at a particular example. This is also well described inside the Embedded Linux Primer by Christopher Hallinan, where a processor of the AMCC PowerPC 405GP is discussed. The hardcoded address for this processor is 0xFFFFFFFC and is visible using .resetvec , the reset vector placement. There also specifies the fact that the rest of this section is completed with only the value 1 until the end of the 0xFFFFFFFF stack; this implies that an empty flash memory array is completed only with values of 1. The information about this section is available in resetvec.S file, which is located at arch/powerpc/cpu/ppc4xx/resetvec.S. The contents of resetvec.S file is as follows:

On inspection of this file's source code, it can be seen that only an instruction is defined in this section independently of the available configuration options.

The configuration for the U-Boot is done through two types of configuration variables. The first one is CONFIG_*, and it makes references to configuration options that can be configured by a user to enable various operational features. The other option is called CFG_* and this is used for configuration settings and to make references to hardware-specific details. The CFG_* variable usually requires good knowledge of a hardware platform, peripherals and processors in general. The configure file for the SAMA5D3 Xplained hardware platform is available inside the include/config.h header file, as follows:

The configuration variables available here represent the corresponding configurations for the SAMA5D3 Xplained board. A part of these configurations refer to a number of standard commands available for user interactions with the bootloader. These commands can be added or removed for the purpose of extending or subtracting commands from the available command line interface.

More information on the U-Boot configurable command interface can be found at http://www.denx.de/wiki/view/DULG/UBootCommandLineInterface.

In an industrial environment, interaction with the U-Boot is mainly done through the Ethernet interface. Not only does an Ethernet interface enable the faster transfer of operating system images, but it is also less prone to errors than a serial connection.

One of the most important features available inside a bootloader is related to the support for Dynamic Host Control Protocol (DHCP), Trivial File Transfer Protocol (TFTP), and even Bootstrap Protocol (BOOTP). BOOTP and DHCP enable an Ethernet connection to configure itself and acquire an IP address from a specialized server. TFTP enables the download of files through a TFTP server. The messages passed between a target device and the DHCP/BOOTP servers are represented in the following image in a more generic manner. Initially, the hardware platform sends a broadcast message that arrives at all the DHCP/BOOTP servers available. Each server sends back its offer, which also contains an IP address, and the client accepts the one that suits its purposes the best and declines the other ones.

Booting the U-Boot options

After the target device has finished communication with DHCP/BOOTP, it remains with a configuration that is specific to the target and contains information, such as the hostname, target IP and hardware Ethernet address (MAC address), netmask, tftp server IP address and even a TFTP filename. This information is bound to the Ethernet port and is used later in the booting process.

To boot images, U-Boot offers a number of capabilities that refer to the support of storage subsystems. These options include the RAM boot, MMC boot, NAND boot, NFS boot and so on. The support for these options is not always easy and could imply both hardware and software complexity.

I've mentioned previously that U-Boot is one of the most used and known bootloaders available. This is also due to the fact that its architecture enables the porting of new development platforms and processors in a very easy manner. At the same time, there are a huge number of development platforms available that could be used as references. The first thing that any developer who is interested in porting a new platform should do is to inspect the board and arch directories to establish their baselines, and, at the same time, also identify their similarities with other CPUs and available boards.

The board.cfg file is the starting point to register a new platform. Here, the following information should be added as a table line:

To port a machine similar to SAMA5D3 Xplained, one of the directories that could be consulted is the arch directory. It contains files, such as board.c, with information related to the initialization process for boards and SOCs. The most notable processes are board_init_r(), which does the setup and probing for board and peripherals after its relocation in the RAM, board_init_f(), which identifies the stack size and reserved address before its relocation in the RAM, and init_sequence[], which is called inside the board_init_f for the setup of peripherals. Other important files inside the same locations are the bootm.c and interrupts.c files. The former has the main responsibility of the boot from memory of the operating system, and the latter is responsible for implementation of generic interrupts.

The board directory also has some interesting files and functions that need to be mentioned here, such as the board/atmel/sama5d3_xplained/sama5d3_xplained.c file. It contains functions, such as board_init(), dram_init(), board_eth_init(), board_mmc_init, spl_board_ init(), and mem_init() that are used for initialization, and some of them called by the arch/arm/lib/board.c file.

Here are some other relevant directories:

More information about the SAMA5D3 Xplained board can be found by inspecting the corresponding doc directory and README files, such as README.at91, README.at91-soc, README.atmel_mci, README.atmel_pmecc, README.ARM-memory-map, and so on.

For people interested in committing to the changes they made while porting a new development board, CPU, or SOC to U-Boot, a few rules should be followed. All of these are related to the git interaction and help you to ensure the proper maintenance of your branches.

The first thing that a developer should do is to track the upstream branch that corresponds to a local branch. Another piece of advice would be to forget about git merge and instead use git rebase. Keeping in contact with the upstream repository can be done using the git fetch command. To work with patches, some general rules need to be followed, and patches need to have only one logical change, which can be any one of these:

Let's take a look at following diagram, which illustrates the git rebase operation:

Porting U-Boot

As shown in both the preceding and following diagram, the git rebase operation has recreated the work from one branch onto another. Every commit from one branch is made available on the succeeding one, just after the last commit from it.

Porting U-Boot

The git merge operation, on the other hand, is a new commit that has two parents: the branch from which it was ported, and the new branch on which it was merged. In fact, it gathers a series of commits into one branch with a different commit ID, which is why they are difficult to manage.

Porting U-Boot

More information related to git interactions can be found at http://git-scm.com/documentation or http://www.denx.de/wiki/U-Boot/Patches.

Almost always when porting a new feature in U-Boot, debugging is involved. For a U-Boot debugger, there are two different situations that can occur:

  • The first situation is when lowlevel_init was not executed
  • The second situation is when the lowlevel_init was executed; this is the most well known scenario

In the next few lines, the second situation will be considered: the baseline enabling a debugging session for U-Boot. To make sure that debugging is possible, the elf file needs to be executed. Also, it cannot be manipulated directly because the linking address will be relocated. For this, a few tricks should be used:

  • The first step is to make sure that the environment is clean and that old objects are not available any more: make clean
  • The next step would be to make sure the dependencies are cleaned: find ./ | grep depend | xargs rm
  • After the cleaning is finished, the target build can start and the output can be redirected inside a log file: make sama5d3_xplained 2>&1 > make.log
  • The generated output should be renamed to avoid debugging problems for multiple boards: mv u-boot.bin u-boot_sama5d3_xplained.bin
  • It is important to enable DEBUG in the board configuration file; inside include/configs/ sama5d3_xplained.h, add the #define DEBUG line

An early development platform can be set up after relocation takes place and the proper breakpoint should be set after the relocation has ended. A symbol needs to be reloaded for U-Boot because the relocation will move the linking address. For all of these tasks, a gdb script is indicated as gdb gdb-script.sh:

Booting the U-Boot options

In an industrial

environment, interaction with the U-Boot is mainly done through the Ethernet interface. Not only does an Ethernet interface enable the faster transfer of operating system images, but it is also less prone to errors than a serial connection.

One of the most important features available inside a bootloader is related to the support for Dynamic Host Control Protocol (DHCP), Trivial File Transfer Protocol (TFTP), and even Bootstrap Protocol (BOOTP). BOOTP and DHCP enable an Ethernet connection to configure itself and acquire an IP address from a specialized server. TFTP enables the download of files through a TFTP server. The messages passed between a target device and the DHCP/BOOTP servers are represented in the following image in a more generic manner. Initially, the hardware platform sends a broadcast message that arrives at all the DHCP/BOOTP servers available. Each server sends back its offer, which also contains an IP address, and the client accepts the one that suits its purposes the best and declines the other ones.

Booting the U-Boot options

After the target device has finished communication with DHCP/BOOTP, it remains with a configuration that is specific to the target and contains information, such as the hostname, target IP and hardware Ethernet address (MAC address), netmask, tftp server IP address and even a TFTP filename. This information is bound to the Ethernet port and is used later in the booting process.

To boot images, U-Boot offers a number of capabilities that refer to the support of storage subsystems. These options include the RAM boot, MMC boot, NAND boot, NFS boot and so on. The support for these options is not always easy and could imply both hardware and software complexity.

I've mentioned previously that U-Boot is one of the most used and known bootloaders available. This is also due to the fact that its architecture enables the porting of new development platforms and processors in a very easy manner. At the same time, there are a huge number of development platforms available that could be used as references. The first thing that any developer who is interested in porting a new platform should do is to inspect the board and arch directories to establish their baselines, and, at the same time, also identify their similarities with other CPUs and available boards.

The board.cfg file is the starting point to register a new platform. Here, the following information should be added as a table line:

To port a machine similar to SAMA5D3 Xplained, one of the directories that could be consulted is the arch directory. It contains files, such as board.c, with information related to the initialization process for boards and SOCs. The most notable processes are board_init_r(), which does the setup and probing for board and peripherals after its relocation in the RAM, board_init_f(), which identifies the stack size and reserved address before its relocation in the RAM, and init_sequence[], which is called inside the board_init_f for the setup of peripherals. Other important files inside the same locations are the bootm.c and interrupts.c files. The former has the main responsibility of the boot from memory of the operating system, and the latter is responsible for implementation of generic interrupts.

The board directory also has some interesting files and functions that need to be mentioned here, such as the board/atmel/sama5d3_xplained/sama5d3_xplained.c file. It contains functions, such as board_init(), dram_init(), board_eth_init(), board_mmc_init, spl_board_ init(), and mem_init() that are used for initialization, and some of them called by the arch/arm/lib/board.c file.

Here are some other relevant directories:

More information about the SAMA5D3 Xplained board can be found by inspecting the corresponding doc directory and README files, such as README.at91, README.at91-soc, README.atmel_mci, README.atmel_pmecc, README.ARM-memory-map, and so on.

For people interested in committing to the changes they made while porting a new development board, CPU, or SOC to U-Boot, a few rules should be followed. All of these are related to the git interaction and help you to ensure the proper maintenance of your branches.

The first thing that a developer should do is to track the upstream branch that corresponds to a local branch. Another piece of advice would be to forget about git merge and instead use git rebase. Keeping in contact with the upstream repository can be done using the git fetch command. To work with patches, some general rules need to be followed, and patches need to have only one logical change, which can be any one of these:

Let's take a look at following diagram, which illustrates the git rebase operation:

Porting U-Boot

As shown in both the preceding and following diagram, the git rebase operation has recreated the work from one branch onto another. Every commit from one branch is made available on the succeeding one, just after the last commit from it.

Porting U-Boot

The git merge operation, on the other hand, is a new commit that has two parents: the branch from which it was ported, and the new branch on which it was merged. In fact, it gathers a series of commits into one branch with a different commit ID, which is why they are difficult to manage.

Porting U-Boot

More information related to git interactions can be found at http://git-scm.com/documentation or http://www.denx.de/wiki/U-Boot/Patches.

Almost always when porting a new feature in U-Boot, debugging is involved. For a U-Boot debugger, there are two different situations that can occur:

  • The first situation is when lowlevel_init was not executed
  • The second situation is when the lowlevel_init was executed; this is the most well known scenario

In the next few lines, the second situation will be considered: the baseline enabling a debugging session for U-Boot. To make sure that debugging is possible, the elf file needs to be executed. Also, it cannot be manipulated directly because the linking address will be relocated. For this, a few tricks should be used:

  • The first step is to make sure that the environment is clean and that old objects are not available any more: make clean
  • The next step would be to make sure the dependencies are cleaned: find ./ | grep depend | xargs rm
  • After the cleaning is finished, the target build can start and the output can be redirected inside a log file: make sama5d3_xplained 2>&1 > make.log
  • The generated output should be renamed to avoid debugging problems for multiple boards: mv u-boot.bin u-boot_sama5d3_xplained.bin
  • It is important to enable DEBUG in the board configuration file; inside include/configs/ sama5d3_xplained.h, add the #define DEBUG line

An early development platform can be set up after relocation takes place and the proper breakpoint should be set after the relocation has ended. A symbol needs to be reloaded for U-Boot because the relocation will move the linking address. For all of these tasks, a gdb script is indicated as gdb gdb-script.sh:

Porting U-Boot

I've mentioned

previously that U-Boot is one of the most used and known bootloaders available. This is also due to the fact that its architecture enables the porting of new development platforms and processors in a very easy manner. At the same time, there are a huge number of development platforms available that could be used as references. The first thing that any developer who is interested in porting a new platform should do is to inspect the board and arch directories to establish their baselines, and, at the same time, also identify their similarities with other CPUs and available boards.

The board.cfg file is the starting point to register a new platform. Here, the following information should be added as a table line:

To port a machine similar to SAMA5D3 Xplained, one of the directories that could be consulted is the arch directory. It contains files, such as board.c, with information related to the initialization process for boards and SOCs. The most notable processes are board_init_r(), which does the setup and probing for board and peripherals after its relocation in the RAM, board_init_f(), which identifies the stack size and reserved address before its relocation in the RAM, and init_sequence[], which is called inside the board_init_f for the setup of peripherals. Other important files inside the same locations are the bootm.c and interrupts.c files. The former has the main responsibility of the boot from memory of the operating system, and the latter is responsible for implementation of generic interrupts.

The board directory also has some interesting files and functions that need to be mentioned here, such as the board/atmel/sama5d3_xplained/sama5d3_xplained.c file. It contains functions, such as board_init(), dram_init(), board_eth_init(), board_mmc_init, spl_board_ init(), and mem_init() that are used for initialization, and some of them called by the arch/arm/lib/board.c file.

Here are some other relevant directories:

More information about the SAMA5D3 Xplained board can be found by inspecting the corresponding doc directory and README files, such as README.at91, README.at91-soc, README.atmel_mci, README.atmel_pmecc, README.ARM-memory-map, and so on.

For people interested in committing to the changes they made while porting a new development board, CPU, or SOC to U-Boot, a few rules should be followed. All of these are related to the git interaction and help you to ensure the proper maintenance of your branches.

The first thing that a developer should do is to track the upstream branch that corresponds to a local branch. Another piece of advice would be to forget about git merge and instead use git rebase. Keeping in contact with the upstream repository can be done using the git fetch command. To work with patches, some general rules need to be followed, and patches need to have only one logical change, which can be any one of these:

Let's take a look at following diagram, which illustrates the git rebase operation:

Porting U-Boot

As shown in both the preceding and following diagram, the git rebase operation has recreated the work from one branch onto another. Every commit from one branch is made available on the succeeding one, just after the last commit from it.

Porting U-Boot

The git merge operation, on the other hand, is a new commit that has two parents: the branch from which it was ported, and the new branch on which it was merged. In fact, it gathers a series of commits into one branch with a different commit ID, which is why they are difficult to manage.

Porting U-Boot

More information related to git interactions can be found at http://git-scm.com/documentation or http://www.denx.de/wiki/U-Boot/Patches.

Almost always when porting a new feature in U-Boot, debugging is involved. For a U-Boot debugger, there are two different situations that can occur:

  • The first situation is when lowlevel_init was not executed
  • The second situation is when the lowlevel_init was executed; this is the most well known scenario

In the next few lines, the second situation will be considered: the baseline enabling a debugging session for U-Boot. To make sure that debugging is possible, the elf file needs to be executed. Also, it cannot be manipulated directly because the linking address will be relocated. For this, a few tricks should be used:

  • The first step is to make sure that the environment is clean and that old objects are not available any more: make clean
  • The next step would be to make sure the dependencies are cleaned: find ./ | grep depend | xargs rm
  • After the cleaning is finished, the target build can start and the output can be redirected inside a log file: make sama5d3_xplained 2>&1 > make.log
  • The generated output should be renamed to avoid debugging problems for multiple boards: mv u-boot.bin u-boot_sama5d3_xplained.bin
  • It is important to enable DEBUG in the board configuration file; inside include/configs/ sama5d3_xplained.h, add the #define DEBUG line

An early development platform can be set up after relocation takes place and the proper breakpoint should be set after the relocation has ended. A symbol needs to be reloaded for U-Boot because the relocation will move the linking address. For all of these tasks, a gdb script is indicated as gdb gdb-script.sh:

The Yocto Project uses various recipes to define interactions to each of the supported bootloaders. Since there are multiple stages of booting, there are also multiple recipes and packages required inside the BSP. The recipes available for various bootloaders are not different from any other recipes available in the Yocto world. However, they have some details that make them unique.

The board that we will focus on here is the sama5d3_xplained development board, and it is available inside the meta-atmel layer. Inside this layer, the corresponding recipes for the first and second stage bootloaders can be found inside the recipes-bsp directory. Here, I am referring to the at91bootstrap and u-boot recipes. There are some misconceptions about first stage and second stage bootloaders. They might be referred to as second level and third level bootloaders, because the boot ROM code may or may not be taken into account during a discussion. In this book, we prefer to call them as first stage and second stage bootloaders.

The AT91bootstrap package represents the first-stage bootloader from Atmel available for their SOCs. It manages hardware initialization and also executes the second stage bootloader download from a boot media inside the memory; it starts it at the end. In the meta-atmel layer, the second stage bootloader is u-boot, and it is later used for the Linux operating system boot.

Usually, inside a BSP layer, the support for multiple development boards is offered, and this means that multiple versions and bootloader packages are offered as well. The distinction between them, however, is on the basis of machine configurations. For the SAMA5D3 Xplained development board, the machine configuration is available inside the conf/machine/sama5d3_xplained file. In this file, the preferred bootloader versions, providers, and configurations are defined. If these configurations are not MACHINE specific, they could very well be performed inside the package recipe.

This is one example of the configurations available for the sama5d3_xplained development board: