U-boot is a bootloader primarily used in embedded systems to boot the kernel. In this article I will describe the steps required to build and deploy U-boot for an ARM based machine. Thanks to QEMU for ARM there’s no need for any physical hardware. QEMU is a free and open source hardware emulator. You can read more about U-boot and QEMU from their respective official websites.

In order to begin this ensure the following is complete:

  1. Install qemu for ARM package on your Linux machine. On Ubuntu the command is:
     sudo apt-get install qemu-system-arm
    
  2. Download and build the Linux kernel from the sources for the ARM Versatile express board.
    $ cd linux
    $ make ARCH=arm CROSS_COMPILE=<PATH TO TOOLCHAIN>/arm-none-linux-gnueabi- vexpress_defconfig
    $ make ARCH=arm CROSS_COMPILE=<PATH TO TOOLCHAIN>/arm-none-linux-gnueabi- zImage dtbs
    
  3. Download the U-boot source code and build the bootloader binary for the vexpress board. The name of file will be u-boot
    $ cd u-boot
    $ make ARCH=arm CROSS_COMPILE=<PATH TO TOOLCHAIN>/arm-none-linux-gnueabi- vexpress_ca9x4_defconfig
    $ make ARCH=arm CROSS_COMPILE=<PATH TO TOOLCHAIN>/arm-none-linux-gnueabi-
    

Create a file init.c and add the following contents

#include <stdio.h>
 
void main() {
	char buff[100];
	printf("Enter anything");
	while(1)
	{
		scanf("%s",buff);
		printf("%s\n",buff);
	}
}

Compile the file as a static binary. The file size will be bigger than a dynamically linked binary. Then gunzip the file using gzip tool. Finally using the mkimage tool create the U-boot image file. This step ensures the binary is in the correct format to be loaded by the kernel.

$ <PATH TO TOOLCHAIN>/arm-none-linux-gnueabi-gcc -static init.c -o init
$ echo init|cpio -o --format=newc > initramfs
$ gzip initramfs
$ mkimage -A arm -O linux -T ramdisk -C gzip -n "Build Root File System" -d initramfs.gz initramfs.gz.uboot

You will also need to setup a TFTP server. The folder of this server should contain

  1. Kernel image (zImage)
  2. Device tree file (vexpress-v2p-ca9.dtb)
  3. The ramdisk image file (initramfs.gz.uboot)

You can read about how to setup your TFTP server here. Setup TFTP server

Once complete start QEMU with following command.

$ qemu-system-arm -M vexpress-a9 -kernel u-boot -serial stdio -append "console=ttyAMA0"

Stop the autoboot by pressing any key. Now using the following commands load the kernel,device tree and init ramdisk. Run ifconfig tool on the command line and set the same IP address for the serverip environment variable.

=> dhcp
=> set serverip 192.168.0.105
=> tftp 0x62008000 zImage
=> tftp 0x623CF740 initramfs.gz.uboot
=> tftp 0x62572FD1 vexpress-v2p-ca9.dtb
=> bootz 0x62008000 0x623cf740 0x62572fd1

The bootz command begins execution of the kernel code, the kernel performs necessary setup and hands over the control to init Ramdisk.

fsociety@fsociety-VirtualBox:~/nfs_share$ qemu-system-arm -M vexpress-a9 -kernel u-boot  -serial stdio -append "console=ttyAMA0,115200"
pulseaudio: set_sink_input_volume() failed
pulseaudio: Reason: Invalid argument
pulseaudio: set_sink_input_mute() failed
pulseaudio: Reason: Invalid argument


U-Boot 2019.01-00352-g2f41ade (Jan 31 2019 - 00:45:42 +0530)

DRAM:  128 MiB
WARNING: Caches not enabled
Flash: 128 MiB
MMC:   MMC: 0
*** Warning - bad CRC, using default environment

In:    serial
Out:   serial
Err:   serial
Net:   smc911x-0
Hit any key to stop autoboot:  0 
=> dhcp 
smc911x: MAC 52:54:00:12:34:56
smc911x: detected LAN9118 controller
smc911x: phy initialized
smc911x: MAC 52:54:00:12:34:56
BOOTP broadcast 1
DHCP client bound to address 10.0.2.15 (13 ms)
*** Warning: no boot file name; using '0A00020F.img'
Using smc911x-0 device
TFTP from server 10.0.2.2; our IP address is 10.0.2.15
Filename '0A00020F.img'.
smc911x: MAC 52:54:00:12:34:56

TFTP error: trying to overwrite reserved memory...
smc911x: MAC 52:54:00:12:34:56
=> set serverip 192.168.0.105
=> tftp 0x62008000 zImage
smc911x: MAC 52:54:00:12:34:56
smc911x: detected LAN9118 controller
smc911x: phy initialized
smc911x: MAC 52:54:00:12:34:56
Using smc911x-0 device
TFTP from server 192.168.0.105; our IP address is 10.0.2.15; sending through gateway 10.0.2.2
Filename 'zImage'.
Load address: 0x62008000
Loading: #################################################################
	 #################################################################
	 #################################################################
	 #################################################################
	 ##########
	 1.3 MiB/s
done
Bytes transferred = 3962688 (3c7740 hex)
smc911x: MAC 52:54:00:12:34:56
=> tftp 0x623CF740 initramfs.gz.uboot
smc911x: MAC 52:54:00:12:34:56
smc911x: detected LAN9118 controller
smc911x: phy initialized
smc911x: MAC 52:54:00:12:34:56
Using smc911x-0 device
TFTP from server 192.168.0.105; our IP address is 10.0.2.15; sending through gateway 10.0.2.2
Filename 'initramfs.gz.uboot'.
Load address: 0x623cf740
Loading: #################################################################
	 #####################################################
	 1.2 MiB/s
done
Bytes transferred = 1718312 (1a3828 hex)
smc911x: MAC 52:54:00:12:34:56
=> tftp 0x62572FD1 vexpress-v2p-ca9.dtb
smc911x: MAC 52:54:00:12:34:56
smc911x: detected LAN9118 controller
smc911x: phy initialized
smc911x: MAC 52:54:00:12:34:56
Using smc911x-0 device
TFTP from server 192.168.0.105; our IP address is 10.0.2.15; sending through gateway 10.0.2.2
Filename 'vexpress-v2p-ca9.dtb'.
Load address: 0x62572fd1
Loading: ##
	 409.2 KiB/s
done
Bytes transferred = 14692 (3964 hex)
smc911x: MAC 52:54:00:12:34:56
=> bootz 0x62008000 0x623cf740 0x62572fd1
Kernel image @ 0x62008000 [ 0x000000 - 0x3c7740 ]
## Loading init Ramdisk from Legacy Image at 623cf740 ...
   Image Name:   Build Root File System
   Image Type:   ARM Linux RAMDisk Image (gzip compressed)
   Data Size:    1718248 Bytes = 1.6 MiB
   Load Address: 00000000
   Entry Point:  00000000
   Verifying Checksum ... OK
## Flattened Device Tree blob at 62572fd1
   Booting using the fdt blob at 0x62572fd1
   Loading Ramdisk to 67cc7000, end 67e6a7e8 ... OK
   Loading Device Tree to 67cc0000, end 67cc6963 ... OK

Starting kernel ...

Booting Linux on physical CPU 0x0
Linux version 4.14.52+ (hema@hema-VirtualBox) (gcc version 4.8.3 20140303 (prerelease) (crosstool-NG linaro-1.13.1+bzr2650 - Linaro GCC 2014.03)) #2 SMP Thu Jan 31 00:47:55 IST 2019
CPU: ARMv7 Processor [410fc090] revision 0 (ARMv7), cr=10c5387d
CPU: PIPT / VIPT nonaliasing data cache, VIPT nonaliasing instruction cache
OF: fdt: Machine model: V2P-CA9
Memory policy: Data cache writeback
CPU: All CPU(s) started in SVC mode.
percpu: Embedded 16 pages/cpu @87eae000 s36544 r8192 d20800 u65536
Built 1 zonelists, mobility grouping on.  Total pages: 32512
Kernel command line: console=ttyAMA0
log_buf_len individual max cpu contribution: 4096 bytes
log_buf_len total cpu_extra contributions: 12288 bytes
log_buf_len min size: 16384 bytes
log_buf_len: 32768 bytes
early log buf free: 14992(91%)
PID hash table entries: 512 (order: -1, 2048 bytes)
Dentry cache hash table entries: 16384 (order: 4, 65536 bytes)
Inode-cache hash table entries: 8192 (order: 3, 32768 bytes)
Memory: 118056K/131072K available (6144K kernel code, 402K rwdata, 1372K rodata, 1024K init, 170K bss, 13016K reserved, 0K cma-reserved)
Virtual kernel memory layout:
    vector  : 0xffff0000 - 0xffff1000   (   4 kB)
    fixmap  : 0xffc00000 - 0xfff00000   (3072 kB)
    vmalloc : 0x88800000 - 0xff800000   (1904 MB)
    lowmem  : 0x80000000 - 0x88000000   ( 128 MB)
    modules : 0x7f000000 - 0x80000000   (  16 MB)
      .text : 0x80008000 - 0x80700000   (7136 kB)
      .init : 0x80900000 - 0x80a00000   (1024 kB)
      .data : 0x80a00000 - 0x80a64b18   ( 403 kB)
       .bss : 0x80a6bc4c - 0x80a964f4   ( 171 kB)
SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=4, Nodes=1
Hierarchical RCU implementation.
	RCU event tracing is enabled.
	RCU restricting CPUs from NR_CPUS=8 to nr_cpu_ids=4.
RCU: Adjusting geometry for rcu_fanout_leaf=16, nr_cpu_ids=4
NR_IRQS: 16, nr_irqs: 16, preallocated irqs: 16
GIC CPU mask not found - kernel will fail to boot.
GIC CPU mask not found - kernel will fail to boot.
L2C: platform modifies aux control register: 0x02020000 -> 0x02420000
L2C: DT/platform modifies aux control register: 0x02020000 -> 0x02420000
L2C-310 enabling early BRESP for Cortex-A9
L2C-310 full line of zeros enabled for Cortex-A9
L2C-310 dynamic clock gating disabled, standby mode disabled
L2C-310 cache controller enabled, 8 ways, 128 kB
L2C-310: CACHE_ID 0x410000c8, AUX_CTRL 0x46420001
smp_twd: clock not found -2
sched_clock: 32 bits at 24MHz, resolution 41ns, wraps every 89478484971ns
clocksource: arm,sp804: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 1911260446275 ns
Failed to initialize '/smb@4000000/motherboard/iofpga@7,00000000/timer@12000': -22
Console: colour dummy device 80x30
Calibrating local timer... 99.64MHz.
Calibrating delay loop... 285.08 BogoMIPS (lpj=1425408)
pid_max: default: 32768 minimum: 301
Mount-cache hash table entries: 1024 (order: 0, 4096 bytes)
Mountpoint-cache hash table entries: 1024 (order: 0, 4096 bytes)
CPU: Testing write buffer coherency: ok
CPU0: thread -1, cpu 0, socket 0, mpidr 80000000
Setting up static identity map for 0x60100000 - 0x60100060
Hierarchical SRCU implementation.
smp: Bringing up secondary CPUs ...
smp: Brought up 1 node, 1 CPU
SMP: Total of 1 processors activated (285.08 BogoMIPS).
CPU: All CPU(s) started in SVC mode.
devtmpfs: initialized
random: get_random_u32 called from bucket_table_alloc+0xe8/0x254 with crng_init=0
VFP support v0.3: implementor 41 architecture 3 part 30 variant 9 rev 0
clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 19112604462750000 ns
futex hash table entries: 1024 (order: 4, 65536 bytes)
NET: Registered protocol family 16
DMA: preallocated 256 KiB pool for atomic coherent allocations
cpuidle: using governor ladder
hw-breakpoint: debug architecture 0x4 unsupported.
Serial: AMBA PL011 UART driver
OF: amba_device_add() failed (-19) for /memory-controller@100e0000
OF: amba_device_add() failed (-19) for /memory-controller@100e1000
OF: amba_device_add() failed (-19) for /watchdog@100e5000
10009000.uart: ttyAMA0 at MMIO 0x10009000 (irq = 35, base_baud = 0) is a PL011 rev1
console [ttyAMA0] enabled
1000a000.uart: ttyAMA1 at MMIO 0x1000a000 (irq = 36, base_baud = 0) is a PL011 rev1
1000b000.uart: ttyAMA2 at MMIO 0x1000b000 (irq = 37, base_baud = 0) is a PL011 rev1
1000c000.uart: ttyAMA3 at MMIO 0x1000c000 (irq = 38, base_baud = 0) is a PL011 rev1
OF: amba_device_add() failed (-19) for /smb@4000000/motherboard/iofpga@7,00000000/wdt@f000
SCSI subsystem initialized
usbcore: registered new interface driver usbfs
usbcore: registered new interface driver hub
usbcore: registered new device driver usb
Advanced Linux Sound Architecture Driver Initialized.
clocksource: Switched to clocksource arm,sp804
NET: Registered protocol family 2
TCP established hash table entries: 1024 (order: 0, 4096 bytes)
TCP bind hash table entries: 1024 (order: 1, 8192 bytes)
TCP: Hash tables configured (established 1024 bind 1024)
UDP hash table entries: 256 (order: 1, 8192 bytes)
UDP-Lite hash table entries: 256 (order: 1, 8192 bytes)
NET: Registered protocol family 1
RPC: Registered named UNIX socket transport module.
RPC: Registered udp transport module.
RPC: Registered tcp transport module.
RPC: Registered tcp NFSv4.1 backchannel transport module.
Unpacking initramfs...
Freeing initrd memory: 1680K
hw perfevents: enabled with armv7_cortex_a9 PMU driver, 1 counters available
workingset: timestamp_bits=30 max_order=15 bucket_order=0
squashfs: version 4.0 (2009/01/31) Phillip Lougher
jffs2: version 2.2. (NAND) © 2001-2006 Red Hat, Inc.
9p: Installing v9fs 9p2000 file system support
io scheduler noop registered (default)
io scheduler mq-deadline registered
io scheduler kyber registered
clcd-pl11x 10020000.clcd: PL111 designer 41 rev2 at 0x10020000
clcd-pl11x 10020000.clcd: /clcd@10020000 hardware, 1024x768@59 display
Console: switching to colour frame buffer device 128x48
clcd-pl11x 1001f000.clcd: PL111 designer 41 rev2 at 0x1001f000
clcd-pl11x 1001f000.clcd: /smb@4000000/motherboard/iofpga@7,00000000/clcd@1f000 hardware, 640x480@59 display
40000000.flash: Found 2 x16 devices at 0x0 in 32-bit bank. Manufacturer ID 0x000000 Chip ID 0x000000
Intel/Sharp Extended Query Table at 0x0031
Using buffer write method
40000000.flash: Found 2 x16 devices at 0x0 in 32-bit bank. Manufacturer ID 0x000000 Chip ID 0x000000
Intel/Sharp Extended Query Table at 0x0031
Using buffer write method
Concatenating MTD devices:
(0): "40000000.flash"
(1): "40000000.flash"
into device "40000000.flash"
libphy: Fixed MDIO Bus: probed
libphy: smsc911x-mdio: probed
smsc911x 4e000000.ethernet eth0: MAC Address: 52:54:00:12:34:56
isp1760 4f000000.usb: bus width: 32, oc: digital
isp1760 4f000000.usb: NXP ISP1760 USB Host Controller
isp1760 4f000000.usb: new USB bus registered, assigned bus number 1
isp1760 4f000000.usb: Scratch test failed.
isp1760 4f000000.usb: can't setup: -19
isp1760 4f000000.usb: USB bus 1 deregistered
usbcore: registered new interface driver usb-storage
rtc-pl031 10017000.rtc: rtc core: registered pl031 as rtc0
IR NEC protocol handler initialized
IR RC5(x/sz) protocol handler initialized
IR RC6 protocol handler initialized
IR JVC protocol handler initialized
IR Sony protocol handler initialized
IR SANYO protocol handler initialized
IR Sharp protocol handler initialized
IR MCE Keyboard/mouse protocol handler initialized
IR XMP protocol handler initialized
mmci-pl18x 10005000.mmci: /aliases ID not available
mmci-pl18x 10005000.mmci: Got CD GPIO
mmci-pl18x 10005000.mmci: Got WP GPIO
mmci-pl18x 10005000.mmci: mmc0: PL181 manf 41 rev0 at 0x10005000 irq 31,32 (pio)
input: AT Raw Set 2 keyboard as /devices/platform/smb@4000000/smb@4000000:motherboard/smb@4000000:motherboard:iofpga@7,00000000/10006000.kmi/serio0/input/input0
ledtrig-cpu: registered to indicate activity on CPUs
usbcore: registered new interface driver usbhid
usbhid: USB HID core driver
aaci-pl041 10004000.aaci: ARM AC'97 Interface PL041 rev0 at 0x10004000, irq 30
aaci-pl041 10004000.aaci: FIFO 512 entries
oprofile: using arm/armv7-ca9
NET: Registered protocol family 17
9pnet: Installing 9P2000 support
Registering SWP/SWPB emulation handler
rtc-pl031 10017000.rtc: setting system clock to 2019-02-27 19:21:54 UTC (1551295314)
ALSA device list:
  #0: ARM AC'97 Interface PL041 rev0 at 0x10004000, irq 30
Freeing unused kernel memory: 1024K
input: ImExPS/2 Generic Explorer Mouse as /devices/platform/smb@4000000/smb@4000000:motherboard/smb@4000000:motherboard:iofpga@7,00000000/10007000.kmi/serio1/input/input2
Enter anything     
hello
hello

You can observe the simple code we had compiled earlier is executed at the end after kernel starts the init RAMdisk.