NFS root on SheevaPlug with Debian

I broke out my SheevaPlug the other day and decided to tinker with it a bit… recently I had some new projects ideas where it would be of use.  I also wanted to configure it with an NFS root to avoid flash wear issues and a way to easily back up the device.  This is an attempt at documenting the process in the hope it may help someone else with all the issues I ran into.  YMMV, damnum absque injuria, etc…

The plug had previously been sitting in my closet for some time, in fact it’s been so long that the Ubuntu Jaunty 9.04 install it came with was very outdated by this point.  Unfortunately the idea of upgrading to any more recent Ubuntu release will not work as they are compiled for newer ARM architectures; the CPU in the SheevaPlug is only an ARMv5 and 9.04 was the last version to support this core.  Ubuntu 9.10 was built for v6 and 10.04 and later target the Cortex ARMv7 core.

However we’re still in luck as Debian has an ARM port which supports Marvell’s Kirkwood platform.  And thanks to some wonderful work by Martin we have an easy to follow process for installing Squeeze on the SheevaPlug.  There’s a few caveats and issues to contend with and you will probably have to upgrade U-Boot (Marvell shipped their own variant which added support for plug computers but newer versions support it directly now).  This is all explained very well on Martin’s Debian installation page.  I recommend choosing the option for loading the installer via TFTP as you will need it later if you want to use NFS root.  I also initially installed Debian on an SD card, for some reason trying a USB drive did not work.

Once the installation is done, boot into your new Squeeze install and prepare an NFS mount on another box somewhere which will serve your rootfs.  I perused the DisklessUbuntuHowTo doc to get a basic idea of how it works, you’ll be doing something similar except the initrd update process will be different – this will be explained below.  Once your NFS share is created on the server side, mount it on the SheevaPlug and copy the files over (obviously replacing the IP’s as necessary):

mount -t nfs /mnt
cp -ax /. /mnt/.
cp -ax /boot/. /mnt/boot/.

The Ubuntu HOW-TO also lists copying /dev but this will not be needed as it is mounted via udev.  On my install /boot was a separate partition so make sure you include that as well.  Edit a few files in the new rootfs on the server-side to reflect a few things – /nfsroot/etc/network/interfaces to assign a static IP and /nfsroot/etc/fstab to replace your rootfs device with /dev/nfs.

You’ll also want to copy over the uImage and uInitrd files from /nfsroot/boot over to your TFTP folder as they’ll be needed to boot the plug.

Now comes the fun part, creating a new initrd.  Unfortunately the one installed by Debian did not support a NFS root so we need to build a new one.  Do this on the server-side as the new initrd will be copied to the TFTP folder as well.  The steps are relatively straight-forward but I recommend you read through the details first to get an better understanding.  The steps I outline are taken from a post on editing initrd’s and another on Debian SheevaPlug installation.

The process consists of the following: copy the kirkwood initrd image, extract it, update the initramfs.conf, re-compress, then rebuild it adding a special header for u-Boot.

~$ mkdir tmp

~$ cd tmp

~/tmp$ cp /home/exports/sheeva_squeeze_nfs_root/boot/initrd.img-2.6.32-5-kirkwood ./initrd.img-2.6.32-5-kirkwood.gz

~/tmp$ gunzip initrd.img-2.6.32-5-kirkwood.gz 

~/tmp$ mkdir initrd

~/tmp$ cd initrd/

~/tmp/initrd$ cpio -id < ../initrd.img-2.6.32-5-kirkwood
25329 blocks

~/tmp/initrd$ ls
bin  conf  etc  init  lib  sbin  scripts

At this point you’ll have the initrd extracted.  Edit the conf/initramfs.conf file and search for BOOT=local, change it to BOOT=nfs.

~/tmp/initrd$ vim conf/initramfs.conf

~/tmp/initrd$ find . | cpio --create --format='newc' > ../initrd.img-2.6.32-5-kirkwood-nfs
25329 blocks

~/tmp/initrd$ cd ..

~/tmp$ ls
initrd  initrd.img-2.6.32-5-kirkwood  initrd.img-2.6.32-5-kirkwood-nfs

~/tmp$ gzip -c initrd.img-2.6.32-5-kirkwood-nfs > initrd.img-2.6.32-5-kirkwood-nfs.gz

~/tmp$ ls
initrd  initrd.img-2.6.32-5-kirkwood  initrd.img-2.6.32-5-kirkwood-nfs  initrd.img-2.6.32-5-kirkwood-nfs.gz

~/tmp$ mkimage -A arm -O linux -T ramdisk -C gzip -a 0x00000000 -e 0x00000000 -n 'Debian ramdisk' -d initrd.img-2.6.32-5-kirkwood-nfs.gz initrd.img-2.6.32-5-kirkwood-nfs
Image Name:   Debian ramdisk
Created:      Thu Oct 20 20:43:31 2011
Image Type:   ARM Linux RAMDisk Image (gzip compressed)
Data Size:    5473115 Bytes = 5344.84 kB = 5.22 MB
Load Address: 0x00000000
Entry Point:  0x00000000

~/tmp$ ls
initrd  initrd.img-2.6.32-5-kirkwood  initrd.img-2.6.32-5-kirkwood-nfs  initrd.img-2.6.32-5-kirkwood-nfs.gz

~/tmp$ cp initrd.img-2.6.32-5-kirkwood-nfs /var/lib/tftpboot/uImage-nfsroot

Now that you have the new initrd created you’ll need to update your uBoot environment variables.  This can be quite complex in-and-of itself.  The previous u-Boot environment variables I had did not work with this new setup, my attempts at abstracting away most of the complexity into many variables did not seem to work, there were many instances where variables referenced in commands and other variables were not getting set properly.  I basically re-worked it from the ground up using sample material at DENX’s (developers of u-Boot) website.  Their manual has a lot of information, you’ll probably want to reference the CommandLineParsing, UBootCmdGroupEnvironment, and LinuxNfsRoot pages primarily.  The working copy of my u-Boot environment is listed below:

addip=setenv bootargs ${bootargs} ip=${ipaddr}:${serverip}:${gatewayip}:${netmask}:${hostname}:${netdev}:off panic=1
addmisc=setenv bootargs ${bootargs}
addtty=setenv bootargs ${bootargs} console=ttyS0,${baudrate}
boot_mmc=setenv bootargs $(bootargs_console); run bootcmd_mmc; bootm ${kernel_addr_r} ${fdt_addr_r}
boot_nfs=tftpboot ${kernel_addr_r} ${bootfile}; tftpboot ${fdt_addr_r} ${fdt_file}; run nfsargs addip addtty; bootm ${kernel_addr_r} ${fdt_addr_r}
bootargs=root=/dev/nfs rw nfsroot= ip= panic=1 console=ttyS0,115200
bootargs_console=console=ttyS0,115200 mtdparts=orion_nand:0x00100000@0x00000000(uBoot)ro,0x00400000@0x00100000(uImage),0x1fb00000@0x00500000(rootfs)
bootargs_nfs=$(bootargs_console) root=/dev/nfs rw nfsroot=$(serverip):$(rootpath) ip=$(ipaddr):$(serverip):$(gateway):$(netmask):sheeva:eth0:none
bootcmd=run boot_nfs
bootcmd_mmc=mmc init; ext2load mmc 0:1 0x00800000 /uImage; ext2load mmc 0:1 0x01100000 /uInitrd
bootcmd_nfs=tftpboot ${kernel_addr_r} ${bootfile}; tftpboot ${fdt_addr_r} ${fdt_file}
console=console=ttyS0,115200 mtdparts=nand_mtd:0x00100000@0x00000000(uBoot)ro,0x00400000@0x00100000(uImage),0x1fb00000@0x00500000(rootfs)
nfsargs=setenv bootargs root=/dev/nfs rw nfsroot=${serverip}:${rootpath}
x_bootargs=console=ttyS0,115200 mtdparts=orion_nand:512k(uboot),4m@1m(kernel),507m@5m(rootfs) rw
x_bootargs_root=ubi.mtd=2 root=ubi0:rootfs rootfstype=ubifs
x_bootcmd_kernel=nand read 0x6400000 0x100000 0x400000
x_bootcmd_sata=ide reset;
x_bootcmd_usb=usb start;

By default it’s set to boot from NFS via the bootcmd=run boot_nfs line.  If you’re having issues getting NFS root to work, you may want to remove the panic=1 from the addip variable, this way it can drop you to an initrd prompt.  The boot_mmc variable is adapted from Martin’s example.  You may need to modify these slightly if your SheevaPlug is different, specifically the bootargs_console variable – mtdparts reflects the flash partition setup on my plug, update if yours differs (I think these are the factory default settings), /proc/mtd will have this information in it if you boot again from the Debian install.  Also I changed it to orion_nand, I believe it used to be orion_mtd initially.

That’s basically it.   Hope this is of use to someone!

SheevaPlug Basics + MultiBoot

I thought documenting my notes and what I’ve leared about the SheevaPlug so far may be useful so I’m putting together a basic HOW-TO.  The documentation from GlobalScale Technology is a great place to start (docs on their site no longer seem to be there, check wiki link posted below).

Firstly, it will likely come pre-installed with Ubuntu Jaunty Beta, although possibly Gentoo.  Both can run on the plug in addition to FreeBSD.  Wiki explains more.  It also has a good entry on configuring the plug to boot from an SD card rather then the onboard NAND flash.

The forums are a very useful resource as well:

Configuring the USB serial console (Linux & Windows).  My Ubuntu desktop failed to create a /dev/ttyUSB1 on it’s own when plugged in, a simple ‘modprobe ftdi_sio product=0x9e8f vendor=0x9e88’ resolved that.

Give care when flashing partitions or images and make sure you don’t overwrite /dev/mtd0 or /dev/mtdblock0 (1st MB of NAND), it holds the uBoot boot image, the plug cannot boot without it.  Next partition is the Linux kernel image (~1-4mb), and finally a root filesystem image (remainder of the 512MB).

I somehow managed to brick mine at one point, after a fsck and reboot I got a “Verifying Checksum … Bad Data CRC” error and it would not boot.  Playing with it a bit, I managed to get it to boot by tftp’ing a kernel image and mount a root filesystem via NFS.  GST’s documentation and the wiki provide plenty of guidance.  I also had issues getting it to re-flash the kernel image to /dev/mtd1 because the kernel image provided by GST was slightly too big for the default size of mtd1.  Since then I’ve read they have released a new kernel image that fits, but at the time I simply increased the size of mtd1 (to 4mb rather then the default 1mb) by editing the mtdparts setting in uBoot.

It also occured to me that coming up with a makeshift bootmenu might be useful for debugging:

set console 'console=ttyS0,115200 mtdparts=nand_mtd:0x00100000@0x00000000(uBoot)ro,0x00400000@0x00100000(uImage),0x1fb00000@0x00500000(rootfs)'

set boottftp 'tftpboot 0x2000000 $(image_name)'
set bootargs_nfs 'root=/dev/nfs rw'
set boot_nfs 'run boottftp;setenv bootargs $(console) $(bootargs_nfs) nfsroot=$(serverip):$(rootpath) ip=$(ipaddr):$(serverip)$(bootargs_end);bootm 0x2000000'

set bootnand 'nand read.e 0x2000000 0x00100000 0x00500000'
set bootargs_nand 'root=/dev/mtdblock2 rw'
set boot_nand 'run bootnand;setenv bootargs $(console) $(bootargs_nand) ip=$(ipaddr):$(serverip)$(bootargs_end);bootm 0x2000000'

set bootargs_sd 'root=/dev/mmcblk0p1 rw'
set boot_sd 'run bootnand;setenv bootargs $(console) $(bootargs_sd) ip=$(ipaddr):$(serverip)$(bootargs_end);bootm 0x2000000'

set bootcmd 'run boot_sd;'

As I mentioned, the mtdparts I provided are configured for a 4mb mtd1 partition, you may need to modify this.  This thread explains mtdparts better.  All of the other variables such as IP, SERVERIP, etc need to be configured in addition to these listed here.  You will also see it’s set to boot from SD card by default, it can be changed to ‘run boot_nand’ or ‘run boot_nfs’ if you like.  Also, the uBoot quick reference page might be use of to you as well.

DIY Radar & SheevaPlug

So I’ve decided to disable my weekly Twitter updates after seeing how annoying it would be to read.  I’ve been working on a few things lately, first is a “DIY radar” weather warning system with the help of NOAA WSR-88D radar data, libGD, and Perl.  I recall some very basic image processing algorithms and code I tinkered with in BASIC when I was much younger, but it was very simplistic and this was ages ago.  It’s fun to catch up on some old programming interests.  I’ll throw a post about it together once the code is a bit more complete.

My Linux-based SheevaPlug (embedded Linux in a wall-wart) has been occupying some time as well, tinkering with the Jaunty install and NFS and SSH.  I’m pretty impressed with the capabilities of this so far, despite the fact I managed to partially brick it for awhile. I eventually want to build a Tweet-A-Watt and use my plug for interfacing.  Wiring my 1-wire weather station to the plug makes sense as well.