DEV Community

Can Gulmez
Can Gulmez

Posted on

Embedded Linux Development - Part 2

In this post, I will continue the embedded Linux development series with second stage which is the bootloader.

Firstly, a bit theoretical background.

The bootloader is a piece of code responsible for:

  • Basic hardware initialization
  • Loading of an application binary, usually an operating system kernel, from flash storage, from the network, or from another type of non-volatile storage
  • Possible decompression of the application binary
  • Execution of the application

Besides these basic functions, most bootloaders provide a shell or menu:

  • Menu to select the operating system to load
  • Shell with commands to load data from storage or network, inspect memory, perform hardware testing/diagnostics

Boot process highly is depended to processor. Let's focus on AM335x Sitara (ARM) booting sequence.

Most embedded processors include a ROM code that implements the initial step of the boot process. It's called the first-stage loader. This ROM code is written by the processor vendor directly built into the processor. That means, it cannot be changed or updated by the others. It initializes the CPU and internal SRAM. It also searches the MLO from NAND/NOR flash, from USB, from SD card, from eMMC, etc.

MLO is a file that you need to produce from the bootloader source code. It's called also second-program loader (SPL). Its mission is to initialize the DDR RAM and run the main bootloader.

The main bootloader initializes the hardware and then loads the Linux kernel and then jump to it. I will use the U-Boot. It's the mostly used bootloader in embedded world.

In summary, the booting process: ROM code --> MLO (SPL) --> U-Boot --> Linux kernel.

So after compiled the u-boot, I expect these:

  • MLO

  • u-boot.img

Let's produce these.

Firstly, download the u-boot:

$ git clone https://source.denx.de/u-boot/u-boot.git
Enter fullscreen mode Exit fullscreen mode

If you look at the source code, you will see that the u-boot supports a lot of processors and boards. So first thing you should do is to configure the u-boot over configuration files (ends with *_defconfig) under u-boot/configs.

But which exactly configuration file corresponds to BeagleBone Black? The answer is am335x_evm_defconfig. It's generic AM335x Sitara processor configuration file.

To use this configuration file:

$ make am335x_evm_defconfig
Enter fullscreen mode Exit fullscreen mode

It will create the .config file and write the configs in it. The important point is that you never edit this file by hand.

To customize and tailor this configs further:

$ make menuconfig     # or xconfig (for Qt-based)
Enter fullscreen mode Exit fullscreen mode

It will open the terminal-based GUI to select/unselect the configs. If you are unfamiliar or newer, you should NOT do it. Because it will show the tons of different configs and it can seem messy. The previous command already made the generic required configs.

After setting up the configs, let's build the outputs.

$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- 
Enter fullscreen mode Exit fullscreen mode

It will take for a while. Because it will compile the tons of source file and then link them together. The output will be like:

(...)

  AR      spl/fs/fat/built-in.o
  CC      spl/fs/fs_internal.o
  AR      spl/fs/built-in.o
  CC      spl/net/arp.o
  CC      spl/net/bootp.o
  CC      spl/net/eth-uclass.o
  CC      spl/net/eth_common.o
  CC      spl/net/net.o
  CC      spl/net/ping.o
  CC      spl/net/tftp.o
  AR      spl/net/built-in.o
  LDS     spl/u-boot-spl.lds
  LD      spl/u-boot-spl
  OBJCOPY spl/u-boot-spl-nodtb.bin
  COPY    spl/u-boot-spl.bin
  MKIMAGE MLO
  MKIMAGE MLO.byteswap
  SYM     spl/u-boot-spl.sym
  MKIMAGE u-boot-dtb.img
  OFCHK   .config
Enter fullscreen mode Exit fullscreen mode

After that you can see the u-boot/MLO, u-boot/u-boot.img and a lots of other files.

As I said before, I need the MLO and u-boot.img and will use these in further. For now, just keeps these in mind.

There is also one more important file that I need. It's uEnv.txt and used by the u-boot when loading the Linux kernel. It consists of the key-value pairs that tell some command to u-boot. So create a new file named of it.

$ vim uEnv.txt
Enter fullscreen mode Exit fullscreen mode

And write the following commands in it.

bootpart=0:1
devtype=mmc
bootdir=
bootfile=zImage
bootpartition=mmcblk0p2
console=ttyS0,115200n8
loadaddr=0x82000000
fdtaddr=0x88000000
set_mmc1=if test $board_name = A33515BB; then setenv bootpartition mmcblk1p2; fi
set_bootargs=setenv bootargs console=${console} root=/dev/${bootpartition} rw rootfstype=ext4 rootwait
uenvcmd=run set_mmc1; run set_bootargs;run loadimage;run loadfdt;printenv bootargs;bootz ${loadaddr} - ${fdtaddr}
Enter fullscreen mode Exit fullscreen mode

At the end of this series, you will fully understand what these are. But basically:

  • the console name is set to ttyS0 and baud rate to 115200.

  • the Linux kernel name is defined as zImage.

  • the address that Linux kernel image will be placed is set to 0x82000000 and the address that device tree blob will be placed to 0x88000000.

  • set the boot environment and load the Linux kernel with bootz command.

Save it and then keep this file in mind as being MLO and u-boot.img.

That's it. You've produced the three components related to bootloader stage. In next post, I will interest with Linux kernel itself.

Resources:

Simmonds C., Mastering Embedded Linux Programming, Packt Publishing, 2015
Embedded Linux system development training, Bootlin, 2024

Top comments (0)