Creando una imagen de Ubuntu para ARM “desde cero”

Cuando el desarrollo recién comienza, a menudo no está claro qué paquetes irán a los rootfs de destino.

En otras palabras, es demasiado pronto para adquirir LFS, buildroot o yocto (u otra cosa), pero ya debes comenzar. Para los ricos (tengo eMMC de 4 GB en muestras piloto) hay una manera de distribuir a los desarrolladores una distribución que les permitirá entregar rápidamente algo que falta actualmente, y luego siempre podemos recopilar listas de paquetes y crear una lista para los rootfs de destino.

Este artículo no es nuevo y es una simple instrucción de copiar y pegar.

El propósito del artículo es construir rootfs de Ubuntu para placas ARM (en mi caso, basados ​​en Colibri imx7d).

Construyendo una imagen

Ensamblamos los rootfs de destino para la replicación.

Descomprimiendo la base de Ubuntu

Nosotros mismos elegimos la versión según nuestras necesidades y preferencias. Aquí he dado 20.

$ mkdir ubuntu20
$ cd ubuntu20
$ mkdir rootfs
$ wget http://cdimage.ubuntu.com/ubuntu-base/releases/20.04/release/ubuntu-base-20.04-base-armhf.tar.gz
$ tar xf ubuntu-base-20.04-base-armhf.tar.gz -C rootfs

Comprobando el soporte de BINFMT en el kernel

Si tiene una distribución común, entonces hay soporte para BINFMT_MISC y todo está configurado; si no, estoy seguro de que sabe cómo habilitar el soporte BINFMT en el kernel.

Asegúrese de que BINFMT_MISC esté habilitado en el kernel:

$ zcat /proc/config.gz | grep BINFMT
CONFIG_BINFMT_ELF=y
CONFIG_COMPAT_BINFMT_ELF=y
CONFIG_BINFMT_SCRIPT=y
CONFIG_BINFMT_MISC=y

Ahora necesitas verificar la configuración:

$ ls /proc/sys/fs/binfmt_misc
qemu-arm  register  status
$ cat /proc/sys/fs/binfmt_misc/qemu-arm
enabled
interpreter /usr/bin/qemu-arm
flags: OC
offset 0
magic 7f454c4601010100000000000000000002002800
mask ffffffffffffff00fffffffffffffffffeffffff

Puede registrarse manualmente utilizando, por ejemplo, aquí están estas instrucciones.

Configurando el brazo estático qemu

Ahora necesitamos una instancia de qemu ensamblada estáticamente.

!!! ¡¡¡ATENCIÓN!!!
Si planeas usar un contenedor para construir algo, consulta:
https://sourceware.org/bugzilla/show_bug.cgi?id=23960
https://bugs.launchpad.net/qemu/+bug/1805913
Luego, para el host x86_64 y el invitado armado, debe usar la versión i386 de qemu:
http://ftp.ru.debian.org/debian/pool/main/q/qemu/qemu-user-static_5.0-13_i386.deb

$ wget http://ftp.debian.org/debian/pool/main/q/qemu/qemu-user-static_5.0-13_amd64.deb
$ alient -t qemu-user-static_5.0-13_amd64.deb
# путь в rootfs и имя исполняемого файла должно совпадать с /proc/sys/fs/binfmt_misc/qemu-arm
$ mkdir qemu
$ tar xf qemu-user-static-5.0.tgz -C qemu
$ file qemu/usr/bin/qemu-arm-static
qemu/usr/bin/qemu-arm-static: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=be45f9a321cccc5c139cc1991a4042907f9673b6, for GNU/Linux 3.2.0, stripped
$ cp qemu/usr/bin/qemu-arm-static rootfs/usr/bin/qemu-arm
$ file rootfs/usr/bin/qemu-arm
rootfs/usr/bin/qemu-arm: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=be45f9a321cccc5c139cc1991a4042907f9673b6, for GNU/Linux 3.2.0, stripped

chroot

Guión sencillo:

ch-montaje.sh

#!/bin/bash

function mnt() {
    echo "MOUNTING"
    sudo mount -t proc /proc proc
    sudo mount --rbind /sys sys
    sudo mount --make-rslave sys
    sudo mount --rbind /dev dev
    sudo mount --make-rslave dev
    sudo mount -o bind /dev/pts dev/pts
    sudo chroot 
}

function umnt() {
    echo "UNMOUNTING"
    sudo umount proc
    sudo umount sys
    sudo umount dev/pts
    sudo umount dev

}

if [ "$1" == "-m" ] && [ -n "$2" ] ;
then
    mnt $1 $2
elif [ "$1" == "-u" ] && [ -n "$2" ];
then
    umnt $1 $2
else
    echo ""
    echo "Either 1'st, 2'nd or both parameters were missing"
    echo ""
    echo "1'st parameter can be one of these: -m(mount) OR -u(umount)"
    echo "2'nd parameter is the full path of rootfs directory(with trailing '/')"
    echo ""
    echo "For example: ch-mount -m /media/sdcard/"
    echo ""
    echo 1st parameter : 
    echo 2nd parameter : 
fi

Admiramos el resultado:

$ ./ch-mount.sh -m rootfs/
# cat /etc/os-release
NAME="Ubuntu"
VERSION="20.04 LTS (Focal Fossa)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 20.04 LTS"
VERSION_ID="20.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=focal
UBUNTU_CODENAME=focal
# uname -a
Linux NShubin 5.5.9-gentoo-x86_64 #1 SMP PREEMPT Mon Mar 16 14:34:52 MSK 2020 armv7l armv7l armv7l GNU/Linux

Solo por diversión, midamos el tamaño antes y después de instalar el conjunto mínimo (para mí) de paquetes:

# du -d 0 -h / 2>/dev/null
63M     /

Actualicemos:

# apt update
# apt upgrade --yes

Instalamos los paquetes que nos interesan:

# SYSTEMD_IGNORE_CHROOT=yes apt install --yes autoconf kmod socat ifupdown ethtool iputils-ping net-tools ssh g++ iproute2 dhcpcd5 incron ser2net udev systemd gcc minicom vim cmake make mtd-utils util-linux git strace gdb libiio-dev iiod

Los archivos y módulos de encabezado del kernel son un asunto aparte. Por supuesto, no instalaremos el gestor de arranque, el kernel, los módulos ni el árbol de dispositivos a través de Ubuntu. Nos llegarán de fuera o las montaremos nosotros mismos o nos las entregará el fabricante de la placa, en cualquier caso esto escapa al alcance de esta instrucción.

Hasta cierto punto, la divergencia de versiones es aceptable, pero es mejor tomarlas de la compilación del kernel.

# apt install --yes linux-headers-generic

Veamos qué pasó y resultó mucho:

# apt clean
# du -d 0 -h / 2>/dev/null
770M    /

No olvide establecer una contraseña.

Empaquetando la imagen

$ sudo tar -C rootfs --transform "s|^./||" --numeric-owner --owner=0 --group=0 -c ./ | tar --delete ./ | gzip > rootfs.tar.gz

Además, podemos instalar etckeeper con la configuración de autopush.

Bueno, digamos que distribuimos nuestro ensamblaje, comenzó el trabajo sobre la mejor manera de ensamblar posteriormente diferentes versiones de nuestro sistema.

etckeeper puede venir en nuestra ayuda.

La seguridad es un asunto personal:

  • puedes proteger ciertas ramas
  • generar una clave única para cada dispositivo
  • desactivar el empuje forzado
  • etc. ...
# ssh-keygen
# apt install etckeeper
# etckeeper init
# cd /etc
# git remote add origin ...

Configuremos el autopush

Por supuesto, podemos crear ramas en el dispositivo con anticipación (digamos que creamos un script o servicio que se ejecutará la primera vez que se inicie).

# cat /etc/etckeeper/etckeeper.conf
PUSH_REMOTE="origin"

O podemos hacer algo más inteligente...

manera perezosa

Tengamos algún tipo de identificador único, digamos el número de serie del procesador (o MAC; las empresas serias compran la gama):

cat / proc / cpuinfo

# cat /proc/cpuinfo
processor       : 0
model name      : ARMv7 Processor rev 5 (v7l)
BogoMIPS        : 60.36
Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm 
CPU implementer : 0x41
CPU architecture: 7
CPU variant     : 0x0
CPU part        : 0xc07
CPU revision    : 5

processor       : 1
model name      : ARMv7 Processor rev 5 (v7l)
BogoMIPS        : 60.36
Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm 
CPU implementer : 0x41
CPU architecture: 7
CPU variant     : 0x0
CPU part        : 0xc07
CPU revision    : 5

Hardware        : Freescale i.MX7 Dual (Device Tree)
Revision        : 0000
Serial          : 06372509

Luego podemos usarlo para el nombre de la rama a la que enviaremos:

# cat /proc/cpuinfo | grep Serial | cut -d':' -f 2 | tr -d [:blank:]
06372509

Creemos un script simple:

# cat /etc/etckeeper/commit.d/40myown-push
#!/bin/sh
set -e

if [ "$VCS" = git ] && [ -d .git ]; then
  branch=$(cat /proc/cpuinfo | grep Serial | cut -d':' -f 2 | tr -d [:blank:])
  cd /etc/
  git push origin master:${branch}
fi

Y eso es todo: después de un tiempo podremos ver los cambios y crear una lista de paquetes para el firmware de destino.

Materiales recomendados

BINFMT_MISC
Soporte del kernel para diversos formatos binarios (binfmt_misc)
Compilando con el usuario chroot de qemu
Construyendo rootfs de Ubuntu para ARM
Cómo crear un Ubuntu live personalizado desde cero
Crossdev qemu-estático-usuario-chroot
guardián

problema getdents64

readdir() devuelve NULL (errno=EOVERFLOW) para qemu estático de usuario de 32 bits en un host de 64 bits
Ext4 64 bits hash rompe 32 bits glibc 2.28+
compiler_id_detection falla para armhf cuando se usa la emulación del modo de usuario QEMU
CMake no funciona correctamente bajo qemu-arm

Fuente: habr.com