I have to admit that Part 1 of this series was posted too long ago. In fact, I had to read it again myself. To bring you up to speed, let me remind you that in that first post, we built and launched an U-Boot bootloader. We left it just before configuring everything to launch a Linux Kernel using TFTP (Trivial File Transfer Protocol), a simplified version of FTP. And that’s exactly what we are going to do next.

In case you don’t remember, instead of building everything by ourselves, we used Buildroot to generate the corresponding images for us. The goal of this entry is to understand what’s going on under the hoods up to this point, not to understand how to build every tool.

As I said in the previous post too, if you want to go deep into this topic, Bootlin documentation and courses are amazing resources created by very experienced people.

Setting up U-Boot

The process is going to be the one shown in the diagram below, where we will be mostly interacting with U-Boot:

First of all we have to make a few configuration changes to U-Boot, like the IP address we want to assign to our board (ipaddr) and the IP of our computer (serverip). Of course, make sure the board is connected to a switch with an Ethernet cable. The serverip configuration is needed because our host computer will run the TFTP server. We will also test if the Olimex can reach the host PC. Let’s go:

				
					=> setenv ipaddr 192.168.1.99
=> setenv serverip 192.168.1.137
FAT... OK
=> setenv autostart no
=> saveenv
Saving Environment to FAT... OK
=> ping 192.168.1.137
Speed: 1000, full duplex
Using ethernet@1c50000 device
host 192.168.1.137 is alive
				
			

And there we have it, our board has TCP/IP connectivity.

As we promised, we are now going to load the Kernel through TFTP into RAM and launch it, a very convenient option during development, so first of all we have to install the required packages in our computer. I’m using Fedora, but every distribution may work slightly different. Make sure you look for a step-by-step guide that works for you.

In any case, usually TFTP will serve any file located in a given directory. In Fedora, that would be /var/lib/tftpboot. To test it out we are going to create a plain text file in this directory and try to load it from U-Boot.

				
					$ echo "adeus rios" > demo.txt
$ sudo mv demo.txt /var/lib/tftpboot
				
			

Now that the file has been created, we will copy that file into RAM. According to the Allwinner A20 User Manual, the RAM is mapped starting at 0x40000000 address:

Let’s for instance copy the file into 0x4800000. If everything went fine, we should see the contents of the file at this address:

				
					=> tftp 0x48000000 demo.txt  
Speed: 1000, full duplex
Using ethernet@1c50000 device
TFTP from server 192.168.1.137; our IP address is 192.168.1.99
Filename 'demo.txt'.
Load address: 0x48000000
Loading: #
	 1000 Bytes/s
done
Bytes transferred = 11 (b hex)
=> md 0x48000000
48000000: 75656461 69722073 ff0a736f ffffffff  adeus rios......
48000010: ffffffff ffffffff ffffffff ffffffff  ................
48000020: ffffffff ffffffff ffffffff ffffffff  ................
48000030: ffffffff ffffffff ffffffff ffffffff  ................
48000040: ffffffff ffffffff ffffffff ffffffff  ................
				
			

That’s what we were looking for. The Olimex board has successfully managed to retrieve a file located in our computer. Let’s now load the Kernel instead.

Launching the Kernel

To load the Linux Kernel we are going to need two files. Both of them generated by Buildroot:

  • The Kernel binary, located in zImage
  • The board device tree (sun7i-a20-olinuxino-lime2.dtb)

As we did before with demo.txt, we are going to copy these files into RAM.

				
					=> tftp 0x40000000 zImage
Speed: 1000, full duplex
Using ethernet@1c50000 device
TFTP from server 192.168.1.137; our IP address is 192.168.1.99
Filename 'zImage'.
Load address: 0x40000000
Loading: #################################################################
	 #################################################################
	 #################################################################
	 #################################################################
	 #################################################################
	 ########
	 5.5 MiB/s
done
Bytes transferred = 4881968 (4a7e30 hex)
=> tftp 0x41000000 sun7i-a20-olinuxino-lime2.dtb
Speed: 1000, full duplex
Using ethernet@1c50000 device
TFTP from server 192.168.1.137; our IP address is 192.168.1.99
Filename 'sun7i-a20-olinuxino-lime2.dtb'.
Load address: 0x41000000
Loading: ##
	 4.2 MiB/s
done
Bytes transferred = 26282 (66aa hex)
				
			

Finally, we are ready to launch Linux using the bootz command. This command uses 3 arguments used to boot an image located in memory: the address of the Kernel, the presence initramfs (- means no initramfs), and the address of the device tree blob.

				
					=> bootz 0x40000000 - 0x41000000
Kernel image @ 0x40000000 [ 0x000000 - 0x4a7e30 ]
## Flattened Device Tree blob at 41000000
   Booting using the fdt blob at 0x41000000
EHCI failed to shut down host controller.
   Loading Device Tree to 49ff6000, end 49fff6a9 ... OK
DE is present but not probed

Starting kernel ...

[    0.000000] Booting Linux on physical CPU 0x0
[    0.000000] Linux version 5.14.13 (xabi@crespum-xps) (arm-buildroot-linux-gnueabihf-gcc.br_real (Buildroot 2021.11) 10.3.0, GNU ld (GNU Binutils) 2.36.1) #1 SMP Tue Dec 14 13:40:48 CET 2021
				
			

Sadly, the last line contains some disappointing news for us. As we don’t have a root file system yet, the Kernel panics:

				
					[    2.247145] ---[ end Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0) ]---
				
			

For greater convenience, we are going to tell U-Boot to automatically grab these files from TFTP and launch them. This is done by modifying bootcmd variable, which contains the first command the bootloader launches.

				
					=> setenv bootcmd 'tftp 0x40000000 zImage; tftp 0x41000000 sun7i-a20-olinuxino-lime2.dtb; bootz 0x40000000 - 0x41000000'
=> saveenv
				
			

Next steps

Stay tuned if you want to check the last part of this series of entries, I promise it’s not going to be another year on hold. Part 3 is going to be the last one of this series and we are finally going to launch a fully working Linux system.