Friday, June 15, 2012

Once upon a time I had a blog that I used rather irregularly (the last post from then was over four years ago). At some point I may try to import those old posts, but in the mean time I think it's time for something new. Last Tuesday I received my Raspberry Pi in the mail, which I have been gleefully toying with since. Giving the number of different configurations I've already toyed with, I decided I ought to document some of what I've done.

My first attempt was to reproduce my HTPC configuration, a Debian-based box running XBMC on Nvidia ION hardware. There are plenty of ways to get XBMC running on such a system (it's x86 after all) and there are plenty of distributions even built for such a thing. However, my setup has some unique requirements.




1) The HTPC is diskless. There were a few reasons for this:
  a) The original mainboard I used for my HTPC didn't even have a CPU fan, so the entire setup had no moving parts (which means zero-noise). My current mainboard has a third-party low-noise fan which you can't even hear even when you put your ear up to the case.
  b) Media is already being served off a fileshare server hidden away in my closet. Why not take advantage of the network fileshare?
  c) It's cheaper than buying a disk for the HTPC.

So all that being said, the HTPC needed to be configured to PXE boot (ok, just punch the setting in the BIOS and get my OpenWRT router configured) and then use an NFS root filesystem.

This also means that if I ever want another HTPC (for a different room, or if I upgrade as I did not too long ago) I don't need to do a single bit of reconfiguration or reinstallation. I just plug it into the network, and the HTPC boots the working NFS root like a boss.


2) I want the HTPC to be stateless. That is, if I'm playing with XBMC and break everything but futzing with the settings too much, a reboot should reset it to "factory" working settings.

I accomplished this by using the same tool the live-images use. I can use a union filesystem to lay a read-write ramdisk on top of the read-only NFS root, so any changes are (intentionally) lost on reboot.


So before I dig into how I got this working on the RPi, let's first talk about how I got this working on my x86 HTPC.

Step 1: Build the NFS root system
As already mentioned, I already have a file server setup to host the NFS root. To get the filesystem setup, just use debootstrap:

#debootstrap squeeze /exports/nfsroot

We'll need to make sure we've got an appropriate kernel/initrd for the system to boot, so go ahead and chroot and get that setup:

#cp /etc/apt/source.list /exports/nfsroot/etc/apt/
#chroot /exports/nfsroot
#apt-get update
#apt-get install linux-image-2.6
#exit


Now copy the resulting images to your TFTP root:

#cp /exports/nfsroot/boot/vmlinuz* /var/lib/tftpboot/kernel
#cp /exports/nfsroot/boot/initrd* /var/lib/tftpboot/initrd


And finally add a pxelinux configuration:
#echo <<EOF > /var/lib/tftpboot/pxelinux.cfg/default

label linux
kernel kernel
append initrd=initrd ro quiet nfsroot=192.168.73.10:/exports/nfsroot vga=788

EOF


Assuming you've got your DHCP server, NFS server, and TFTP server configured appropriately, this should be enough to get you booted into an NFS root system. Once you've got that tested and working, you can proceed to install all the packages you need (install xorg, add the deb-multimedia repository and install xbmc, etc.).


Step 2: Reconfigure as a "live" filesystem
Now that we've got the HTPC booting with the NFS root, let's update the configuration to support a read-only share and lay a read-write ramdisk over it. I opt to use Aufs for this since it's included as a module in the stock Debian squeeze kernel package (in earlier versions of Debian it was also available via module-assistant).

The union filesystem layering will be done in the initrd after the NFS root is mounted, so I added the following script to the directory containing scripts run after the root filesystem is mounted:
#echo << EOF > /exports/nfsroot/usr/share/initramfs-tools/scripts/init-bottom/unionfs
#!/bin/sh -e

case $1 in
  prereqs)
    exit 0
    ;;
esac

for x in $(cat /proc/cmdline); do
  case $x in
    root=*)
      ROOTNAME=${x#root=}
      ;;
    aufs=*)
      UNION=${x#aufs=}
        case $UNION in
          LABEL=*)
            UNION="/dev/disk/by-label/${UNION#LABEL=}"
            ;;
          UUID=*)
            UNION="/dev/disk/by-uuid/${UNION#UUID=}"
            ;;
        esac   
      ;;
  esac
done

if [ -z "$UNION" ]; then
    exit 0
fi

modprobe aufs

# make the mount points on the init root file system
mkdir /aufs /ro /rw

# mount read-write file system
if [ "$UNION" = "tmpfs" ]; then
  mount -t tmpfs rw /rw -o noatime,mode=0755
else
  mount $UNION /rw -o noatime
fi

# move real root out of the way
mount --move ${rootmnt} /ro

mount -t aufs aufs /aufs -o noatime,dirs=/rw:/ro=ro

# test for mount points on union file system
[ -d /aufs/ro ] || mkdir /aufs/ro
[ -d /aufs/rw ] || mkdir /aufs/rw

mount --move /ro /aufs/ro
mount --move /rw /aufs/rw

# strip fstab off of root partition
if [ ! -z "$ROOTNAME" ]; then
    grep -v $ROOTNAME /aufs/ro/etc/fstab > /aufs/etc/fstab
fi

mount --move /aufs /root

exit 0

EOF


This allows you to pass an "aufs=FOO" argument to the kernel command line, and have it mount FOO as a read-write filesystem on top of the true root. In our case, we'll pass "aufs=tmpfs" to have the initrd mount a ramdisk over the true root fs.

Since we'll need the aufs module to run this script, update the initramfs-tools configuration to explicitly include this module:
#echo aufs >> /exports/nfsroot/etc/initramfs-tools/modules

Now chroot and rebuild the initrd:
#chroot /exports/nfsroot
#update-initramfs -u
#exit


Copy the new initrd to your tftpboot root:
#cp /export/nfsroot/boot/initrd* /var/lib/tftpboot/initrd

And update your pxelinux configuration to add aufs=tmpfs to the append line:
#perl -p -i -e 's/^append /append aufs=tmpfs/' /var/lib/tftpboot/pxelinux.cfg/default

Step 3: Dealing with peculiarities
The only particular problem I encountered with this process (beyond the usual system setup) is that when using an NFS root filesystem, my additional NFS shares weren't automatically mounted (in particular the share with all the video files I want XBMC to play) even though they were listed in /etc/fstab.

The reason for this is that NFS shares are brought online by the init system as part of the if-up scripts, since it doesn't make sense to try and mount them until the network interfaces are brought up. However, when using an NFS root, these scripts aren't run since the network interface is already up by the time we get into the root filesystem.

This has a simple solution, which is the following:
#echo ASYNCMOUNTNFS=no >> /exports/nfsroot/etc/default/rcS

This tells the init system that it should not expect NFS shares to be asynchronously mounted by the if-up scripts and that the init scripts should bring them up itself.


Ok, that's enough for today. Next time, how to get the Raspberry Pi to do the same tricks.

1 comment:

  1. Melknin, Thank you for these information however I am not clear if I should be opening up chrome on my mac or my ipad? Will it work with the latest MLP version (Winter theme)? I am such a novice on the computer and scared that I will erase my game on my ipad. In addition, should I download I-fun app on the ipad or the computer? Sorry if my questions are lame, just trying to figure this game out for my daughter. We've spend so much money on this dam game! I trust you understand. Any help that you can provided will be so appreciated! BTW, you're pretty brilliant

    ReplyDelete