l.unchti.me

Debian on the Zyxel NAS540

The Zyxel NAS540 is a fairly inexpensive 4-bay NAS. Unfortunately, the stock firmware is nigh-unusable and weighed down by bloat. With a little bit of work it is possible to run Debian stable on it.

While the internal NAND flash is fairly large (256Mb) an easier to debug method depositing a standard Debian jessie root filesystem on an external SD memory card is used here. This mostly avoids bricking alterations, although a new barebox configuration and kernel image have to be written to the flash (at least until we get a better barebox with file system support).

Preparatory Work

The minimum required is a (reasonably fast) µSD-card of at least 2Gb size. When installing more lightweight distributions you may get away with less although currently prices don’t differ substantially for decent sized cards.

A Debian-ish Linux machine is highly recommended.

Hooking up a serial console is of some use if unexpectancies arise. To open the NAS-enclosure, 4 screws beneath the label have to be removed. The upper part of the case can then be moved back using judicious amounts of force.

The serial port is found on the pin header marked in the image below:

PCB image

Its pinout is:

+----+----+----+----+----+
|GND |    | TX | RX |    |
+----+    +----+----+----+

Assembling a root image

We create an ext4 filesystem on our card at /dev/mmclk0:

# parted --script --align optimal /dev/mmcblk0 mklabel gpt mkpart primary ext4 0% 100%
# mkfs.ext4 /dev/mmcblk0p1

Debian includes a debootstrap wrapper for foreign architectures in the qemu-user-static package:

# apt-get install qemu-user-static

Bootstrap the system on the SD card:

# mount /dev/mmcblk0p1 /mnt
# qemu-debootstrap --no-check-gpg --arch=armhf testing /mnt

Enter the root file system (transparent qemu emulation is still enabled), mount /proc and /sys:

# chroot /mnt /bin/bash -l
# mount -t proc proc /proc
# mount -t sysfs sysfs /sys

Pick an appropriate Debian mirror from the list:

# cat > /etc/apt/sources.list  << EOF
deb http://ftp.debian.org/debian testing main
deb-src http://ftp.debian.org/debian testing main
EOF
# apt-get update
# apt-get upgrade

Set a hostname and root password, add a minimal network configuration, and install openssh:

# echo $HOSTNAME > /etc/hostname
# passwd
# cat > /etc/network/interfaces.d/eth0 << EOF
auto eth0
iface eth0 inet dhcp
EOF
# apt-get install openssh-server mtd-utils

We are going to use the partition UUID (NOT the file system UUID!) to uniquely identify the root partition. The benefit is that it is still possible to have a variable number of drives and a properly configured kernel may boot without an initramfs.

~> ls -l /dev/disk/by-partuuid/
total 0
lrwxrwxrwx 1 root root 10 Mar  1 13:24 1fd1dff9-34b6-42af-ae91-63e35799040a -> ../../sda3
lrwxrwxrwx 1 root root 15 Mar 17 17:07 56749a98-ab89-49e8-a77d-b2486aa7a519 -> ../../mmcblk0p1
lrwxrwxrwx 1 root root 10 Mar  1 13:24 659edc67-f610-4adc-8c71-c341466242c8 -> ../../sda1
lrwxrwxrwx 1 root root 10 Mar  1 13:24 ef0d6d35-a507-4b7a-a3ae-e1ad2724b0cb -> ../../sda2

Write a minimal /etc/fstab:

# cat > /etc/fstab << EOF
PARTUUID="$PARTUUID" / ext4 defaults 0 1
EOF

Kernel Configuration

Unfortunately, support for the QorIQ LS1024A SoC is not included in the mainline kernel forcing us to resort to an older version (3.2.54) from Zyxel’s GPL binary. For simplicity’s sake the kernel tree can be found here. A confirmed working config can be found here; if you don’t want to build your own kernel the uImage and modules are available as well.

As 3.2.54 does not compile with gcc versions above 4.9, an earlier version has to be installed for cross-compilation. If you don’t know how to do this just use the prebuilt binaries.

# apt-get install xz-utils
# cd /
# wget https://br.unchti.me/uImage
# wget https://br.unchti.me/modules.tar.xz
# tar xf modules.tar.xz

Bootloader Shenanigans

The next part is somewhat hairy as it reflashes the barebox configuration and kernel on the internal NOR flash from a telnet console on the original firmware. First create a minimal barebox configuration file on the MMC card:

# mkdir /boot/barebox
# cat >> /boot/barebox/config << EOF
#!/bin/sh

# barebox images
uloaderimage=microloader-c2kevm.bin
bareboximage=barebox-c2kevm.bin

spi_parts="256k(uloader)ro,512k(barebox)ro,256k(env)"
spi_device="spi0.0"

nand_device="comcertonand"
nand_parts="10M(config),10M(kernel1),110M(rootfs1),10M(kernel2),110M(rootfs2),-(reserved)"

autoboot_timeout=3

bootargs="console=ttyS0,115200n8 pcie_gen1_only=yes usb3_internal_clk=yes init=/sbin/init root=PARTUUID=$PARTUUID rootdelay=5 panic=5"
EOF

# cat >> /boot/barebox/bin/boot << EOF
#!/bin/sh

. /env/config

if [ -n $spi_parts ]; then
	mtdparts="${mtdparts}${spi_device}:${spi_parts}"
fi

if [ -n $nand_parts ]; then
	if [ -n ${mtdparts} ]; then
		mtdparts="${mtdparts};"
	fi
	mtdparts="${mtdparts}${nand_device}:${nand_parts}"
fi

if [ -n $mtdparts ]; then
	bootargs="${bootargs} mtdparts=${mtdparts}"
fi

# set fan speed to 20% to avoid the irregular jitter
mw 0x90470058 0x05000000
mw 0x90458000 0x80000001
mw 0x90458028 0x80001388
mw 0x9045802C 0x00000fa0

kdev="/dev/nand0.kernel2"
echo "booting kernel from $kdev"

bootm $kdev
	
echo " Failed."
exit
EOF

cat >> /boot/barebox/bin/init
#!/bin/sh

PATH=/env/bin
export PATH

. /env/config

if [ -e /dev/spi0 -a -n "$spi_parts" ]; then
	addpart /dev/spi0 $spi_parts
fi

if [ -e /dev/nand0 -a -n "$nand_parts" ]; then
	addpart /dev/nand0 $nand_parts

fi

echo "Disabling eee function of phy 4 ..."
phy write 4 0x1F 0x0000
phy write 4 0x00 0x8000
#Wait 20ms for PHY reset
sleep 1
phy write 4 0x1F 0x0005
phy write 4 0x05 0x8b85
phy write 4 0x06 0x0ae2
phy write 4 0x1F 0x0007
phy write 4 0x1E 0x0020
phy write 4 0x15 0x1008
phy write 4 0x1F 0x0000
phy write 4 0x0D 0x0007
phy write 4 0x0E 0x003c
phy write 4 0x0D 0x4007
phy write 4 0x0E 0x0000

echo "Disabling eee function of phy 6 ..."
phy write 6 0x1F 0x0000
phy write 6 0x00 0x8000
#Wait 20ms for PHY reset
sleep 1
phy write 6 0x1F 0x0005
phy write 6 0x05 0x8b85
phy write 6 0x06 0x0ae2
phy write 6 0x1F 0x0007
phy write 6 0x1E 0x0020
phy write 6 0x15 0x1008
phy write 6 0x1F 0x0000
phy write 6 0x0D 0x0007
phy write 6 0x0E 0x003c
phy write 6 0x0D 0x4007
phy write 6 0x0E 0x0000

echo
echo -n "Hit any key to stop autoboot: "
timeout -a $autoboot_timeout
if [ $? != 0 ]; then
	exit
fi

boot
EOF

Then put the bareboxenv binary from here on the card (or compile it yourself).

# wget https://br.unchti.me/bareboxenv -O /usr/local/bin/bareboxenv

Unmount the SD card and plug it the card into the NAS.

Next get the vendor firmware to start up a telnet server by using the web frontend’s backdoor (see here for further information).

Log in using telnet and make sure that /dev/mtd2 points to the barebox environment. The partition table should look like this:

# cat /proc/mtd 
dev:    size   erasesize  name
mtd0: 00040000 00010000 "uloader"
mtd1: 00080000 00010000 "barebox"
mtd2: 00040000 00010000 "env"
mtd3: 00a00000 00020000 "config"
mtd4: 00a00000 00020000 "kernel1"
mtd5: 06e00000 00020000 "rootfs1"
mtd6: 00a00000 00020000 "kernel2"
mtd7: 06e00000 00020000 "rootfs2"
mtd8: 00600000 00020000 "reserved"

Mount the card in the stock firmware, build a barebox environment bundle and copy it to the NOR flash:

# mount /dev/sdX /mnt/
# /mnt/usr/local/bin/bareboxenv -s /mnt/boot/barebox bb.env
# flash_eraseall /dev/mtd2
# flashcp bb.env /dev/mtd2
# flash_eraseall /dev/mtd6
# flashcp /mnt/uImage /dev/mtd6

Reboot and you’re done. The new system will acquire a DHCP lease on the first ethernet device. Just log in using SSH and configure away however you want.