Goal
This guide details the installation of Arch Linux ARM on QNAP TS-212 and keep the system running with minimal effort.Although not tested, it should work on every TS-21x/TS-22x with minimal adjustments.
You should pause here and read every bit of information over at Debian on QNAP TS-21x/TS-22x, as it is a formidable source of information. Many thanks to Martin Michlmayr for all the work. Also, he details particular steps as how to backup and restore your device's flash, which aren't covered here.
I chose not to include extra configuration steps like hard drives power management, time synchronisation and software RAID (just to name a few), as they are not specific to the hardware in question and are well documented elsewhere.
I'm in no way liable for any bricking, damage, etc. you inflict on your devices; execute at your own risk. Also, and in case you're wondering, this procedure will most surely void your device's warranty.
Please feel free to drop a comment if you have any correction or suggestion to this guide.
Hardware
CPU | ARMv5TE single core Marvell Feroceon (Rev 1) running at 1.2Ghz, on Marvell DB-88F6281A-BP LE (Kirkwood System-on-Chip MV88F6281, Rev 3) |
DRAM | 256MB DDRII |
Boot Loader | U-Boot 1.1.4 (March 2nd, 2011, 14:34:42 - Marvell version is 3.4.4) |
Network | 1x Gigabit RJ-45 ethernet port |
LEDs | USB, Status, HDD1, HDD2, LAN, Power |
USB Ports | 3x USB 2.0 |
Buttons | Power, One Touch Copy, Reset |
SATA | 2x 3.5" SATA I/II interfaces |
Fan | 1x 6cm, 12V DC |
Buzzer | 1x |
Serial Port | 1x 3.3V TTL with JST PHR-4 female connector |
Flash Memory | 16 MB Serial NOR Micron ST M25P128, layed out in the following style:# MTD Size in Block Starts at Bytes Long Words Contains 0 0xF8000000 512K 0x020000 U-Boot 4 0xF8080000 256K 0x010000 U-Boot Config 5 0xF80C0000 1280K 0x050000 NAS Config 1 0xF8200000 2M 0x080000 Kernel 2 0xF8400000 9M 0x240000 RootFS1 3 0xF8D00000 3M 0x0C0000 RootFS2 |
Considerations
To keep the energy consumption as low as possible, the hard drives should be sleeping whenever not in use. To minimise its usage, root file system will be put on an USB stick.As 256MB of RAM is not really enough, swap will be necessary and will be put on the same USB stick.
Arch Linux ARM's Kirkwood kernel image is bigger that the original 2M partition. This means that the kernel will be housed in the 9MB partition.
This particular U-boot version lacks the capability of loading files from USB, so the kernel will be loaded from flash memory. As such, kernel must be flashed every time it changes.
The flashing procedure should be done synchronously with the "pacman -S" procedure, either in a mkinitcpio build hook script (which Arch Linux ARM kernels don't execute), a kernel-install script (idem) or using a pacman hook feature (still nonexistent).
One valid option is to use some file system event monitor (inotify-tools, for instance), wait for /boot/uImage to be modified and then act on it. This isn't a good choice, as one should see the flashing actually being executed.
The chosen solution is to "intercept" pacman's execution with a /usr/local/bin/pacman script, which will call /usr/bin/pacman and, upon its successful completion and asserting that uImage differ from flash contents, flash the kernel image.
Assumptions
You:- Know your way around Arch Linux, U-Boot, particularly tftp, char and block devices, fdisk, mkfs, mkswap, tar, fstab, screen and bash scripting;
- Read Debian on QNAP TS-21x/TS-22x.
- A QNAP TS-212, in any state (bootable into U-Boot), diskless;
- A 3.3V TTL serial cable, equiped with a JST PHR-4 male connector. I use a TTL-to-USB "Cygnal Integrated Products, Inc. CP210x UART Bridge", patched with an old CDROM audio cable;
- An USB stick, minimum 4GB. As USB support varies, you should use a branded one - and fast, too. I use a SanDisk Cruzer Fit 4GB, as it is unobtrusive;
- A networked Linux PC with a TFTP server and terminal multiplexer with serial support (tftp-hpa and screen, for instance). I use Arch Linux x86-64;
- A network with DHCP and no (valid) BOOTP.
Steps
On the Linux PC, act as root from now on, so:$ sudo -i
Install needed packages:
# pacman -S tftp-hpa screen
Ensure the TFTP server is running:
# systemctl start tftpd.socket tftpd.service
Insert the USB stick and unmount every auto mounted partition:
# umount /dev/sdX?Of course X must be replaced by the letter Linux generated for the particular device...
~1GB should be enough swap; repartition the USB stick:
# fdisk /dev/sdX Welcome to fdisk (util-linux x.xx.x). Changes will remain in memory only, until you decide to write them. Be careful before using the write command. Command (m for help): o Created a new DOS disklabel with disk identifier 0xe34cf4eg. Command (m for help): n Partition type: p primary (0 primary, 0 extended, 4 free) e extended Select (default p): Using default response p. Partition number (1-4, default 1): First sector (2048-7821311, default 2048): Last sector, +sectors or +size{K,M,G,T,P} (2048-7821311, default 7821311): +2700M Created a new partition 1 of type 'Linux' and of size 2.7 GiB. Command (m for help): n Partition type: p primary (1 primary, 0 extended, 3 free) e extended Select (default p): Using default response p. Partition number (2-4, default 2): First sector (5531648-7821311, default 5531648): Last sector, +sectors or +size{K,M,G,T,P} (5531648-7821311, default 7821311): Created a new partition 2 of type 'Linux' and of size 1.1 GiB. Command (m for help): t Partition number (1,2, default 2): Hex code (type L to list all codes): 82 Changed type of partition 'Linux' to 'Linux swap / Solaris'. Command (m for help): p Disk /dev/sdX: 3.7 GiB, 4004511744 bytes, 7821312 sectors Units: sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disklabel type: dos Disk identifier: 0xgggggggg Device Boot Start End Blocks Id System /dev/sdX1 2048 5531647 2764800 83 Linux /dev/sdX2 5531648 7821311 1144832 82 Linux swap / Solaris Command (m for help): w The partition table has been altered. Calling ioctl() to re-read partition table.Copy & paste the disk identifier into a text file, as it will be needed ahead.
Now create "usbroot" ext4 file system in first partition and "usbswap" swap in second partition:
# mkfs.ext4 -m 1 -L usbroot /dev/sdX1 # mkswap -L usbswap /dev/sdX2
Mount the "usbroot" partition, download the root filesystem, extract it and copy the kernel container uImage to the TFTP server root:
# mount -o noatime /dev/sdX1 /mnt # curl -OL http://archlinuxarm.org/os/ArchLinuxARM-kirkwood-latest.tar.gz # tar -xzvC/mnt -fArchLinuxARM-kirkwood-latest.tar.gz # cp /mnt/boot/uImage /srv/tftp/
Default fstab is empty; fill it with the partitions you created earlier:
# cat >> /mnt/etc/fstab << EOF LABEL=usbroot / ext4 noatime 0 1 LABEL=usbswap none swap defaults 0 0 EOF
As USB is a rather slow medium, swap usage should be minimised. Also, Linux's memory compaction doesn't work all that well out-of-the-box, so you must take an extra step to prevent future page allocation failures:
# cat > /mnt/etc/sysctl.d/90-vm.conf << EOF vm.swappiness=5 vm.min_free_kbytes=16184 EOF
Kernel will be written to flash from Linux, so "mtdblock" module is necessary:
# cat > /mnt/etc/modules-load.d/mtdblock.conf << EOF mtdblock EOF
As previously explained, pacman calls will be "intercepted" by the following script:
# cat > /mnt/usr/local/bin/pacman << EOF #!/bin/bash /usr/bin/pacman "\$@" || exit \$? /usr/local/bin/flash-kernel EOF # chmod 755 /mnt/usr/local/bin/pacman
yaourt and any other similar tool you intend to use will need the same treatment.
And this one will flash the kernel:
# cat > /mnt/usr/local/bin/flash-kernel << EOF #!/bin/bash if [ "\$('id' -u)" != "0" ]; then echo -n "\$('basename' \$0): This script must be executed with root privileges! " command -v sudo > /dev/null 2>&1 || { echo >&2 "However, sudo is not installed. Aborting!"; exit 1; } exec sudo -E "\$0" \${1+"\$@"} fi [ -f /boot/uImage ] || { echo >&2 "\$('basename' \$0): No kernel (/boot/uImage) installed. Aborting!"; exit 1; } modprobe mtdblock > /dev/null 2>&1 [ -b /dev/mtdblock2 ] || { echo >&2 "\$('basename' \$0): No MTD (/dev/mtdblock2) available. Aborting!"; exit 1; } [ "\$('file' -sb /boot/uImage)" != "\$('file' -sb /dev/mtdblock2)" ] || exit 0 echo -n "\$('basename' \$0): Flashing /boot/uImage to /dev/mtdblock2... " cat /boot/uImage > /dev/mtdblock2 if [ "\$?" != "0" ]; then echo >&2 "Your system could be unusable!!! Please flash the kernel again!!!" exit 1 else echo "Ok!" /usr/local/bin/backup-mtd fi EOF # chmod 755 /mnt/usr/local/bin/flash-kernel
It is recommended to keep at all times a backup copy of the flash contents:
# cat > /mnt/usr/local/bin/backup-mtd << EOF #!/bin/bash BACKUP_KEEP_AT_MOST=5 BACKUP_DIRECTORY=/var/backups BACKUP_SUBDIRECTORY=mtdblock if [ "\$('id' -u)" != "0" ]; then echo -n "\$('basename' \$0): This script must be executed with root privileges! " command -v sudo > /dev/null 2>&1 || { echo >&2 "However, sudo is not installed. Aborting!"; exit 1; } exec sudo -E "\$0" \${1+"\$@"} fi modprobe mtdblock > /dev/null 2>&1 [ -b /dev/mtdblock2 ] || { echo >&2 "\$('basename' \$0): No MTD (/dev/mtdblock*) available. Aborting!"; exit 1; } TIME=\$(date +"%Y-%m-%d_%H-%M-%S") rm -fr \$BACKUP_DIRECTORY/\$BACKUP_SUBDIRECTORY/\$TIME > /dev/null 2>&1 mkdir -p \$BACKUP_DIRECTORY/\$BACKUP_SUBDIRECTORY/\$TIME > /dev/null 2>&1 echo "\$('basename' \$0): Backing up /dev/mtdblock* partition(s) to \$BACKUP_DIRECTORY/\$BACKUP_SUBDIRECTORY/\$TIME..." echo "Generated at \$TIME. Contents are as follows:" > \$BACKUP_DIRECTORY/\$BACKUP_SUBDIRECTORY/\$TIME/description for PARTITION in \$('ls' -dtr1 /dev/mtdblock*); do FILE=\$('basename' \$PARTITION) echo -n "\$FILE... " cat \$PARTITION > \$BACKUP_DIRECTORY/\$BACKUP_SUBDIRECTORY/\$TIME/\$FILE echo "Ok!" echo -n "\$FILE: " >> \$BACKUP_DIRECTORY/\$BACKUP_SUBDIRECTORY/\$TIME/description 'file' -b \$BACKUP_DIRECTORY/\$BACKUP_SUBDIRECTORY/\$TIME/\$FILE >> \$BACKUP_DIRECTORY/\$BACKUP_SUBDIRECTORY/\$TIME/description done echo -n "Keeping only last \$BACKUP_KEEP_AT_MOST backups... " 'ls' -dtr1 \$BACKUP_DIRECTORY/\$BACKUP_SUBDIRECTORY/*/ | 'head' -n -\$BACKUP_KEEP_AT_MOST | 'xargs' rm -fr echo "Ok! Current backups are:" 'ls' -dtr1 \$BACKUP_DIRECTORY/\$BACKUP_SUBDIRECTORY/*/ | sort echo "Done!" EOF # chmod 755 /mnt/usr/local/bin/backup-mtd
Unmount the USB stick and insert it in the TS-212.
Power on the TS-212 while holding the reset button until it beeps twice (approximately 8 seconds - this instructs it to enter recovery mode). It will beep once more after a while (meaning it couldn't get a BOOTP response - which is intended).
Connect the serial cable and start the terminal multiplexer on the serial port with 115200/8-N-1:
# screen /dev/ttyUSB0 115200 8N1
With these kind of devices, the NIC's MAC address is set by U-Boot from its configuration. While this is printed on a sticker on the board right next to the power button, it's easier to copy & paste it to a text file from:
Marvell>> printenvThe value is stored in the "ethaddr" variable; copy & paste it onto the text file.
When booting, U-Boot waits only a second for a key press on the serial port to stop automatic boot. Right after issuing the next commands, repeatedly press space until you are on the U-Boot prompt ("Marvell>>"):
Marvell>> resetenv Marvell>> reset
Reset the MAC address with the value you kept earlier:
Marvell>> setenv ethaddr 'GG:GG:GG:GG:GG:GG'
Now issue:
Marvell>> setenv mainlineLinux 'yes' Marvell>> setenv arcNumber '2139'This will make U-Boot inform the kernel which hardware it is booting on.
Booting with "root=/dev/sdX1" isn't feasible, as the X will be volatile from the moment that an hard disk is added to the system. To overcome that, "root=PARTUUID=" will be used:
Marvell>> setenv bootargs 'console=ttyS0,115200 root=PARTUUID=GGGGGGGG-01 rootfstype=ext4 rootwait rw'Replace "GGGGGGGG" with the disk identifier you kept on your text file (minus the initial "0x").
Automatic boot consists on loading the kernel from flash partition #2, and starting it:
Marvell>> setenv bootcmd 'cp.l 0xF8400000 0x800000 0x240000;bootm 0x800000'
Finally, save the U-Boot variables, reboot and stop automatic boot when instructed:
Marvell>> saveenv Marvell>> reset
At this point, kernel isn't yet lodged on the flash, so it must be loaded from the TFTP server. Connect the TS-212 to the network if it isn't already.
Select a free IP address on your network and set it with:
Marvell>> set ipaddr '192.168.0.303'This address is temporary; it will only be used by the TFTP client.
Set the IP address of the TFTP server with:
Marvell>> setenv serverip '192.168.0.302'
Load the kernel from TFTP and run it with:
Marvell>> tftp 0x800000 uImage;bootm 0x800000
When greeted by getty, log on as root; default password is "root" - you should change it now.
Graphical interface is unnecessary:
# systemctl set-default multi-user.target
Upgrade the system:
# pacman -Suyy
It the previous update didn't flash a kernel, flash it now:
# flash-kernelPlease be advised that this procedure should take over three minutes.
A backup of the flash will be created every time a new kernel is flashed.
qcontrol is a package that controls LEDs, buzzer, buttons and, more important, the fan. Install it:
# pacman -S qcontrol
Assign the right configuration:
# ln -s /etc/qcontrol/ts219.lua /etc/qcontrol.conf
Enable it and reboot:
# systemctl enable qcontrold.socket qcontrol.service # reboot
Done!
Final Words
Kernel loading should output a "rtc-mv rtc-mv: internal RTC not ticking" message. This can safely be ignored, as this board uses Seiko Instruments S-35390A (rtc-s35390a) to keep the time - and it will be registered as /dev/rtc.When installing a new kernel, depmod outputs an error related to hci_vhci module. That can also be ignored.