Peters Homepage
Menu
News
Me
Articles
University
Software
HMT
Screenshots

Valid HTML 4.01!

Valid CSS!

Installing debian Sarge on a Linksys NSLU2

Last updated: 09/10/2005


Debian on Linksys NSLU2

Installing Debian Sarge on a Linksys NSLU2

Index

News

08/11/2006: The NSLU2 is now offically supported by Debian-installer. See http://www.cyrius.com/debian/nslu2/ for details
09/10/2005: Serial port no longer needed. See SerialLessInstallation for details
01/10/2005: Builtin ethernet works in little endian mode! See nslu2-developers. With a bit more work the USB adaptor will no longer be needed.

Introduction

This document describes how to install Debian Sarge on the Linksys NSLU2. The Linksys NSLU2 is a small (130x21x91 mm) and cheap (~75€) consumer NAS device. Hardware specs are:
  • Intel IXP420 (ARMv5TE) CPU running at 266MHz (*)
  • 32MB SDRAM
  • 8MB NOR flash
  • 10/100 ethernet (builtin)
  • 2x USB 2.0 host
NSLU2 board
See CPUOverview and PhotosOfTheInternals from the nslu2-linux.org wiki for more details.

Notice that the NSLU2 is underclocked to 133MHz from the factory, but this can easily be resolved by removing a resistor. See OverClockTheSlug for details.

The original Linksys software is based on a 2.4.22 Linux kernel and uses RedBoot as bootloader.

Why?

Why would anyone want to do this?
  • Packaged software: There already exist specialized distributions for the NSLU2 (OpenSlug and Unslung), but they are either quite restricted or in early development. The amount of packaged software is furthermore quite limited compared to Debian's 16K packages.
  • Familiarity: I run Debian on my other machines, so it's easier to maintain the NSLU2 if it's just another Debian machine instead of learning another distribution.
And finally, because you can ;-) Debian is afterall The Universal OS.

Requirements

  • A Linksys NSLU2 with a serial port modification. NEWS: Serial port no longer needed, see SerialLessInstallation for details
  • USB hard drive or memory stick large enough to install Debian (256MB+)
  • USB ethernet adapter (D-Link DUB-E100, Linksys USB200M, .. - See AddEthernetAdapter or linux-usb.org for a list of adapters known to work)
  • A Linux box to compile the kernel on (preferable running Debian)
  • A TFTP server to serve the installation images (apt-get install tftpd-hpa)
The serial port modification is needed for serial console access during Debian installation and initial setup. The ethernet adapter is needed as I haven't gotten the Intel sources working in little endian mode on a 2.6 kernel. Hopefully someone will step up for that task...

Big/Little Endian

The issues are as follows:
  • The CPU core can run in both big and little endian modes, but the peripherals are connected big endian
  • The original Linksys software is running in big endian mode, including the RedBoot bootloader
  • Debian only supports little endian mode on ARM
Therefore the kernel has to switch the processor into little endian mode at startup (in arch/arm/boot/compressed/little-endian.S).

The kernel image furthermore needs to be byteswapped before upload as it is loaded into memory by RedBoot running in big endian mode, but needs to be read by the Linux kernel running in little endian mode.

This can be done with the following little python script:

== byteswap.py snip start ==
#!/usr/bin/python
# swap every 32bit word in input and write it to output

import array, sys

if len(sys.argv) != 3:
  sys.stderr.write('Invalid arguments.\n')
  sys.stderr.write('Usage: %s <input> <output>\n' % sys.argv[0])
  sys.exit(1)

if sys.argv[1] == '-':
  file = sys.stdin
else:
  file = open(sys.argv[1], 'rb')

input = file.read()
a = array.array('I')
# add padding if needed
a.fromstring(input + '\0' * (4-len(input)&3))
a.byteswap()

if sys.argv[2] == '-':
  file = sys.stdout
else:
  file = open(sys.argv[2], 'wb')

a.tofile(file)
== byteswap.py snip end ==

Cross Compiler

First of all we need a cross compiler to compile the kernel. I have used the crosstool setup from the nslu2-linux.org wiki. See CompileCrossTool for a description of how to do this.

When the script finishes you should have an armv5b-softfloat-linux-gcc in /opt/crosstool/armv5b-softfloat-linux/gcc-3.3.4-glibc-2.2.5/bin/.

Alternatively you can download a precompiled version of it:

Kernel Patch

The kernel for running Debian on the NSLU2 is based on a standard 2.6.11.12 kernel.org kernel with some additional patches. The kernel patch is based on the work done by the OpenSlug team with a few patches to run in little endian mode. Currently this includes:
  • Use the correct linker script even if the compiler defaults to big endian
  • Switch the processor into little endian mode at startup
  • Access the (big endian-connected) flash
  • Access the PCI controller in little endian mode
Download (you need both):

Kernel Compilation

Start by downloading the 2.6.11.12 kernel from kernel.org and unpack it somewhere.
wget ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.11.12.tar.bz2
tar jxvf linux-2.6.11.12.tar.bz2

Enter the directory and apply the patches:

cd linux-2.6.11.12
patch -p1 <../openslug1.2-2.6.11.12.patch
patch -p1 <../nslu2-le-2.6.11.12.patch

Configure the kernel for the NSLU2:

make ARCH=arm CROSS_COMPILE=armv5b-softfloat-linux- nslu2_defconfig

And build it:

make ARCH=arm CROSS_COMPILE=armv5b-softfloat-linux- zImage

Byteswap the kernel image and copy it to your tftp directory:

sudo byteswap.py arch/arm/boot/zImage /var/lib/tftpboot/zImage-swap
Alternatively you can download a precompiled version of it:

Testing the Kernel

Start your terminal program, power up the NSLU2, break Redboot startup and test the kernel:
IP: 192.168.0.1/255.255.255.0, Gateway: 192.168.0.1
Default server: 0.0.0.0, DNS server IP: 0.0.0.0

RedBoot(tm) bootstrap and debug environment [ROMRAM]
Red Hat certified release, version 1.92 - built 15:16:07, Feb  3 2004

Platform: IXDP425 Development Platform (XScale)
Copyright (C) 2000, 2001, 2002, Red Hat, Inc.

RAM: 0x00000000-0x02000000, 0x000723a0-0x01ff3000 available
FLASH: 0x50000000 - 0x50800000, 64 blocks of 0x00020000 bytes each.
== Executing boot script in 2.000 seconds - enter ^C to abort
^C
RedBoot> ip_address -l 192.168.1.77 -h 192.168.1.100
IP: 192.168.1.77/255.255.255.0, Gateway: 192.168.0.1
Default server: 192.168.1.100, DNS server IP: 0.0.0.0
RedBoot> load -r -v -b 0x01d00000 zImage-swap
Raw file loaded 0x01d00000-0x01defd5f, assumed entry at 0x01d00000
RedBoot> exec 0x01d00000
Using base address 0x01d00000 and length 0x000efd60
Uncompressing Linux...................................................................
done, booting the kernel.
Linux version 2.6.11.12 (jacmet@p4) (gcc version 3.3.4) #1 Sun Jul 17 12:01:02 CEST 2005
CPU: XScale-IXP42x Family [690541f1] revision 1 (ARMv5TE)
CPU0: D VIVT undefined 5 cache
CPU0: I cache: 32768 bytes, associativity 32, 32 byte lines, 32 sets
CPU0: D cache: 32768 bytes, associativity 32, 32 byte lines, 32 sets
Machine: Linksys NSLU2
Warning: bad configuration page, trying to continue
Memory policy: ECC disabled, Data cache writeback
Built 1 zonelists
Kernel command line: root=/dev/ram0 rw rootfstype=ext2 initrd=0x01000000,10M noirqdebug
  init=/linuxrc mem=32M@0x00000000 console=ttyS0,115200n8
PID hash table entries: 256 (order: 8, 4096 bytes)
ixp4xx_timer_init()
Dentry cache hash table entries: 8192 (order: 3, 32768 bytes)
Inode-cache hash table entries: 4096 (order: 2, 16384 bytes)
Memory: 32MB = 32MB total
Memory: 20032KB available (1832K code, 165K data, 96K init)
calibrate_delay (jiffies = 4294937304)
Mount-cache hash table entries: 512 (order: 0, 4096 bytes)
CPU: Testing write buffer coherency: ok
checking if image is initramfs...it isn't (bad gzip magic numbers); looks like an initrd
Freeing initrd memory: 10240K
NET: Registered protocol family 16
nslu2_init()
PCI: IXP4xx is host
PCI: IXP4xx Using indirect access for memory space
PCI: bus0: Fast back to back transfers disabled
dmabounce: registered device 0000:00:01.0 on pci bus
dmabounce: registered device 0000:00:01.1 on pci bus
dmabounce: registered device 0000:00:01.2 on pci bus
SCSI subsystem initialized
usbcore: registered new driver usbfs
usbcore: registered new driver hub
OpenN2 Misc I/O Driver Version 0.1.7
enable_irq(22) unbalanced from c0012188
enable_irq(29) unbalanced from c0012190
NetWinder Floating Point Emulator V0.97 (double precision)
devfs: 2004-01-31 Richard Gooch (rgooch@atnf.csiro.au)
devfs: boot_options: 0x0
JFFS2 version 2.2. (C) 2001-2003 Red Hat, Inc.
IXP4xx Watchdog Timer: heartbeat 60 sec
Serial: 8250/16550 driver $Revision: 1.90 $ 2 ports, IRQ sharing disabled
ttyS0 at MMIO 0xc8000000 (irq = 15) is a XScale
io scheduler noop registered
io scheduler deadline registered
RAMDISK driver initialized: 16 RAM disks of 10240K size 1024 blocksize
IXP4XX-Flash0: Found 1 x16 devices at 0x0 in 16-bit bank
 Intel/Sharp Extended Query Table at 0x0031
Using buffer write method
cfi_cmdset_0001: Erase suspend on write enabled
Searching for RedBoot partition table in IXP4XX-Flash0 at offset 0x7e0000
6 RedBoot partitions found on MTD device IXP4XX-Flash0
Creating 6 MTD partitions on "IXP4XX-Flash0":
0x00000000-0x00040000 : "RedBoot"
0x00040000-0x00060000 : "SysConf"
0x00060000-0x00160000 : "Kernel"
0x00160000-0x00180000 : "Ramdisk"
0x00180000-0x007e0000 : "Flashdisk"
0x007e0000-0x00800000 : "FIS directory"
PCI: enabling device 0000:00:01.2 (0140 -> 0142)
ehci_hcd 0000:00:01.2: EHCI Host Controller
ehci_hcd 0000:00:01.2: irq 26, pci mem 0x48002000
ehci_hcd 0000:00:01.2: new USB bus registered, assigned bus number 1
ehci_hcd 0000:00:01.2: park 0
ehci_hcd 0000:00:01.2: USB 2.0 initialized, EHCI 1.00, driver 10 Dec 2004
hub 1-0:1.0: USB hub found
hub 1-0:1.0: 5 ports detected
PCI: enabling device 0000:00:01.0 (0140 -> 0142)
ohci_hcd 0000:00:01.0: OHCI Host Controller
ohci_hcd 0000:00:01.0: irq 28, pci mem 0x48000000
ohci_hcd 0000:00:01.0: new USB bus registered, assigned bus number 2
hub 2-0:1.0: USB hub found
hub 2-0:1.0: 3 ports detected
PCI: enabling device 0000:00:01.1 (0140 -> 0142)
ohci_hcd 0000:00:01.1: OHCI Host Controller
ohci_hcd 0000:00:01.1: irq 27, pci mem 0x48001000
ohci_hcd 0000:00:01.1: new USB bus registered, assigned bus number 3
hub 3-0:1.0: USB hub found
hub 3-0:1.0: 2 ports detected
Initializing USB Mass Storage driver...
usb 1-2: new high speed USB device using ehci_hcd and address 2
usbcore: registered new driver usb-storage
USB Mass Storage support registered.
eth0: register usbnet at usb-0000:00:01.2-2, DLink DUB-E100 USB Ethernet
usbcore: registered new driver usbnet
i2c /dev entries driver
LOADED Xicor x1205 RTC Dvr v0.9.3.3NPW
get status = 0x00
NET: Registered protocol family 2
IP: routing cache hash table of 512 buckets, 4Kbytes
TCP established hash table entries: 2048 (order: 2, 16384 bytes)
TCP bind hash table entries: 2048 (order: 1, 8192 bytes)
TCP: Hash tables configured (established 2048 bind 2048)
NET: Registered protocol family 1
RAMDISK: Couldn't find valid RAM disk image starting at 0.
Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(1,0)

Debian Installation

The NSLU2 is ofcause not directly supported by Debian, but the Debian-installer image from one of the other ARM platforms runs nicely on it.

We can E.G. use the initrd for the LART platform. This also needs to be byteswapped like the kernel image:

wget ftp://ftp.debian.org/debian/dists/sarge/main/installer-arm/current/images/lart/netboot/initrd.gz
sudo byteswap.py initrd.gz /var/lib/tftpboot/initrd-debian.gz-swap
Alternatively you can download a preconverted version of it:

This can then be loaded into Redboot together with the kernel. Remember to connect your USB ethernet adapter and USB hard drive:

IP: 192.168.0.1/255.255.255.0, Gateway: 192.168.0.1
Default server: 0.0.0.0, DNS server IP: 0.0.0.0

RedBoot(tm) bootstrap and debug environment [ROMRAM]
Red Hat certified release, version 1.92 - built 15:16:07, Feb  3 2004

Platform: IXDP425 Development Platform (XScale)
Copyright (C) 2000, 2001, 2002, Red Hat, Inc.

RAM: 0x00000000-0x02000000, 0x000723a0-0x01ff3000 available
FLASH: 0x50000000 - 0x50800000, 64 blocks of 0x00020000 bytes each.
== Executing boot script in 2.000 seconds - enter ^C to abort
^C
RedBoot> ip_address -l 192.168.1.77 -h 192.168.1.100
IP: 192.168.1.77/255.255.255.0, Gateway: 192.168.0.1
Default server: 192.168.1.100, DNS server IP: 0.0.0.0
RedBoot> load -r -v -b 0x01d00000 zImage-swap
Raw file loaded 0x01d00000-0x01defd5f, assumed entry at 0x01d00000
RedBoot> load -r -v -b 0x01000000 initrd-debian.gz-swap
Raw file loaded 0x01000000-0x01187b83, assumed entry at 0x01000000
RedBoot> exec 0x01d00000
...
The kernel will now boot and load the Debian installer. (Hint: use cu instead of minicom to get proper colors).

The installer will complain about low memory, but it will continue:

low memory

Later after configuring network and Debian mirror it will also complain about missing kernel modules. Ignore this and continue without selecting any additional installer components:

missing modules

Next step is partitioning. You don't have to use the same setup, but it's easier if you also have /dev/sda2 as rootfs, as you can then use the precompiled kernel:

partitions

Follow the rest of the installation through and you will finally get a warning that the installer didn't recognize the bootloader:

no bootloader

Ignore the warning and continue to reboot the system.

Boot From USB

After the Debian installer completes we need to boot from the selected root partition on the USB hard drive.

Unfortunately it isn't possibly to directly boot of an USB mass storage device as the detection and partition scan happens after the kernel tries to mount the rootfs.

Luckily there is a kernel cmdline option called 'rootdelay' which forces the kernel to wait the requested number of seconds before mounting the rootfs. Using rootdelay=10 seems to do it for me, but you might need to increase it for slow disks.

We can then recompile the kernel with the proper cmdline and boot on the USB disk to finish the Debian installation.

Download:

welcome

After selecting time zone, root passwd, mirror configuration and any additional packages the installation is finally completed:

done

Installing Modules

So far we have just been working with the kernel image alone, but the nslu2 configuration also builds a number of kernel modules. To install these the easist approach is to mount the USB disk on the PC and run make modules_install with INSTALL_MOD_PATH pointing at the USB disk:

First make sure that the modules are built:

make ARCH=arm CROSS_COMPILE=armv5b-softfloat-linux-
Mount the USB disk on the PC:
sudo mount /dev/sda2 /mnt/usb
Install the modules:
sudo make ARCH=arm CROSS_COMPILE=armv5b-softfloat-linux- INSTALL_MOD_PATH=/mnt/usb modules_install
Alternatively you can download a precompiled version of them:

Writing the Kernel to Flash

The final step is to write the kernel to the flash so that RedBoot will automatically load it on power-up instead of having to manually load it through tftp.

The RedBoot on the NSLU2 expects to find the kernel in the flash at address 0x50060000 (/dev/mtd2). It also expects to find a 16 byte header before the kernel image. Notice that the kernel cannot be bigger than 1MB!

The format of this header is:

  • Byte 0-3: Size of kernel in bytes (big endian format)
  • Byte 4-15: Zeros
See BootFlash for more info.

This can be done with the following little python script:

== flashimage.py snip start ==
#!/usr/bin/python
# Prepends RedBoot/Sercomm header to kernel image

import array, sys

if len(sys.argv) != 3:
  sys.stderr.write('Invalid arguments.\n')
  sys.stderr.write('Usage: %s <input> <output>\n' % sys.argv[0])
  sys.exit(1)

input = open(sys.argv[1], 'rb').read()

header = array.array('I',[0]*4)
# byte 0..3 = image size
header[0] = len(input)

# header in big endian format
if sys.byteorder != 'big':
  header.byteswap()

output = open(sys.argv[2], 'wb')
header.tofile(output)
output.write(input)
== flashimage.py snip end ==

Remember to first byteswap your kernel image with byteswap.py before running flashimage.py on it! E.G.:

flashimage.py zImage-sda2-swap kernel.img

Alternatively you can download a precompiled version of it:

This can then be put on the tftp server and loaded into RAM by RedBoot and from there written to flash:

RedBoot(tm) bootstrap and debug environment [ROMRAM]
Red Hat certified release, version 1.92 - built 15:16:07, Feb  3 2004

Platform: IXDP425 Development Platform (XScale)
Copyright (C) 2000, 2001, 2002, Red Hat, Inc.

RAM: 0x00000000-0x02000000, 0x000723a0-0x01ff3000 available
FLASH: 0x50000000 - 0x50800000, 64 blocks of 0x00020000 bytes each.
== Executing boot script in 2.000 seconds - enter ^C to abort
^C
RedBoot> ip_address -l 192.168.1.77 -h 192.168.1.100
IP: 192.168.1.77/255.255.255.0, Gateway: 192.168.0.1
Default server: 192.168.1.100, DNS server IP: 0.0.0.0
RedBoot> load -r -v -b 0x01d00000 kernel.img
Raw file loaded 0x01d00000-0x01defd4f, assumed entry at 0x01d00000
RedBoot> fis write -f 0x50060000 -b 0x01d00000 -l 0x100000
* CAUTION * about to program FLASH
            at 0x50060000..0x5015ffff from 0x01d00000 - continue (y/n)? y
... Erase from 0x50060000-0x50160000: ........
RedBoot> reset

The kernel is now automatically loaded by the standard RedBoot bootscript.

Conclusion

As this article explains it is possible to get a Debian/ARM machine up and running for around 200€ with off-the-shelf components, a bit of time and a tiny bit of hardware hacking (the serial port). The biggest thing lacking is getting the builtin ethernet to work.

It would be interesting to be able to install Debian without the serial port hack. Several methods could be possible: DONE

  • Use the preseed support in the Debian-installer to create an installer which doesn't need user interaction. See DebianInstallerPreseed for more info.
  • Get the network-console support in Debian-installer running so the installation can be performed over ssh. See DebianInstallerNetworkConsole for more info.
  • Simply provide a disk image of an installed base system with ssh running which can be written to USB stick / drive.

I would like to thank the nice people behind the nslu2-linux project - a lot of the information here is based on their work.

A DebianSlug project has been started on nslu2-linux.org to create an easy to install firmware image for running Debian on the NSLU2. Stay tuned for more information.

If you have any comments or questions you are welcome to contact me.