Roku HD1000: Network Block Device

Up: Roku HD1000 projects


Description

This is a Linux kernel module and some userspace support programs. Loading this module into the HD1000 creates a bunch of virtual /dev/nbd/* devices. You can then use the nbd-client to attach these virtual devices to a remote nbd-server. Because this works at the block device layer, the nbd devices appear to both userspace and the kernel as if they are local storage; you can mostly treat them as if they are local disk partitions, mostly.

WARNING

USE AT OWN RISK

I make no guarantees that this won't break under stress. I had to play some tricks to create it in the first place, and I have not tested it extensively. Especially when it comes to things like virtual swap devices, bugs can lead to deadlocks. The nbd module here is based on a version that is believed to be safe for swapping.

As this was not built against the exact kernel that is on the Roku, it could conceivably cause hard lockups, lost or corrupt data, or other problems. If you have any critical data on the Roku itself make sure you have a backup somewhere else if you load this module.


Example: swapping over the network

Say you want 100M of swap space on the Roku for development, and you don't have a big CF card handy. But you do have a Linux workstation somewhere with enough room for a 100M file. Create the file:

workstation$ dd if=/dev/zero of=swapfile bs=1048576 count=100

Format the file as swap space:

workstation$ mkswap swapfile

Then run an nbd server to make that swap file available over the network. Pick an available TCP port number (example here is 9944):

workstation$ nbd-server 9944 swapfile

On the Roku, load the module to make the nbd infrastructure available:

hd1000# insmod nbd.o

Set up one of the nbd devices on the Roku as a client of the workstation. I'm using /dev/nbd/0 here but it could just as easily be a different one such as /dev/nbd/27:

hd1000# nbd-client workstation 9944 /dev/nbd/0

Now /dev/nbd/0 on the Roku appears to be a 100M local device formatted as swap space. So just turn it on:

hd1000# swapon /dev/nbd/0

This works equally as well if swapfile is a disk partition on the workstation. I've been using a 512M logical volume myself. I'd expect it to also work with a sparse file, which would make the initial creation of the file go much faster.


Downloads

nbd.o-2004012501.mipsel.bz2 [51K]
nbd.o-2004012501.mipsel.gz [56K]
nbd.o-2004012501.mipsel [201K]

A precompiled nbd.o kernel module loadable into the Roku 1.5.11 firmware. PLEASE SEE WARNINGS ABOVE.

nbd-2.6-2004012501.mipsel.tar.bz2 [11K]
nbd-2.6-2004012501.mipsel.tar.gz [11K]
nbd-2.6-2004012501.mipsel.tar [41K]

nbd 2.6.0 userspace client and server tools, precompiled for the Roku 1.5.11 firmware.

nbd-2.6-2004012501.i386.tar.bz2 [10K]
nbd-2.6-2004012501.i386.tar.gz [10K]
nbd-2.6-2004012501.i386.tar [30M]

nbd 2.6.0 userspace client and server tools, precompiled for x86 Linux, glibc 2.2.3.

nbd-2.6.0.tar.gz [88K]

nbd userspace tool sourcecode. Authoritative source is sourceforge.net. I have a mirror here.

linux-2.4.18.tar.bz2 [24M]

Linux mainline kernel sourcecode, used to create the precompiled module. Authoritative source is kernel.org. I have a mirror here.

linux-2.4.23.tar.bz2 [29M]

Linux mainline kernel sourcecode, used to create the precompiled module. Authoritative source is kernel.org. I have a mirror here.


How I compiled the nbd module

As of this writing the Roku kernel doesn't ship with nbd support, and the nbd driver in the kernel version they're using wouldn't necessarily be safe anyway since there are some known races in older versions. Here is the procedure I used to create this module.

I did this back in December 2003, so some of the stuff here is a little out of date. Some notes on the setup:

First prep the Roku. We need the SDK:

on workstation, dir is /opt/roku/devel
  mkdir sdk
  cd sdk
  unzip RokuSDK_1.5.11.zip

I need my flash card for other purposes, and in any case at 32M it isn't big enough to hold the SDK. So I'll be running the SDK via the SMB share. This means the start script needs to be changed:

on workstation, dir is /opt/roku/devel/sdk
  mv MountSDK.roku MountSDK.roku.orig
  create MountSDK.roku
    #!/bin/sh
    losetup /dev/loop/0 /tmp/Volumes/dododge-roku/devel/sdk/rokudev.cramfs
    mount /dev/loop/0 /usr/share/rokudev -o ro
    killall taskview

on roku, dir is /tmp/Volumes/dododge-roku/devel/sdk
  ./MountSDK.roku

I know from working out this procedure that the Roku is unlikely to have enough memory to run the SDK. So I'll use my 32M CF card as swap. The card is partitioned with 1 partition, and had been formatted with FAT16. I originally experimented with making the entire partition a swap device, but then I found that there's no mkfs for FAT16 on the Roku, which makes reverting the card to a filesystem when I need to flash new firmware painful. So I'll use a swap file instead:

on roku
  mount -o remount,rw,noatime \
    /dev/ide/host0/bus0/target0/lun0/part1 /mnt/flash1
  dd if=/dev/zero of=/mnt/flash1/swapfile bs=1048576 count=30
  mkswap /mnt/flash1/swapfile
  swapon /mnt/flash1/swapfile

Since the Roku is using 2.4.18 plus ATI-only-knows-what patches, I'll use 2.4.18 as my baseline. But the nbd driver has an update in 2.4.19 which apparently makes it more usable for swapping, and the are yet further fixes of race conditions and other stuff in more recent revisions. So I'll unpack the 2.4.23 version of the nbd driver itself.

on workstation, dir is /opt/roku/devel
  bunzip2 < linux-2.4.18.tar.bz2 | tar xvf -
  mv linux linux-2.4.18
  bunzip2 < linux-2.4.23.tar.bz2 \
    | tar xvf - linux-2.4.23/drivers/block/nbd.c \
      linux-2.4.23/include/linux/nbd.h
  mv linux-2.4.23/include/linux/nbd.h linux-2.4.18/include/linux/nbd.h
  mv linux-2.4.23/drivers/block/nbd.c linux-2.4.18/drivers/block/nbd.c
  rm -rf linux-2.4.23
  chmod -R a+w linux-2.4.18

To get a basic configuration for the kernel, we'll start with the autoconf.h header that Roku has in their SDK. We know that this doesn't match their kernel, but the architecture settings should at least be close. Here's a perl script that can generate a .config file:

on workstation, dir is /opt/roku/devel
  create configfilt.pl
    $und={};
    $def={};
    $mod={};
    while(<>) {
      (m%\A#undef\s+(CONFIG_[A-Za-z0-9_]+)\s*\Z%
        && ($und->{$1}='n')) ||
      (m%\A#define\s+(CONFIG_[A-Za-z0-9_]+)_MODULE(\s+1)\s*\Z%
        && ($mod->{$1}='y')) ||
      (m%\A#define\s+(CONFIG_[A-Za-z0-9_]+)(\s+1)?\s*\Z%
        && ($def->{$1}='y')) ||
      (m%\A#define\s+(CONFIG_[A-Za-z0-9_]+)\s+\((.+)\)\s*\Z%
        && ($def->{$1}=$2)) ||
      (m%\A#define\s+(CONFIG_[A-Za-z0-9_]+)\s+(\S+)\s*\Z%
        && ($def->{$1}=$2));
    }
    foreach $key (keys %{$def}) {
      delete $und->{$key};
      delete $mod->{$key};
      print $key,"=",$def->{$key},"\n";
    }
    foreach $key (keys %{$mod}) {
      delete $und->{$key};
      print $key,"=m\n";
    }
    foreach $key (keys %{$und}) {
      print "# ",$key," is not set\n";
    }

Generate the .config file.

on roku, dir is /tmp/Volumes/dododge-roku/devel
  cp /usr/share/rokudev/usr/include/linux/autoconf.h .

on workstation, dir is /opt/roku/devel/linux-2.4.18
  perl -w ../configfilt.pl < ../autoconf.h > .config

We may also need to fake out some version information, so that insmod will be happy to load the resulting module. And we need to turn on enough stuff to get nbd to compile. Since the Roku uses devfs, we should turn that on; the module will then automatically create the /dev/nbd/* nodes.

on workstation, dir is /opt/roku/devel/linux-2.4.18
  edit Makefile
    EXTRA_VERSION = _dev-xilleon_x220
  make ARCH=mips menuconfig
    General setup
      + Networking support
    Block devices
      m Network block device support
    File systems
      + /dev files system support
      +   Automatically mount at boot

In this case menuconfig will also take care of things like version.h and the arch symlinks. But note that it believes we're cross-compiling, which will confuse the Roku. So get rid of that.

on workstation, dir is /opt/roku/devel/linux-2.4.18
  grep -v CROSSCOMPILE < .config > foo
  mv foo .config

We'll need a temporary directory that's got more space than Roku's tmpfs. I'll put that on the SMB share as well. This will be for things like gcc's intermediate files.

on workstation, dir is /opt/roku/devel
  mkdir tmp
  chmod 777 tmp

Now let's see if it'll compile:

on roku, dir is /tmp/Volumes/dododge-roku/devel/linux-2.4.18
  TMPDIR=/tmp/Volumes/dododge-roku/devel/tmp make modules

After a minute or so I have a "drivers/block/nbd.o", about 200k. Will it load?

on roku, dir is /tmp/Volumes/dododge-roku/devel/linux-2.4.18/drivers/block
  insmod nbd.o

No errors when loading, and I managed to scrape this out of dmesg before usb-storage spammed it out of the buffer:

nbd: registered device at major 43

Aside: USB drivers

I briefly looked at using the above procedure to compile a USB driver, but found it to be more complicated. The issue with USB is that it requires PCI support be enabled, and the kernel configuration code for MIPS automatically forcibly disables PCI support if you don't have architecture-specific configuration patches to turn it back on. And of course we do not [yet] have the Xilleon patches that would do this. You could certainly hack on the arch/mips/config.in file manually to work around this, but I haven't gone down that road yet so I don't know what other side effects might be in there. In any case knowing about this will probably save you some confusion if you try for a USB device.

Aside: enbd

If you've ever looked into nbd, you might have come across enbd as well, which is an enhanced network block device system. I did try to get enbd working, without success. The main problem is that gcc insisted on inserting calls to a _flush_cache function, which does not appear to exist in the HD1000 kernel. I don't know exactly why this was happening, but I believe it has something to do with trampolines. I tried various compiler flags without any luck. If you try it, and get the module to load, I'd be interested to hear how you did it.


Changes

2004012501

Initial release.


Dave Dodge dododge/at/dododge/dot/net