Devuan on N900

What follows is an account about how I managed to get Devuan running on a Nokia N900 (console only, no desktop environment for now).


[edit] Linux kernel

I first cloned mainline (or rather stable) kernel:

git clone

This creates a folder named linux-stable in the present working directory, and populates it by git (hidden) files and kernel sources (obtained by checking out the master branch). I decided to work on the stable branch (linux-4.10.y at present) on a separate directory:

mkdir linux-4.10.8 (or whatever)
cd linux-stable
git --work-tree=../linux-4.10.8 checkout linux-4.10.y -- .
cd ../linux-4.10.8

I used Pali’s rx51_defconfig [1] as a configuration file:

curl -o arch/arm/configs

Now we need a cross-toolchain to compile the kernel. Let’s assume we are on an x86 machine running Devuan Jessie. Also, I assume we are root. There are two options: either the arm-none-eabi toolchain, or the arm-linux-gnueabihf one. The former is easier to install since it is available from

deb jessie main


apt-get install gcc-arm-none-eabi

The latter is more complicated, but may be useful later on. It may be downloaded from By following Debian’s wiki [2], we first create /etc/apt/sources.list.d/crosstools.list containing:

deb jessie main

Then we install the archive key:

curl | apt-key add -

At this point the installation goes like:

dpkg --add-architecture armhf
apt-get update
apt-get install crossbuild-essential-armhf

Now we may compile the kernel [3]. Let’s first cd back to the linux-4.10.8 directory. Then select the desired target architecture and cross-toolchain:

export ARCH=arm
export CROSS_COMPILE=arm-none-eabi- (or arm-linux-gnueabihf-)

Prepare the kernel configuration:

make rx51_defconfig

At this point it is wise to hack a bit the kernel configuration by setting the two watchdogs (OMAP and TWL4030) to be included into the kernel itself and not separated as loadable modules [4]. This is done by calling

make menuconfig

and following the path Device Drivers -> Watchdog Timer Support, then setting the two watchdogs not as <M> but as <*>. Save and exit.

Next step is the actual build, which is triggered by:

make (add -j followed by a number N to enable N parallel build processes) zImage

At the end, the compressed kernel image should be available in the linux-4.10.8 directory at:


This is not usable yet - needs an appended Device Tree Blob. So, again from the linux-4.10.8 directory, issue:

make omap3-n900.dtb


cat arch/arm/boot/zImage arch/arm/boot/dts/omap3-n900.dtb > zImage

The resulting zImage should be bootable and stable. For further useful functions, such as battery charging and network connection, we also need the modules:

make (-jN) modules

These will be installed later, after having cross-debootstrapped the Devuan file system.

[edit] Devuan file system

For the following steps it is essential to be root on a Devuan system [5]. Let’s install some packages:

apt-get install binfmt-support qemu qemu-user-static debootstrap

From outside of the kernel directory, we create a novel directory for the N900 file system:

mkdir n900

Time to run debootstrap:

debootstrap --foreign --arch armhf jessie n900

At the end, copy ‘qemu-arm-static’ to the n900 directory, chroot there (after setting the locale as C, in order to avoid lots of warnings) and conclude the debootstrap process:

cp /usr/bin/qemu-arm-static n900/usr/bin
export LANG=C
export LC_ALL=C
chroot n900
cd debootstrap
debootstrap --second-stage
dpkg --configure -a

Time to install the kernel modules to the newly created file system:

cd linux-4.10.8
export INSTALL_MOD_PATH=/full/path/to/n900/dir
make modules_install

At this point, we have a minimal system, with a few problems and many missing features. The first problem is related to the fact that we won’t use any initrd, but boot directly into the rootfs; this seems simpler, but when the system V init process starts, no runtime file system is mounted yet (we only have the root fs; /proc, /sys, /dev and so on are missing). As a consequence, the loopback lo device may not be mounted, and udev (more precisely the net.agent) waits for it up to timeout, causing a 30 seconds delay at boot (which, by the way, precludes a complete boot if the watchdogs are in the modules).

Good news are that we don’t need udev: there won’t be hotplugged devices, and the udev’s functions may be easily performed in a different way. So let’s take rid of udev. cd to the n900 directory. First, let’s setup etc/apt/sources.list as:

# jessie
deb jessie main contrib non-free

# jessie-security, previously known as 'volatile'
deb jessie-security main contrib non-free
# jessie-updates, previously known as 'volatile'
deb jessie-updates main contrib non-free

Then, change etc/apt/preferences.d/avoid-systemd to:

Package: systemd-sysv
Pin: release o=Debian
Pin-Priority: -1

Package: udev
Pin: release o=Devuan
Pin-Priority: -1

Now, in order to perform our customisation, we have to chroot again. To this aim, we first mount the /proc file system of the host to the n900 directory, and tell the system to install packages without starting their associated services. So let’s create usr/sbin/policy-rc.d with [6]:

exit 101


chmod +x usr/sbin/policy-rc.d
mount -t proc /proc proc
chroot .

Remove udev:

apt-get purge udev

(if suggested by the system, also apt-get autoremove ).

To supply for the missing udev at boot and mount the /dev nodes anyway, we need to modify the /etc/init.d/ script (remember we are in the chroot jail, so the leading / is correct). Add the following lines:

# Mount devtmpfs on /dev
domount "$MNTMODE" devtmpfs "" /dev devtmpfs "-omode=0755,nosuid"

at the end of the mount_filesystems function (right before the closing curly bracket).

We conclude this digression on udev by updating and upgrading:

apt-get update
apt-get upgrade

The second problem of our system is SELinux. It is probably not needed (and if I am not wrong it is disabled in the kernel); in any case, at boot the /sys/fs/selinux node is not mounted, and the init process, calling a function from libselinux to check whether SELinux is enabled, tries anyway to mount the selinux filesystem: being unable to do that, it complains “loudly.” I found two solutions to this “issue:” an easy workaround, and a modification of the init code.

The workaround consists in simply creating a /selinux (empty) folder. In this way, the functions from libselinux, when they don’t find /sys/fs/selinux , mount the selinux fs at /selinux (and don’t complain anymore). Besides this, I disabled SELinux in /etc/selinux/config by changing the declaration of the SELINUX variable to:


If one does not like the /selinux workaround, it is possible to modify the init code (and disable SELinux in its config file as above). I think there is a bug here, since the code should not try to mount selinux in any case; however, the simplest thing to do is to get the source code for sysvinit and re-build it without declaring the WITH_SELINUX variable: this cancels out the pieces of code which take care of SELinux. Here however we need the arm-linux-gnueabihf toolchain, due to a missing crypt library in the arm-none-eabi one.

So let’s exit the chroot jail and the n900 directory, and do:

apt-get source sysvinit
cd sysvinit* (the actual name of the directory has the version appended)
export CC=arm-linux-gnueabihf-gcc

This should build the init code; I got a few warnings, but no errors. The new init binary is at src/init , and should replace the sbin/init in the n900 directory.

Third problem: adding a few packages. Again chroot n900 (with proc mounted). I did what follows.

passwd (to set root password)
adduser <user> (to create a std user)
apt-get install locales
apt-get install keyboard-configuration
apt-get install console-setup

To set up the correct locale, just do:

dpkg-reconfigure locales

and follow the instructions.

To set up correctly the keyboard, modify /etc/default/keyboard as follows:

XKBLAYOUT=“<two letter country code>”

and do:

dpkg-reconfigure console-setup

following the instructions. This, besides other things, runs ckbcomp to compile the XKB keyboard description to a keymap suitable for the console.

WARNING: ckbcomp reads /etc/default/keyboard , and then files in /usr/share/X11/xkb/ . The first file to be read is rules/xorg , which specifies other files to be read for the keyboard model and layout specified in /etc/default/keyboard . Based on the rules, files are read in geometry/, keycodes/ and symbols/ . At present (and probably also in the future) ckbcomp does not read files in types/ : the types for modifier keys are hardcoded in ckbcomp itself. As a consequence, there is a particular issue: the version of ckbcomp included in Jessie does not know about PC_FN_LEVEL2 , a type which allows on some keyboard layouts (e.g. Italian and German) to switch from left/right to up/down arrows by pressing (and holding) the function (blue) key. This results in a warning, and the arrow switch is instead obtained by means of shift. To correct the behaviour, I installed console-setup version 1.164 (from Debian sid), or rather installed it on Debian and copied /usr/bin/ckbcomp to the Devuan file system.

If one wants to customise the keyboard, the layout is defined in symbols/nokia_vndr/rx-51 ; edit the file [7] and run again dpkg-reconfigure console-setup .

In order to have a working power button for shutdown, we need:

apt-get install acpi-support-base

The way I will use to boot the system provides (sometimes…) a dark screen (no backlight). It is a good idea to prepare at this time a script to turn on the light: create /root/light with

echo 200 > /sys/class/backlight/acx565akm/brightness

then chmod +x /root/light and create /root/.bash_profile with


To enable battery charging, edit /etc/modules adding:


This tells the kernel to load the three needed modules.

Last thing: wireless networking. First we need the firmware (which is non-free):

apt-get install firmware-ti-connectivity

Then we need the kernel module: add


to /etc/modules . At this point the wifi interface should be ready. In order to connect to my wlan, I also set up the wpa-psk protocol [8]. This requires:

apt-get install wpasupplicant

and create /etc/network/interfaces.d/wlan0 containing

auto wlan0
iface wlan0 inet dhcp
	wpa-ssid <myssid>
	wpa-psk <mypsk>

I got <mypsk> from the Devuan host, by running (from outside the chroot):

wpa_passphrase <myssid>

and giving, at prompt, the corresponding passphrase. (Actually, I did wpa_passhprase <myssid> <mypassphrase> > /path/to/n900/etc/network/interfaces.d/wlan0 , and edited this file.)

I also added ssh support [9]:

apt-get install openssh-server

The file system should be quite ready for booting into console. Exit the chroot jail, unmount proc and remove usr/sbin/policy-rc.d .

[edit] Boot

Instead of flashing u-boot, I decided to test the system by:

  1. copying the n900 Devuan file system to sd card;
  2. uploading the kernel to memory and booting from there by using Nokia’s proprietary flasher and NOLO.

I created an ext3 partition on the sd card (ext3 is readable from Maemo, so one may change things on the new filesystem by booting into Maemo and mounting the sd card partition). I did it this way: connected the N900 to my host Devuan system by usb, turned it on and selected “Mass storage mode.” Novel nodes appear at /dev (on the host), one for the /home partition in the internal eMMC and one for the external sd card (with subnodes for the partitions). Let’s call sdb the node related to the sd card. I did (from /dev ):

parted -a optimal sdb

Then used parted commands to resize the original FAT primary partition, created an extended partition, and a logical partition inside the extended one. Lastly,

partprobe sdb

to re-read the partition table. At this point, inside /dev there should be sdb1 (primary), sdb2 (extended) and sdb5 (logical) nodes. Format sdb5 :

mkfs.ext3 sdb5

Now I created a directory /media/devuan , and mounted sdb5 there. Then cd /path/to/n900 (the new Devuan file system) and:

rsync -avh * /media/devuan

This copies the new Devuan file system to the new ext3 partition on the sd card.

My main computer is a Mac, so I downloaded Nokia’s proprietary flasher [10] (I have also a binary for 0xFFFF, built from the sources with a few modifications, but it is prone to hanging the whole system) and installed it (actually, since newer macs have some restrictions due to the System Integrity Protection, I had to unpack the files and copy them manually to proper positions. But this is another story). Then transferred the kernel zImage from the Devuan host to the Mac.

This is the way I boot Devuan on the N900: turn off the device, hold “u”, connect to Mac by usb cable, wait for the usb symbol to appear on the top right corner (usually with backlight off) and on the Mac, from the directory containing the kernel zImage, issue:

flasher-3.5 -k zImage -l
flasher-3.5 -b”rootwait root=/dev/mmcblk0p5 rw”

or, in a single line,

flasher-3.5 -k zImage -l -b”rootwait root=/dev/mmcblk0p5 rw”

Explanation: the first command uploads the kernel to ram. The second tells NOLO to boot it and pass to the kernel the specified command line. There, rootwait tells the kernel to wait for the root file system to be mounted before passing it the control, the root parameter tells the location of the root fs ( mmcblk0 is the sd card, in contrast to Maemo, where it is mmcblk1 ; p5 is the partition where the Devuan fs is located) and the rw flag tells to mount the root fs as read and write.

This is what happens: the usb symbol on the N900 screen disappears, after about one second the Nokia logo fades out, and after a while messages appear on the screen reporting a regular kernel boot, ending in a login prompt. One should be able to login as root with the password previously specified, and then issue “light” to run the script which turns on the screen backlight. Also, the device should appear on the wlan with a dynamically assigned IP address; I can issue (on the Mac, as standard user - with the same name as the standard user previously defined for the N900 Devuan file system):

ssh <N900 IP>

enter the standard user’s password, get a remote shell, issue “su”, enter the root password, and enjoy the new system from my Mac.

There are indeed a few things to tune, and I still miss a desktop environment.

[edit] Footnotes

  4. Actually the system may boot also with the watchdogs in the modules, but in this case it is very unstable: to avoid reboot, the watchdogs need to be loaded in a few seconds, which may not be possible in some cases (namely, I had a problem due to udev waiting for the loopback lo device to be mounted, which did not happen due to the lack of an initrd).
  10. It may be found at