NFS- and tftp-booting target boards with Linux and U-boot

Prepared by Alison Chaikenshe-devel logo and offered under Creative Commons logo


Goal: Configure a target running u-boot to retrieve files via TFTP and a laptop to serve them, both using static IP addresses for convenience. The command output shown here comes from a Freescale SabreSD board running U-boot 2013.04 and connected to a laptop running Debian, but the instructions are far more general, although x86-based boards and Linux kernels before 3.0 do not make use of device-tree.

Hardware set-up

Connect an appropriate cable between the console port (often a DB9 or USB connector) of your target and a USB connector on your laptop. On Linux, the USB connection should appear at /dev/ttyUSB0. By default, this device is not accessible to non-root users. Fix this problem with:

<user@localhost>$ sudo addgroup $USER dialout

Then connect a LAN cable between the laptop and the target, either using a crossover cable or by interposing a network switch. The LED next to the Ethernet connector on the board should light up.

Host-side configuration

nmtui
connection list
NetworkManager's nmtui shows the new laptop static IP connection under the "Wired" heading.

First, set the static IP address on the host with which it will communicate with the board. The easiest way is use NetworkManager's nmcli. Here "SabreSD" is the name of the connection and 172.17.0.5 is my elected IP address:

<user@localhost>$ nmcli con add con-name SabreSD ifname eth0 type ethernet ip4 172.17.0.5/16
<user@localhost>$ nmcli con show SabreSD

The second command shows the settings for the new connection, which now can be selected from nmtui ncurses configurator (see figure) or your favorite NetworkManager GUI. Alternatively, you can use a script to put your other wired connection down and choose the new static IP connection:

<user@localhost>$ nmcli con down Wired\ connection\ 1
<user@localhost>$ nmcli con up SabreSD            
Connection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/19)

When finished with your session connected to the board, use ntmui or nmcli to restore the default 'Wired connection 1'.

Test the network connection from board to host by pinging the laptop from the U-boot prompt:

U-Boot > ping 172.17.0.5
Using FEC device
host 172.17.0.5 is alive
U-Boot > 

Now create a /tftpboot directory and copy your root filesystem, kernel and device-tree there. U-boot follows symbolic links, so a lot of developers create a symlink to the device-tree and kernel files so that the U-boot environment variables (see below) need not ever change.

Now install needed host-side software packages: inetd; nfs-kernel-server; nfs-common; nfswatch

Configure inetd by adding a line to /etc/inetd.conf:

172.17.0.5:tftp    dgram   udp   wait   nobody  /usr/sbin/tcpd  in.tftpd /tftpboot

Here 172.17.0.5 is the address on which the laptop will listen for tftp connections. Restart inetd so that it uses the new IP address and not that of other interfaces:

<user@localhost>$ sudo systemctl restart inetd

Now your server should be listening for tftp connections:

<user@localhost>$ netstat -l | grep tftp
udp        0      0 172.17.0.5:tftp         0.0.0.0:* 

Debugging tftp: the steps so far start tftp and listen on a port, but will not necessarily allows connections depending on local network configuration. When 'ping 172.17.0.5' works from the u-boot, but tftp transfers time out, check what 'tcpdump -i eth0' says:

<user@localhost>$ sudo tcpdump -i eth0
07:57:06.212950 IP 172.17.0.1.1071 > 172.17.0.5.tftp:  53 RRQ "/tftpboot/uImage-imx6" octet timeout 5 blksize 1468
07:57:06.212981 IP 172.17.0.5 > 172.17.0.1: ICMP host 172.17.0.5 unreachable - admin prohibited, length 89

"admin prohibited" means that security is preventing the tftp connection. First add the board's IP address (here 172.17.0.1) to /etc/hosts.allow if needed. Then add rules to /etc/iptables rules following the advice at StackExchange

Opening tftp port with iptables
Modify iptables rules to allow inetd to answer connections for tftp, then reload the rules.

Another tftp-debugging tip: if tftp fails and your board is connected via USB adapter, type 'usb start' at the U-boot prompt and try again.

Optional: tell systemd to start inetd and the NFS server at boot:

<user@localhost>$ systemctl enable nfs-kernel-server
<user@localhost>$ systemctl enable inetd

Note that inetd and NFS are potentially security risks if configured improperly. Do not expose your $HOME via inetd! Do not configure NFS to serve files from /!

What you've done so far will enable the board to access files in /tftpboot via tftp, but will not tell NFS to make the root filesystem available for mounting. Edit the /etc/exports file to configure NFS:

# /etc/exports: the access control list for filesystems which may be exported
#		to NFS clients.  See exports(5).
/tftpboot       172.17.0.1(rw,sync,no_subtree_check,no_root_squash)

Here /tftpboot is the default directory that inetd exposes to the target and 172.17.0.1 is the IPv4 address I've selected for it. The parameter no_root_squash is optional and permits root to login to the target without a password. Once you've modified /etc/exports, type

<user@localhost>$ sudo systemctl restart nfs-kernel-server

if the server is already running so that it will re-read its configuration file.

Configuring the kernel

The following kernel options are necessary as of Linux 4.1 to make mounting the root filesystem via NFS work. Note static compilation (=y) since loading of modules will not work until the filesystem is mounted.

CONFIG_KERNFS=y
CONFIG_NFS_FS=y
CONFIG_NFS_V2=y
CONFIG_NFS_V3=y
CONFIG_NFS_V3_ACL=y
CONFIG_NFS_V4=y
CONFIG_ROOT_NFS=y
CONFIG_NFS_USE_KERNEL_DNS=y
CONFIG_NFS_ACL_SUPPORT=y
CONFIG_NFS_COMMON=y
CONFIG_IP_PNP=y

Also choose one of these:

CONFIG_IP_PNP_DHCP
CONFIG_IP_PNP_BOOTP
CONFIG_IP_PNP_RARP
Target-side configuration

The standard U-boot parameters needed to get this set-up working:

bootdelay=10
bootargs_net=console=${console},${baudrate} ip=${ipaddr} root=/dev/nfs nfsroot=${serverip}:${rootfs} rootwait rw
ethact=FEC
ethprime=FEC
fdt_high=0xffffffff
ip_dyn=no
netload=tftp ${fdt_addr} ${dtbfile};tftp ${loadaddr} ${uimage}
netboot=setenv bootargs ${bootargs_net};run netload;bootm ${loadaddr} - ${fdt_addr}

Implementation-specific parameters: console, baudrate, ipaddr, serverip, rootfs, fdt_addr, dtbfile, loadaddr and uimage. Pick values appropriate for your architecture and filesystem layout. Console generally matches "tty*", baudrate is typically 115200, and ipaddr and serverip are static IPv4 addresses (i.e., ones that don't start with 192 or 127). Any well-formed address can be chosen for your devices if neither is connected to an external network. The pathnames in dtbfile and uimage variables are relative to /tftpboot, if that's where the files are located.

Don't forget to enable sshd on your target:

<user@localhost>$ systemctl enable sshd.socket
<user@localhost>$ systemctl start sshd.socket

Note that you start the socket, not "systemctl start sshd@0.service". The way systemd's socket-based activation works, the listener on the socket will start the service automagically when a client tries to connect.

(Finally) Bringing the Board Up

With these parameters, 'run netboot' at the U-boot prompt should bring the board up. The most common mistake is to omit 'ip_dyn=no'. If you do, tftp will work fine, but then the board will get a dynamic address when the Linux kernel starts. Note that failure to edit /etc/exports or to restart nfs-kernel-server after editing /etc/exports also mean that tftp will start, but NFS will fail.

Once the board boots, ping it from Linux:

<user@localhost>$ ping 172.17.0.1
Network is unreachable
<user@localhost>$ ping -I eth0 172.17.0.1
PING 172.17.0.1 (172.17.0.1) 56(84) bytes of data.
64 bytes from 172.17.0.1: icmp_seq=1 ttl=64 time=0.372 ms

'ping -I eth0' insures that the laptop tries to use the hard-cabled interface. I've seen ping report 'Network is unreachable' when wlan0 is enabled and eth0 is plugged in. U-boot can send pings but does not respond, so it's necessary to boot the board to ping the other way.

offered under the Creative Commons
      Attribution-ShareAlike 3.0 license offered under
      the Gnu Public License v3 Valid XHTML 1.0! Valid CSS!
alison@she-devel.com (Alison Chaiken)