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

Needed host-side software packages: xinetd; nfs-kernel-server; nfs-common; nfswatch

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

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

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

Now create a /tftpboot directly 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.

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 config NFS:

# /etc/exports: the access control list for filesystems which may be exported
#		to NFS clients.  See exports(5).

Here /tftpboot is the default directory that xinetd exposes to the target and 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.

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

Now to set our desired static IP address on the host. The easiest way is use NetworkManager's nmcli. Here "SabreSD" is the name of the connection and is my elected IP address:

<user@localhost>$ nmcli con add con-name SabreSD ifname eth0 type ethernet ip4
<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'.

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.


Also choose one of these:

Target-side configuration

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

bootargs_net=console=${console},${baudrate} ip=${ipaddr} root=/dev/nfs nfsroot=${serverip}:${rootfs} rootwait rw
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.

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

For debugging purposes, try pinging the laptop from the U-boot prompt:

U-Boot > ping
Using FEC device
host is alive
U-Boot > 
<user@localhost>$ sudo rfkill block all
<user@localhost>$ ping
Network is unreachable
<user@localhost>$ ping -I eth0
PING ( 56(84) bytes of data.
64 bytes from 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 disabled 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 Chaiken)