home / blog index | Devuan on Orange Pi

How to install Devuan or almost anything Debian based on Orange Pi PC/One/Lite/Whatever

sdcard will only have one partition - / aka rootfs. We will borrow kernel, device trees, u-boot, headers and few scripts from Armbian project, so we won’t have to compile anything.

You can set up separate /boot partition and encrypted LVM if you want. This guide will only cover first case, but if you want to try encryption you should know these kernel cmdline arguments.

root=/dev/mapper/pi--vg-root
cryptdevice=UUID=<uuid after mounting mapped, decrypted lv>:pi--vg
cryptdevice=/dev/mmcblk0p2:pi--vg

Preparing rootfs.

We will use Debian tool called debootstrap which allows us to create rootfs for almost every distro using apt/dpkg.

Prepare debootstrap and armhf emulator

Your distribution might not have installed debootstrap by default. Install it with

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

debootstrapping - stage 1

Let’s create a directory, where we can keep our rootfs temporarily.

# mkdir rootfs_opi

Now we can select, which distro we want to install. I’m going with Devuan Chimaera which is based on Debian Bullseye. Let’s start with this command:

# debootstrap --arch=armhf --foreign chimaera /path/to/rootfs_opi/

debootstrapping - stage 2

Let’s move armhf qemu emulator to rootfs temporarily.

# cp /usr/bin/qemu-arm-static rootfs_opi/usr/bin/

Now let’s chroot into rootfs.

# chroot rootfs_opi /usr/bin/qemu-arm-static /bin/sh -i

Let’s start stage 2 of debootstrapping.

# /debootstrap/debootstrap --second-stage

You might want to input DNS IP address to /etc/resolv.conf if you have connection issues.

Set root password

First, we have to set root password, if you want to share your rootfs it will be good idea to set it to toor.

# passwd

Setup /etc/fstab

Open /etc/fstab with nano or vim and paste this:

/dev/mmcblk0p1 / ext4 defaults,noatime,nodiratime,commit=600,errors=remount-ro 0 1
tmpfs /tmp tmpfs defaults,nosuid 0 0

This will mount the first ext4 partition which contains rootfs as / and create tmpfs at /tmp

Setup serial console

Devuan uses sysvinit, so we have to paste this line:

T0:2345:respawn:/sbin/getty -L ttyS0 115200 vt100

at the end of /etc/inittab file.

Setup locales

Run:

# apt install locales
# dpkg-reconfigure locales

Choose en_US.UTF-8 and your native language if you want.

Setup DHCP by default.

Your /etc/network/interfaces should look like this:

# interfaces(5) file used by ifup(8) and ifdown(8)
# Include files from /etc/network/interfaces.d:
source /etc/network/interfaces.d/*

auto lo
iface lo inet loopback

auto eth0
allow-hotplug eth0
iface eth0 inet dhcp

Setup sources.list

Open /etc/apt/sources.list with nano/vim/whatever set it’s contents to this:

# Main
deb http://deb.devuan.org/merged chimaera main non-free contrib
deb-src http://deb.devuan.org/merged chimaera main non-free contrib

# Security
deb http://deb.devuan.org/merged chimaera-security main non-free contrib
deb-src http://deb.devuan.org/merged chimaera-security main non-free contrib

# Updates
deb http://deb.devuan.org/merged chimaera-updates main non-free contrib
deb-src http://deb.devuan.org/merged chimaera-updates main non-free contrib

# Armbian (U-Boot, Kernel and device trees)
deb http://apt.armbian.com bullseye main bullseye-utils bullseye-desktop

Run

# apt update
# apt install wget
# wget -q -O - https://mirrors.dotsrc.org/armbian-apt/armbian.key | apt-key add -
# apt update

Setup OpenSSH server

Installing server

Run:

# apt install openssh-server

Remove unnecessary X11 packages

For some reason, installing openssh-server wants to install many unnecessary X11 packages like fonts and icon packs. If you aren’t planning on using X11 then running

# apt autoremove --purge *icon-theme

will be a good idea, especially if you are going to share your rootfs.

Configure SSH

Open /etc/ssh/sshd_config and set

PermitRootLogin yes
PasswordAuthentication yes

Entropy seeding

It’s good idea on virtual servers and SBCs to install some tool like haveged to seed entropy.

# apt install haveged

NTP client and timezone

Most boards require NTP client, or you will live in 1970 with broken SSL.

# apt install ntp

Now change timezone.

# ln -sf /usr/share/zoneinfo/Region/City /etc/localtime

I live in Poland so I have to run

# ln -sf /usr/share/zoneinfo/Europe/Warsaw /etc/localtime

Hostname

By default, debootstrap will set hostname same as the system that we are using to bootstrap. So open /etc/hostname and change its contents to for example pi or name of your cat if you have one :cat:

Install kernel, device tree, and few other things.

Here we have a listing of all boards supported by Armbian project. We will need linux-image, linux-headers, linux-dtb, u-boot and few other things.

I have Orange Pi One so I have to execute

# apt install armbian-firmware linux-dtb-current-sunxi linux-headers-current-sunxi linux-image-current-sunxi linux-u-boot-orangepione-current sunxi-tools initramfs-tools u-boot-tools

Setup Armbian scripts for kernel and initramfs.

initramfs

Open /etc/initramfs/post-update.d/99-uboot and paste this:

#!/bin/sh

echo "update-initramfs: Converting to u-boot format" >&2
tempname="/boot/uInitrd-$1"
mkimage -A arm -O linux -T ramdisk -C gzip -n uInitrd -d $2 $tempname > /dev/null

ln -sf $(basename $tempname) /boot/uInitrd > /dev/null 2>&1 || mv $tempname /boot/uInitrd
exit 0

You might want to change arm in mkimage line to arm64. (I’m not sure if it’s actually going to be arm64 tho :flushed:)

kernel

Open /etc/kernel/preinst.d/initramfs-cleanup and paste this:

#!/bin/sh

version="$1"

[ -x /usr/sbin/update-initramfs ] || exit 0

# passing the kernel version is required
if [ -z "$version" ]; then
    echo >&2 "W: initramfs-tools: ${DPKG_MAINTSCRIPT_PACKAGE:-kernel package} did not pass a version number"
    exit 0
fi

# avoid running multiple times
if [ -n "$DEB_MAINT_PARAMS" ]; then
    eval set -- "$DEB_MAINT_PARAMS"
    if [ -z "$1" ] || [ "$1" != "upgrade" ]; then
        exit 0
    fi
fi

STATEDIR=/var/lib/initramfs-tools

version_list="$(ls -1 "$STATEDIR" | linux-version sort --reverse)"

for v in $version_list; do
    if ! linux-version compare $v eq $version; then
        # try to delete delete old initrd images via update-initramfs
        INITRAMFS_TOOLS_KERNEL_HOOK=y update-initramfs -d -k $v 2>/dev/null
        # delete unused state files
        find $STATEDIR -type f ! -name "$version" -printf "Removing obsolete file %f\n" -delete
        # delete unused initrd images
        find /boot -name "initrd.img*" -o -name "uInitrd-*" ! -name "*$version" -printf "Removing obsolete file %f\n" -delete
    fi
done

exit 0

Scripts are taken from Armbian project. Make both scripts executable with chmod +x Now, regenerate initramfs with:

# update-initramfs -u

Setup u-boot

Create file /boot/armbianEnv.txt and paste this (you might want to change overlay_prefix to match your board):

verbosity=1
bootlogo=false
console=both
disp_mode=1920x1080p60
overlay_prefix=sun8i-h3
rootdev=/dev/mmcblk0p1
rootfstype=ext4
usbstoragequirks=0x2537:0x1066:u,0x2537:0x1068:u

Create file /boot/boot.cmd and paste this:

# DO NOT EDIT THIS FILE
#
# Please edit /boot/armbianEnv.txt to set supported parameters
#

setenv load_addr "0x45000000"
setenv overlay_error "false"
# default values
setenv verbosity "1"
setenv console "both"
setenv disp_mem_reserves "off"
setenv disp_mode "1920x1080p60"
setenv rootfstype "ext4"
setenv docker_optimizations "on"
setenv bootlogo "false"
setenv devnum "0"
setenv rootdev "/dev/mmcblk${devnum}p1"
setenv earlycon "off"

# Print boot source
itest.b *0x28 == 0x00 && echo "U-boot loaded from SD"
itest.b *0x28 == 0x02 && echo "U-boot loaded from eMMC or secondary SD"
itest.b *0x28 == 0x03 && echo "U-boot loaded from SPI"

# get PARTUUID of first partition on SD/eMMC it was loaded from
# mmc 0 is always mapped to device u-boot (2016.09+) was loaded from
if test "${devtype}" = "mmc"; then
    part uuid mmc ${devnum}:1 partuuid;
    setenv devnum ${mmc_bootdev}
    setenv rootdev "/dev/mmcblk${mmc_bootdev}p1"
fi

echo "Boot script loaded from ${devtype}"

if test -e ${devtype} ${devnum} ${prefix}armbianEnv.txt; then
    load ${devtype} ${devnum} ${load_addr} ${prefix}armbianEnv.txt
    env import -t ${load_addr} ${filesize}
fi

if test "${console}" = "display" || test "${console}" = "both"; then setenv consoleargs "console=ttyS0,115200 console=tty1"; fi
if test "${console}" = "serial"; then setenv consoleargs "console=ttyS0,115200"; fi
if test "${earlycon}" = "on"; then setenv consoleargs "earlycon ${consoleargs}"; fi
if test "${bootlogo}" = "true"; then setenv consoleargs "bootsplash.bootfile=bootsplash.armbian ${consoleargs}"; fi

setenv bootargs "root=${rootdev} rootwait rootfstype=${rootfstype} ${consoleargs} hdmi.audio=EDID:0 disp.screen0_output_mode=${disp_mode} consoleblank=0 loglevel=${verbosity} ubootpart=${partuuid} ubootsource=${devtype} usb-storage.quirks=${usbstoragequirks} ${extraargs} ${extraboardargs}"

if test "${disp_mem_reserves}" = "off"; then setenv bootargs "${bootargs} sunxi_ve_mem_reserve=0 sunxi_g2d_mem_reserve=0 sunxi_fb_mem_reserve=16"; fi
if test "${docker_optimizations}" = "on"; then setenv bootargs "${bootargs} cgroup_enable=memory swapaccount=1"; fi

load ${devtype} ${devnum} ${ramdisk_addr_r} ${prefix}uInitrd
load ${devtype} ${devnum} ${kernel_addr_r} ${prefix}zImage

if test -e ${devtype} ${devnum} "${prefix}.next"; then
    echo "Found mainline kernel configuration"
    load ${devtype} ${devnum} ${fdt_addr_r} ${prefix}dtb/${fdtfile}
    fdt addr ${fdt_addr_r}
    fdt resize 65536
    for overlay_file in ${overlays}; do
        if load ${devtype} ${devnum} ${load_addr} ${prefix}dtb/overlay/${overlay_prefix}-${overlay_file}.dtbo; then
            echo "Applying kernel provided DT overlay ${overlay_prefix}-${overlay_file}.dtbo"
            fdt apply ${load_addr} || setenv overlay_error "true"
        fi
    done
    for overlay_file in ${user_overlays}; do
        if load ${devtype} ${devnum} ${load_addr} ${prefix}overlay-user/${overlay_file}.dtbo; then
            echo "Applying user provided DT overlay ${overlay_file}.dtbo"
            fdt apply ${load_addr} || setenv overlay_error "true"
        fi
    done
    if test "${overlay_error}" = "true"; then
        echo "Error applying DT overlays, restoring original DT"
        load ${devtype} ${devnum} ${fdt_addr_r} ${prefix}dtb/${fdtfile}
    else
        if load ${devtype} ${devnum} ${load_addr} ${prefix}dtb/overlay/${overlay_prefix}-fixup.scr; then
            echo "Applying kernel provided DT fixup script (${overlay_prefix}-fixup.scr)"
            source ${load_addr}
        fi
        if test -e ${devtype} ${devnum} ${prefix}fixup.scr; then
            load ${devtype} ${devnum} ${load_addr} ${prefix}fixup.scr
            echo "Applying user provided fixup script (fixup.scr)"
            source ${load_addr}
        fi
    fi
    bootz ${kernel_addr_r} ${ramdisk_addr_r} ${fdt_addr_r}
else
    echo "Found legacy kernel configuration"
    load ${devtype} ${devnum} ${fdt_addr_r} ${prefix}script.bin
    bootz ${kernel_addr_r} ${ramdisk_addr_r}
fi

Compile boot.scr

Just run:

# mkimage -C none -A arm -T script -d /boot/boot.cmd /boot/boot.scr

Cleanup rootfs

While you’re still in chroot, run:

# apt clean
# exit

Now that we quit rootfs, let’s remove QEMU, DNS that we set up manually and root .bash_history if you switched to bash.

# rm /path/to/rootfs_opi/etc/resolv.conf \
/path/to/rootfs_opi/usr/bin/qemu-arm-static \
/root/.bash_history

Tarring up the result

Run:

cd /path/to/rootfs_opi
tar -cjpf ~/sunxi_rootfs.tar.bz2 .

Preparing bootable sdcard

rootfs

Using tool like gparted create new MSDOS/MBR partition table on your sdcard and create single partition with ext4 file system. /dev/mmcblk0p1 should contain ext4. Now, mount /dev/mmcblk0p1 at for example /mnt (if you don’t have anything mounted here already ofc)

mount /dev/mmcblk0p1 /mnt

And unpack your tarball to /mnt

tar -C /mnt/ -xjpf ~/sunxi_rootfs.tar.bz2

u-boot mbr

Inside your rootfs, there is an image of u-boot that we have to burn into sdcard mbr. In case of Orange Pi One it’s located at:

/usr/lib/linux-u-boot-current-orangepione_21.02.3_armhf/u-boot-sunxi-with-spl.bin

Copy that file into a more accessible directory if you want.

Now let’s clean mbr (without destroying partition table) on sdcard.

dd if=/dev/zero of=/dev/mmcblk0 bs=1k count=1023 seek=1 status=noxfer

And now - let’s burn u-boot!

dd if=./u-boot-sunxi-with-spl.bin of=/dev/mmcblk0 bs=1024 seek=8 status=noxfer

That’s all! Your Orange Pi should properly boot now. If I forgot something then please send me a mail or DM on some messaging platform and I’ll update this article.

I’ve used Orange Pi docs and linux-sunxi.org wiki as reference and added steps necessary to use Armbian packages.