Installer Debian Stretch sur un Orange Pi PC2

toolchain

On travaille sur une architecture amd64 et l’Orange Pi PC2 a une architecture ARM 64 bits, également appelée AARCH64.

On va donc commencer par installer une toolchain Linaro. On télécharge l’archive binaire sur le site, actuellement la version 6.3.1

On l’extrait sous $HOME, ce qui crée le répertoire gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/, que l’on ajoute au PATH

export PATH=~/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin:$PATH

Les exécutables sont préfixés par aarch64-linux-gnu- qu’il faudra indiquer pour les cross-compilations.

u-boot + atf

Pour booter, l’Orange Pi PC2 a besoin de u-boot mais également de arm-trusted-firmware.

Dans un premier temps, on clone les dépôts git correspondant

git clone git://git.denx.de/u-boot.git
git clone https://github.com/apritzel/arm-trusted-firmware.git

On commence par construire le firmware bl31.bin en se mettant sur la branche allwinner ou allwinner-stable selon les gouts et on le copie dans le répertoire u-boot:

cd arm-trusted-firmware
git checkout allwinner-stable
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j4 PLAT=sun50iw1p1 bl31
cp build/sun50iw1p1/release/bl31.bin ~/u-boot

On va dans le répertoire u-boot et on se positionne sur la dernière release stable, actuellement la v2017.09. Par exemple

cd ~/u-boot
git co v2017.09

Note : le support de l’Orange Pi PC2 existe seulement depuis la v2017.07.

Puis on lance la compilation. La variable CROSS_COMPILE contient le préfixe correspondant aux outils de la toolchain installés précédemment

make ARCH=arm CROSS_COMPILE=aarch64-linux-gnu- orangepi_pc2_defconfig
make ARCH=arm CROSS_COMPILE=aarch64-linux-gnu- -j4

A la fin de la compilation, on va concaténer tous les fichiers utiles en un seul que l’on va écrire sur la carte SD

cat spl/sunxi-spl.bin u-boot.itb > u-boot-sunxi-with-spl.bin
dd if=u-boot-sunxi-with-spl.bin of=/dev/sdX bs=8k seek=1

Dans la commande ci-dessus, il faut remplacer /dev/sdX par le device correspondant à la carte SD (/dev/sdb ou /dev/mmcblk0 par exemple).

Noyau Linux

Actuellement, le noyau mainline ne supporte pas complétement l’Orange Pi PC2. Il manque en particulier le support de l’interface Ethernet.

Heureusement, il existe une version patchée à l’adresse suivante.

On commence donc par cloner cette version et se positioner sur la branche souhaitée (ici orange-pi-4.13)

git clone https://github.com/megous/linux
cd linux
git checkout origin/orange-pi-4.13

On exécute ensuite un script de compilation du noyau qui utilise la configuration par défaut (linux-4.13-64) et qui permet de compiler dans un répertoire dédié (KBUILD_OUTPUT) et d’obtenir les binaires utiles dans OUT/IMAGE.

OUT=$HOME/build64
IMAGE=v1
export KERNEL_DIR=$HOME/linux
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
export KBUILD_OUTPUT=$OUT/.tmp/linux-arm64

cp -v "$KERNEL_DIR/linux-4.13-64" "$KBUILD_OUTPUT/.config"
make -C $KERNEL_DIR -j4 clean
make -C $KERNEL_DIR -j4 Image dtbs

mkdir -p $OUT/$IMAGE
cp -f $KBUILD_OUTPUT/arch/arm64/boot/Image $OUT/$IMAGE
cp -f $KBUILD_OUTPUT/.config $OUT/$IMAGE/linux.config
cp -f $KBUILD_OUTPUT/arch/arm64/boot/dts/allwinner/sun50i-h5-orangepi-pc2.dtb $OUT/$IMAGE/board.dtb

A la fin de la compilation, on obtient 3 fichiers utiles dans le répertoire OUT, que l’on va copier sur la carte SD.

Partitionnement de la carte SD

On considère que la carte SD est le device /dev/sdX. On va créer une partition de boot en format FAT, un swap et la partition root.

La parition boot va contenir le noyau, le device tree et le script de configuration.

La partition root va être constitué d’un système de fichiers en F2FS, a priori plus adapté à une carte SD que le format ext4. C’est une des raisons de l’existence de la partition FAT : u-boot saurait se contenter d’une partition unique ext4 mais, à ma connaissance, pas en F2FS. De plus, ce n’est pas très génant de séparer boot et root.

Device           Start      End  Sectors  Size Id Type
/dev/sdX1         2048   264191   262144  128M  c W95 FAT32 (LBA)
/dev/sdX2       264192  2361343  2097152    1G 82 Linux swap / Solaris
/dev/sdX3      2361344 31116287 28754944 13,7G 83 Linux

Création et montage des systèmes de fichiers

sudo mkfs.vfat /dev/sdX1
sudo mkswap /dev/sdX2
sudo mkfs.f2fs /dev/sdb3

sudo mount /dev/sdX1 /mnt/tmp1
sudo mount /dev/sdX3 /mnt/tmp3

Partition boot

Depuis le répertoire OUT utilisé pour la compilation du noyau, on copie les 3 fichiers présents

cp board.dtb  Image linux.config /mnt/tmp1

Reste le fichier de configuration de boot. On édite le fichier boot.cmd avec le contenu suivant

# default values
setenv load_addr "0x44000000"
setenv rootdev "/dev/mmcblk0p3"
setenv verbosity "1"
setenv rootfstype "f2fs"
setenv console "serial"

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

echo "Boot script loaded from ${devtype}"

if load ${devtype} 0 ${load_addr} env.txt; then
env import -t ${load_addr} ${filesize}
fi

# temp fix: increase cpufreq and bus clock / speeds things up with vanilla images
if test "${cpufreq_hack}" = "on"; then
mw.l 0x1c2005c 1
mw.l 0x1c20000 0x80001010
fi

# No display driver yet
if test "${console}" = "display" || test "${console}" = "both"; then setenv consoleargs "console=tty1"; fi
if test "${console}" = "serial" || test "${console}" = "both"; then setenv consoleargs "${consoleargs} console=ttyS0,115200"; fi

setenv bootargs "root=${rootdev} noinitrd rootwait rootfstype=${rootfstype} ${consoleargs} panic=10 consoleblank=0 enforcing=0 loglevel=${verbosity} ${extraargs} ${extraboardargs}"
load ${devtype} 0 ${fdt_addr_r} board.dtb
fdt addr ${fdt_addr_r}
fdt resize
load ${devtype} 0 ${kernel_addr_r} Image
booti ${kernel_addr_r} - ${fdt_addr_r}

# Recompile with:
# mkimage -C none -A arm -T script -d boot.cmd boot.scr

Ce script est repris, en l’adaptant, du script de démarrage utilisé par armbian.

Il faut ensuite compiler ce fichier par la commande suivante

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

Si la commande mkimage n’existe pas, il faut installer le package u-boot-tools.

On copie également les deux fichiers sur la partition boot de la carte SD

cp boot.cmd boot.scr /mnt/tmp1

Partition rootfs

On a de quoi booter un noyau mais il faut maintenant créer un système complet dans la partition root. Le choix fait ici est de créer un système Debian en utilisant la commande multistrap. On aura également besoin de qemu-aarch64-static pour émuler un processeur ARM 64 bits et chrooter dans le rootfs.

On commence donc par installer les packages

apt install multistrap qemu-user-static qemu-system-arm

Il faut ensuite un fichier de configuration pour préparer les packages debian que l’on veut installer sur l’image.

Fichier arm64.conf

    [general]
cleanup=true
noauth=true
unpack=true
debootstrap=Debian Net Utils Init

[Debian]
packages=apt kmod lsof apt-utils
source=http://deb.debian.org/debian
keyring=debian-archive-keyring
suite=stretch
components=main contrib non-free

[Net]
packages=netbase net-tools ethtool udev iproute iputils-ping ifupdown isc-dhcp-client ssh ca-certificates openssl chrony
source=http://deb.debian.org/debian

[Utils]
packages=locales adduser vim zsh less wget dialog usbutils rsync tmux git
source=http://deb.debian.org/debian

[Init]
packages=init systemd
source=http://deb.debian.org/debian

Pour créer les devices présents dans /dev/ dans l’image, on va utiliser le script device-table.pl fourni avec multistrap. Auparavant, il faut fournir la liste des devices à créer dans un fichier dev_table

#<name>         <type>  <mode>  <uid>   <gid>   <major> <minor> <start> <inc> <count>
/dev    d       755     0       0       -       -       -       -       -
/dev/mem        c       640     0       0       1       1       0       0      -
/dev/kmem       c       640     0       0       1       2       0       0      -
/dev/null       c       640     0       0       1       3       0       0      -
/dev/zero       c       640     0       0       1       5       0       0      -
/dev/random     c       640     0       0       1       8       0       0      -
/dev/urandom    c       640     0       0       1       9       0       0      -
/dev/tty        c       666     0       0       5       0       0       0      -
/dev/tty        c       666     0       0       4       0       0       1      6
/dev/console    c       640     0       0       5       1       0       0      -
/dev/ram        b       640     0       0       1       1       0       0      -
/dev/ram        b       640     0       0       1       0       0       1      4
/dev/loop       b       640     0       0       7       0       0       1      2
/dev/ptmx       c       666     0       0       5       2       0       0      -

Pour simplifier l’installation, on regroupe ensuite les différentes opérations dans un script à exécuter en tant que root

#!/bin/sh
ARCH=arm64
QEMU=/usr/bin/qemu-aarch64-static
ROOTFS_DIR="/images/debian_arm64"
HOSTNAME="opipc2"
ROOTDEV="/dev/mmcblk0p3"
BOOTDEV="/dev/mmcblk0p1"
ID_RSA_PUB="ssh-rsa AAAAB3...JglcE61D36Xs1aWF37Q9DKP54L user@example"

rm -rf $ROOTFS_DIR
/usr/share/multistrap/device-table.pl --no-fakeroot -d $ROOTFS_DIR -f dev_table
multistrap -d $ROOTFS_DIR -a $ARCH -f $ARCH.conf

cp $QEMU $ROOTFS_DIR/usr/bin
export DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true
export LC_ALL=C LANGUAGE=C LANG=C
chroot $ROOTFS_DIR /var/lib/dpkg/info/dash.preinst install
chroot $ROOTFS_DIR dpkg --configure -a

#users
chroot $ROOTFS_DIR useradd -m -s /bin/zsh -U -G sudo -u 1000 philippe
chroot $ROOTFS_DIR usermod -p '$1$SzENG9k7$BosCWiw59HnQsYiZse4nU/' root

#hostname
filename=$ROOTFS_DIR/etc/hostname
echo $HOSTNAME > $filename

#network interfaces
filename=$ROOTFS_DIR/etc/network/interfaces
echo auto eth0 >> $filename
echo iface eth0 inet dhcp >> $filename

#fstab
filename=$ROOTFS_DIR/etc/fstab
echo $BOOTDEV /boot vfat noatime 0 1 > $filename
echo $ROOTDEV /     ext4 noatime 0 1 >> $filename

#apt sources
filename=$ROOTFS_DIR/etc/apt/sources.list
echo deb http://deb.debian.org/debian         stretch main > $filename
echo deb http://deb.debian.org/security   stretch/updates main >> $filename
echo deb http://deb.debian.org/debian         stretch-updates main >> $filename

# ssh
mkdir -p $ROOTFS_DIR/root/.ssh
chmod 700 $ROOTFS_DIR/root/.ssh
echo $ID_RSA_PUB >> $ROOTFS_DIR/root/.ssh/authorized_keys

Il suffit ensuite de recopier ce rootfs sur la partition root, par exemple en utilisant la commande rsync -av.