TFTP Boot on the Elimo Impetus

Where were we?

In case you missed the latest update, Elimo designed a small, powerful Linux System-on-Module, designed for low cost and fast system integration. It’s FOSS and OSHW!

We have buildroot working and booting, and we are in the process of adding support for all peripherals

TL;DR

The name of our Allwinner S3 based module is Impetus, it now has a working buildroot tree that you can find on our public GitHub repo!

You can boot the device from the SD Card, of course, but in this post we show how to use TFTP to boot kernels over the network, to speed up kernel development by avoiding continuous reflashing of a poor SD Card. Instructions are available on our wiki

To receive updates about our work on the board and how to buy some, watch this space or subscribe to our RSS feed!
We also offer bespoke embedded electronics design services – get in touch to find out how we can help to make your electronic product idea a reality

Problem

When developing a custom kernel, the standard boot process from SD Card can be inconvenient:

  1. build the kernel
  2. remove power from the board
  3. remove the SD Card from your board
  4. put it in an SD Card reader
  5. flash sdcard.img to the SD Card with dd
  6. remove the SD Card from the reader
  7. put it in the board
  8. power the board
  9. test, find a problem
  10. goto 1

I have worn out more several SD card readers like this =)

Solution

The patched u-boot for the Elimo Impetus supports the Ethernet EMAC+PHY on the S3. Together with the Ethernet connector on the Initium, this allows u-boot to load from the network everything it needs to boot, i.e.:

  1. the zImage kernel
  2. the dtb file for the flattened device tree
  3. the boot.scr telling u-boot how to run the kernel

This means that instead of the whole process above, you can keep the same SD card in the board and just load a different kernel from a TFTP server at every boot. Win!

Building U-boot for automatic network boot

Starting from commit 3697ea143b, buildroot for the Elimo Impetus supports automatic tftp boot. This behaviour however is not enabled by default, since it overrides mmc boot (which is what you want in most use cases). To enable it, you will need to change the configuration file used by genimage to create the SD card image.

The default config, in board/elimo/impetus/genimage.cfg creates a boot partition with a boot script, the dtb and the kernel image. The one you want to use is in board/elimo/impetus/genimage-tftpboot.cfg and only puts a custom uboot environment file in the boot partition.

Assuming you already have a functional build, you can make the switch by changing the following line in your .config:

BR2_ROOTFS_POST_SCRIPT_ARGS="-c board/elimo/impetus/genimage.cfg"

to:

BR2_ROOTFS_POST_SCRIPT_ARGS="-c board/elimo/impetus/genimage-tftpboot.cfg"

You can now run make (no need to rebuild u-boot, just the SD Card image changes) to obtain a new sdcard.img that will always load the kernel and dtb from TFTP.

Setting up a TFTP server

To understand why this is needed, let’s look at what u-boot needs to do to boot via TFTP:

  • the ethernet0 interface needs to be detected (u-boot does this for you!)
  • a network configuration needs to be assigned to ethernet0 (a DHCP server will help)
  • a TFTP server needs to be found (again, DHCP helps!)
  • the kernel, dtb and bootscript need to be fetched and loaded to RAM
  • from now on, everything is the same as a regular boot

You will therefore need a DHCP server and TFTP server in your network. The easy option is to use dnsmasq, which is a FOSS DNS+DHCP+TFTP server. You get all you need in one go, so that is what we will do.

Assuming dnsmasq is already configured as a DHCP server (there are countless guides online, like this one), you just need 2 config lines to get dnsmasq to act as a tftp server:

# Enable dnsmasq's built-in TFTP server
enable-tftp

# Set the root directory for files available via TFTP.
tftp-root=/var/tftp

You can either add these to /etc/dnsmasq.conf or, if your installation supports that, you can add them as a separate file under /etc/dnsmasq.d/ A restart of dnsmasq will make it reload the config.

The last step is creating the directory from where dnsmasq will serve files:

sudo mkdir -p /var/tftp

Usage

You can now copy the boot files to that directory on the server. Since probably the TFTP server is not your development machine, the command to do that would look something like this:

scp <buildroot_root>/output/images/{boot.scr,sun8i-s3-elimo-initium.dtb,zImage} <user>@<tftp_server_host>:/var/tftp/

Make sure that <user> on <tftp_server_host> has the appropriate permissions to create files in /var/tftp/

You can now connect the Initium’s ethernet port to your network, insert the SD Card we created, power the board and …

U-Boot SPL 2020.10 (Nov 12 2020 - 15:24:12 +0000)
DRAM: 128 MiB
Trying to boot from MMC1


U-Boot 2020.10 (Nov 12 2020 - 15:24:12 +0000) Elimo Engineering

CPU:   Allwinner V3s (SUN8I 1681)
Model: Elimo Initium
DRAM:  128 MiB
MMC:   mmc@1c0f000: 0
Loading Environment from FAT... OK
In:    serial@1c28000
Out:   serial@1c28000
Err:   serial@1c28000
Net:   phy interface0
eth0: ethernet@1c30000
starting USB...
No working controllers found
Hit any key to stop autoboot:  0
ethernet@1c30000 Waiting for PHY auto negotiation to complete.... done
BOOTP broadcast 1
DHCP client bound to address 192.168.0.240 (30 ms)
Using ethernet@1c30000 device
TFTP from server 192.168.0.2; our IP address is 192.168.0.240
Filename '/boot.scr'.
Load address: 0x42000000
Loading: #
         22.5 KiB/s
done
Bytes transferred = 286 (11e hex)
Using ethernet@1c30000 device
TFTP from server 192.168.0.2; our IP address is 192.168.0.240
Filename 'boot.scr'.
Load address: 0x41900000
Loading: #
         30.3 KiB/s
done
Bytes transferred = 286 (11e hex)
Using ethernet@1c30000 device
TFTP from server 192.168.0.2; our IP address is 192.168.0.240
Filename 'zImage'.
Load address: 0x41000000
Loading: #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         ###################
         364.3 KiB/s
done
Bytes transferred = 5042560 (4cf180 hex)
Using ethernet@1c30000 device
TFTP from server 192.168.0.2; our IP address is 192.168.0.240
Filename 'sun8i-s3-elimo-initium.dtb'.
Load address: 0x41800000
Loading: #
         318.4 KiB/s
done
Bytes transferred = 10117 (2785 hex)
## Executing script at 41900000
Failed to load 'zImage'
Failed to load 'sun8i-s3-elimo-initium.dtb'
## Flattened Device Tree blob at 41800000
   Booting using the fdt blob at 0x41800000
   Loading Device Tree to 42dfa000, end 42dff784 ... OK

Starting kernel ...

[    0.000000] Booting Linux on physical CPU 0x0
[    0.000000] Linux version 5.10.0-rc1 (matteo@elimo) (arm-buildroot-linux-uclibcgnueabihf-gcc.br_real (Buildroot 2020.08-951-g8655eb2351-dirty) 9.3.0, GNU ld (GNU Binutils) 2.33.1) #1 SMP Thu Nov 12 12:45:48 GMT
 2020

From now on, to try one new kernel you just need to run the copy command above and reset the board. No more SD Card flashing!

Final note

You might have noticed the following errors in the boot sequence:

Failed to load 'zImage'
Failed to load 'sun8i-s3-elimo-initium.dtb'

These are harmless: it’s simply the bootscript trying to load the kernel and dtb from mmc. In our special build those are not there (they are provided via TFTP!) so the load fails. This would be easily fixed by removing the load mmc... lines from board/elimo/impetus/boot.cmd, but it would mean duplicating that file for the sake of aesthetics, sooo….