Wednesday, April 9, 2014

TFTP and NFS Debian on Beaglebone Black

Overview

Back when I was working with kernel 3.2 and the TI Linux SDK, I had the Beaglebone booting to a NFS. I thought it would be nice to do that again, because it is very convenient for development purposes.

Downloads

Get this if you don't already have a micro SD card with an image on it:
Debian eMMC Flasher (can also be used as a live system): Official Image, Other Versions


Background

I will break up the configuration into three parts:
  • TFTP
  • NFS
  • Local bootstrap



TFTP

TFTP is a popular method of booting IP phones, routers, and net boot capable PCs. It provides a simple way to download files to clients. TFTP requires a server to be running somewhere on the network, though. This can be a Linux system or your fancy home router (DD-WRT, tomato, or other custom firmware).

TFTP is easy to install on an Ubuntu host:
> sudo apt-get install xinetd tftpd tftp

Unlike most installs, this does not create a default configuration. So you have to create the following:
> sudo nano /etc/xinetd.d/tftp

and enter the following into the file:
service tftp
{
protocol = udp
port = 69
socket_type = dgram
wait = yes
user = nobody
server = /usr/sbin/in.tftpd
server_args = /tftpboot
disable = no
}

This will configure the service to reside on port 69 and serve files from the root folder /tftpboot. Since /tftpboot doesn't exist, let's create this folder now, populate it with boot binaries, set permissions, and restart the service:
> sudo mkdir /tftpboot
> sudo chmod -R 777 /tftpboot
> sudo chown -R nobody /tftpboot
> sudo /etc/init.d/xinetd restart


NFS

NFS stands for network file system. It has been around a while (the 80's) and is easy to setup on an existing Linux host.

First we have to install the NFS server on our host:
> sudo apt-get install nfs-kernel-server

Next we need to populate a root filesystem. You could use the image-builder scripts from Robert C Nelson, but those require lots of harddrive space and time. A faster way is to copy an already generated image. I chose to use a micro SD card with the eMMC image because I had one laying around.
  1. Use a card reader to mount the SD card in the Ubuntu host.
  2. Copy the root file system to a host folder and copy boot files to our TFTP folder:
> sudo   cp -a   /media/eMMC-Flasher/*   /home/programmer/bbb_nfs_root
> sudo  cp  /media/BEAGLE_BONE/initrd.img   /tftpboot
> sudo  cp  /media/BEAGLE_BONE/zImage   /tftpboot
> sudo  cp  /media/BEAGLE_BONE/dtbs/am335x-boneblack.dtb   /tftpboot

You may have to change the am335x-boneblack.dtb file to match your system, for example a BBW. Browse the dtbs folder for options.

Once you have a full rootfs folder, you now need to tell the server where to look by modifying the /etc/exports file:
> sudo nano /etc/exports

Add this line (modify the path to point to your rootfs):
/home/programmer/bbb_nfs_root *(rw,nohide,insecure,no_subtree_check,async,no_root_squash)

NOTE: Ensure that there is a space between rootfs and *, but no space between * and (rw,nohi.....

Save and exit, and then restart the NFS server, so our folder will get served up:
> sudo /etc/init.d/nfs-kernel-server restart


Local Bootstrap

Now that the server is configured, we need to tell the Beaglebone to boot from the network.

First, a little background on the Beaglebone filesystem. The AM335x can get its initial image from a number of sources. The Beaglebone White used a uSD card, while the Beaglebone Black can use a uSD card or the on-board eMMC. Either way, the partitioning is the same: one small FAT boot partition and an EXT root partition that fills up the rest of the space.

The Beaglebone Linux images use Das uBoot as their bootloader (versus LILO or GRUB found on typical desktop systems). uBoot takes care of initializing low level hardware and provides filesystem drivers to aide in booting the main operating system. On the Beaglebones, uBoot can be run interactively, through the console serial port, or it can be scripted through a file called uEnv.txt. So, in order to boot a NFS, all we need to do is create a specially crafted uEnv.txt file.

The easiest way to modify the uEnv.txt file is to use a USB connection to a host PC. Any host will work, as we only need to modify the files that mount automatically as a mass storage device. For this to work, though, you need to have an initial image booting the Beaglebone. Any image (Angstrom/Debian/etc) will work, as long as it uses uBoot and uEnv.txt.

I highly recommend using a micro SD card image until you get the uEnv.txt settings nailed down. It is much easier to use a card reader, to modify the files on a host, than it is to try to get the Beaglebone booting again (it needs to boot in order to start the mass storage driver). If you still insist on using the eMMC, you should be familiar with the serial console, because you are going to need it.

After several failed attempts, here is my golden uEnv.txt file. Change the IP addresses and rootpath to match your configuration. I could not get DHCP to work, so I assigned ipaddr to be outside of the range of my DHCP server. This IP is only used for the TFTP process and once the operating system is up and running, it will reinitialize the Ethernet and get a DHCP address.
serverip=192.168.1.100
ipaddr=192.168.1.55
rootpath=/home/programmer/bbb_nfs_root

# Choose one...
#mmcORtftp=load mmc ${mmcdev}:${mmcpart}
mmcORtftp=tftp

# Original Configs
loadaddr=0x80300000
initrd_addr=0x81600000
fdtaddr=0x815f0000
initrd_high=0xffffffff
fdt_high=0xffffffff

systemd=quiet init=/lib/systemd/systemd
mmcrootfstype=ext4 rootwait fixrtc
console=ttyO0,115200n8
kernel_file=zImage
initrd_file=initrd.img

loadkernel=${mmcORtftp} ${loadaddr} ${kernel_file}
loadinitrd=${mmcORtftp} ${initrd_addr} ${initrd_file}; setenv initrd_size \ ${filesize}
loadfdt=${mmcORtftp} ${fdtaddr} ${fdtfile}
loadfiles=run loadkernel; run loadinitrd; run loadfdt

mmcargs=setenv bootargs console=${console} root=/dev/nfs \ nfsroot=${serverip}:${rootpath},nolock ${systemd}
uenvcmd=run loadfiles; run mmcargs; bootz ${loadaddr} \ ${initrd_addr}:${initrd_size} ${fdtaddr}

Enjoy testing new Kernels, unzipping large files with the host processor, and taking advantage of the massive storage capacity of your host hard drive :)


Sources:
http://processors.wiki.ti.com/index.php/Booting_Linux_kernel_using_U-Boot
http://processors.wiki.ti.com/index.php/Alternate_Boot_Methods_for_OMAP-L137/DA830#Mounting_Root_File_System
http://askubuntu.com/questions/201505/how-do-i-install-and-run-a-tftp-server

2 comments:

  1. Sadly this does not appear to work with the latest debian build.
    I thought It was due to changes in the adresses used in the latest uEnv.txt which now reads;
    loadaddr=0x82000000
    initrd_addr=0x88080000
    fdtaddr=0x88000000
    But making these changes did not help.
    The SD card boot hangs with 4 leds on and the mmc boot boots into the mmc, ignoring both the SD card and the NFS instructions. I've checked the NFS server by mounting it from a working SD boot as follows (with your parameters substituted)
    sudo mount 192.168.10.10:/home/programmer/bbb_nfs_root /mnt/nfs
    ls /mnt/nfs
    bin boot dev etc home lib lost+found media mnt opt ...
    Any ideas where I have gone wrong?
    Thanks

    ReplyDelete
  2. I know this is old but still very useful. Simple changes will make it work with Debian 9.x. To copy files you need the following in /tftpboot from the sd card. /rootfs/boot/vmlinuz-[build number], /rootfs/boot/initrd.img-[build number], and the directory /rootfs/boot/dtbs. Then copy the entire rootfs directory to the bbb_nfs_root directory using sudo cp -a. For the uEnv.txt file on the BBB you only need the following:
    client_ip=172.16.0.72
    server_ip=172.16.100.50
    #gw_ip=172.16.0.1
    netmask=255.255.0.0
    hostname=BBB
    device=eth0
    autoconf=off
    root_dir=/home/[username]/programmer/bbb_nfs_root/rootfs
    nfs_options=,vers=3
    jfsrootfstype=ext4 rootwait fixrtc
    nfsroot=${server_ip}:${root_dir}${nfs_options}
    uname_r=4.14.108-ti-r113
    #uuid=
    dtb=am335x-boneblack-uboot-univ.dtb

    ReplyDelete