D-Link DNS-320 & DNS-325 NAS: Keeping D-Link Firmware

It's not necessary to remove the original firmware to boot Linux, so long as you have serial access you can tell u-boot to load a different kernel temporarily.

Debian kernels will support the DNS-320 and DNS-325, however you need to use wheezy-backports to get a kernel new enough.

Building a root filesystem

debootstrap is a utility that allows you to create a Debian installation from another already-installed system. My good friend has a guide on building one using debootstrap, so will only go into brief detail here. All of his tips are useful, so read that first!

Start debootstrap with packages you want to install:

host:# aptitude install debootstrap binfmt-support qemu-user-static
host:# debootstrap --verbose --foreign --arch=armel --variant=minbase \
       --include=module-init-tools,locales,udev,dialog,ifupdown,procps,iproute,iputils-ping,pump,nano,wget,netbase,console-data \
       wheezy rootfs http://ftp.uk.debian.org/debian

Use qemu-arm-static to finish the job:

host:# cp /usr/bin/qemu-arm-static rootfs/usr/bin/
host:# chroot rootfs
I have no name!@host:/# ./debootstrap/debootstrap --second-stage
I have no name!@host:/# echo 'nas' > etc/hostname
I have no name!@host:/# passwd

Next, set up APT sources, including backports:

I have no name!@host:/# echo 'APT { Install-Recommends "false"; };' > etc/apt/apt.conf.d/no-recommends
I have no name!@host:/# cat <<EOF > etc/apt/sources.list
deb http://ftp.uk.debian.org/debian/ wheezy main contrib non-free
deb http://security.debian.org/ wheezy/updates main contrib non-free
I have no name!@host:/# cat <<EOF > /etc/apt/sources.list.d/backports.list
deb http://ftp.debian.org/debian wheezy-backports main
I have no name!@host:/# cat <<EOF > /etc/apt/preferences.d/wheezy-backports
Package: linux*kirkwood
Pin: release a=wheezy-backports
Pin-Priority: 999
I have no name!@host:/# apt-get clean ; apt-get update

Get a kernel and configure the NAS to build images:

I have no name!@host:/# cat <<'EOF' > /etc/modules
# Load required modules
I have no name!@host:/# apt-get install linux-image-kirkwood u-boot-tools
I have no name!@host:/# cat <<'EOF' > /etc/kernel/postinst.d/zz-local-build-image
#!/bin/sh -e
# passing the kernel version is required
[ -z "${version}" ] && exit 0

# NB: change depending on your NAS model
cat /boot/vmlinuz-${version} /usr/lib/linux-image-${version}/kirkwood-dns320.dtb \
    > /tmp/appended_dtb

/usr/bin/mkimage -A arm -O linux -T kernel -C none -n uImage \
                 -a 0x00008000 -e 0x00008000 \
                 -d /tmp/appended_dtb /boot/uImage-${version}
ln -sf /boot/uImage-${version} /uImage

/usr/bin/mkimage -A arm -O linux -T ramdisk -C gzip -n uInitrd \
                 -a 0x00e00000 -e 0x00e00000 \
                 -d /boot/initrd.img-${version} /boot/uInitrd-${version}
ln -sf /boot/uInitrd-${version} /uInitrd
I have no name!@host:/# chmod a+x /etc/kernel/postinst.d/zz-local-build-image
# Recreate the kernel package to create the images to boot from
I have no name!@host:/# dpkg-reconfigure $(dpkg --get-selections | grep linux-image-3 | cut -f1)

At this stage, you should have a directory full of all the normal files that make up a Debian system, and kernel / initrd images in /boot. Next you will need to get it to the NAS and boot from it.

Booting the NAS

First, connect to the serial port and turn on the NAS. At the Hit any key to stop autoboot prompt press space then 1. You should then be sitting at a "Marvell>>" u-boot prompt. Before doing anything, run:

Marvel>> printenv

And make a note of the contents. This will be a useful reference if you want to restore any u-boot parameters.

Booting Via USB

U-boot (the bootloader) supports ext2-formatted USB keys. However it is very picky about what you use. Use a proper USB key, not a USB card reader. If u-boot seems unable to find the key, try another.

Format the stick with an ext2 filesystem. Then copy the root filesystem you generated onto it:

host:~# cp -arv rootfs/* /mnt/usb_stick/

Of course you can also just have a small /boot partition that is ext2 formatted and the rest something else, but I will leave that as an exercise for the reader.

Now go back to your serial console where your NAS is waiting. Insert the USB stick and run the following commands:

Marvell>> setenv ethaddr 00:50:43:xx:xx:xx [Any MAC address]
Marvell>> setenv bootargs console=ttyS0,115200 root=/dev/sda1 usb-storage.delay_use=0 rootdelay=1 rw
Marvell>> usb reset ; ext2load usb 0:1 0xa00000 /uImage ; ext2load usb 0:1 0xf00000 /uInitrd
Marvell>> bootm 0xa00000 0xf00000

This does the following...

  1. Set the MAC address. D-link ships the NASes with corrupt u-boot configuration, so it assigns a random MAC address on each reboot until you do saveenv. This will confuse Debian. If you're not sure what to choose, make a note of what it uses when booted with the D-link formware and use that.
  2. Set kernel command line options, namely a serial console and to use the USB stick as root.
  3. Initialise USB, load image off first USB stick into memory.
  4. Run loaded image. NB: D-link's u-boot will refuse to load the initrd unless it is copied to this memory address.

After this, you should watch your system boot.

Booting Via Network

You can load kernels over the network using TFTP, and use NFS to get the root filesystem. You will need to set up servers for both, see instructions here.

Copy the uImage built earlier into the TFTP server's directory, for example:

host:linux-2.6$ cp rootfs/boot/u{Image,Initrd}* /srv/tftp/

And copy the root filesystem into a NFS export, for example:

host:~# cp -arv rootfs/* /srv/export/nas_root/

Now go back to your serial console where your NAS is waiting. Do something like:

Marvell>> setenv ethaddr 00:50:43:xx:xx:xx [Any MAC address]
Marvell>> setenv ipaddr [Our IP address, e.g.]
Marvell>> setenv serverip [Where TFTP server is, e.g.]
Marvell>> setenv bootargs console=ttyS0,115200 root=/dev/nfs
  nfsroot=[NFS server, e.g.]:/srv/export/nas_root,v3, ip=::::::dhcp rw
Marvell>> tftp 0xa00000 uImage-3.10-0.bpo.2-kirkwood
Marvell>> tftp 0xf00000 uInitrd-3.10-0.bpo.2-kirkwood
Marvell>> bootm 0xa00000 0xf00000

This does the following...

  1. Set the MAC address.
  2. Configure the network so the TFTP server can be found.
  3. Set kernel command line options, namely a serial console and where to find the NFS server
  4. Copy kernel image into memory via TFTP.
  5. Run loaded image. NB: D-link's u-boot will refuse to load the initrd unless it is copied to this memory address.

After this, you should watch your system boot.

Boot Debian by default

Your NAS should now be booting. You can make the above happen by default by:

Marvell>> [all the setenv commands from above]
Marvell>> setenv bootcmd '[commands needed to boot]'
Marvell>> saveenv
Marvell>> reset

And return to D-Link firmware either by running the contents of the original bootcmd manually, or setenv'ing the parameters back again.

Installing kernel to NAND

Whilst we're trying to leave the NAND alone on this page, it may make your life easier to write the kernel image here to save you keeping a TFTP server running, e.g.

NAND chips can be divided into partitions. D-Link by default divide it into 6:

Creating 6 MTD partitions on "orion_nand":
0x000000000000-0x000000100000 : "u-boot"
0x000000100000-0x000000600000 : "uImage"
0x000000600000-0x000000b00000 : "ramdisk"
0x000000b00000-0x000007100000 : "image"
0x000007100000-0x000007b00000 : "mini firmware"
0x000007b00000-0x000008000000 : "config"

The first is for u-boot, the second for the kernel image. We want to write the uImage built earlier here:

nas:~# dd if=/dev/mtd1 of=dlink.uImage bs=1M
nas:~# flash_eraseall /dev/mtd1
Erasing 128 Kibyte @ 500000 -- 100 % complete.
nas:~# nandwrite -p /dev/mtd1 ${path-to-uImage}
Writing data to block 0 at offset 0x0
Writing data to block 1 at offset 0x20000
Writing data to block 2 at offset 0x40000
Writing data to block 3 at offset 0x60000
Writing data to block 4 at offset 0x80000
Writing data to block 5 at offset 0xa0000
Writing data to block 6 at offset 0xc0000
Writing data to block 7 at offset 0xe0000
Writing data to block 8 at offset 0x100000
Writing data to block 9 at offset 0x120000
Writing data to block 10 at offset 0x140000
Writing data to block 11 at offset 0x160000
Writing data to block 12 at offset 0x180000
Writing data to block 13 at offset 0x1a0000
Writing data to block 14 at offset 0x1c0000

Finally, set up u-boot environment to permanently boot our kernel:

Marvell>> [all the setenv commands from above]
Marvell>> setenv bootcmd 'nand read.e 0x1000000 0x00100000 0x00200000 ; bootm 0x1000000'
Marvell>> saveenv
Marvell>> reset

Next Steps

Appendix: Compiling a kernel

You don't need to compile a kernel any more, but just in case you want to anyway here are some notes. Any Linux 3.6+ kernel will have support for the NASes built in. You will need an ARM cross compiling toolchain, see instructions here.

Once you have an appropriate toolchain, you can compile using much the same instructions as a regular compilation. As well as building a kernel you will also need to build the devicetree blob for your NAS. As the D-Link u-boot cannot load this, we will need to bodge it onto the end of the kernel image:

host:~$ cd ${kernel_source}
# Set up cross-compiler
host:linux-2.6$ alias cross-make='make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-'
# Configure kernel
host:linux-2.6$ cat arch/arm/configs/kirkwood_defconfig - <<EOF > .config
### Extra options atop kirkwood_defconfig
host:linux-2.6$ cross-make menuconfig # If you need to
# Compile
host:linux-2.6$ cross-make -j5 zImage kirkwood-dns320.dtb kirkwood-dns325.dtb
# Append the DTB for your device (choose either dns320 or dns325)
host:linux-2.6$ cat arch/arm/boot/kirkwood-dns320.dtb >> arch/arm/boot/zImage
host:linux-2.6$ cross-make uImage