Criação de uma imagem Ubuntu para ARM do zero

Quando o desenvolvimento começa, geralmente ainda não está claro quais pacotes serão utilizados no rootfs alvo.

Em outras palavras, não é hora de pegar LFS, buildroot ou yocto (ou algo assim), mas já é preciso começar. Para os mais afortunados (eu tenho 4GB de eMMC nos meus protótipos), existe a opção de fornecer aos desenvolvedores uma distribuição que possibilite rapidamente instalar o que falta no momento, e então sempre podemos compilar listas de pacotes e criar uma lista para o rootfs alvo.

Este artigo não traz novidades e consiste em uma simples instrução de copiar e colar.

O objetivo do artigo é compilar o rootfs do Ubuntu para a placa ARM (no meu caso, baseada no Colibri imx7d).

Compilação da imagem

Estamos compilando o rootfs alvo para ser replicado.

Desempacotando o Ubuntu Base

Nós escolhemos a versão de acordo com a necessidade e preferências pessoais. Aqui, eu listei 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

Verificando o suporte ao BINFMT no kernel

Se você estiver usando uma distribuição comum, o suporte ao BINFMT_MISC estará presente e tudo estará configurado; caso contrário, estou certo de que você sabe como habilitar o suporte ao BINFMT no kernel.

Certifique-se de que o BINFMT_MISC está habilitado no kernel:

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

Agora, precisamos verificar as configurações:

$ 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

Você pode registrar manualmente usando, por exemplo, este guia.

Configurando o qemu static arm

Agora, precisaremos de uma instância do qemu compilada estaticamente.

!!! ATENÇÃO!!!
Se você pretende usar um contêiner para compilar algo, consulte:
https://sourceware.org/bugzilla/show_bug.cgi?id=23960
https://bugs.launchpad.net/qemu/+bug/1805913
Então, para host x86_64 e convidado arm, é necessário usar a versão i386 do 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
$ alien -t qemu-user-static_5.0-13_amd64.deb
# O caminho no rootfs e o nome do arquivo executável deve corresponder a /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), linked estaticamente, BuildID[sha1]=be45f9a321cccc5c139cc1991a4042907f9673b6, para GNU/Linux 3.2.0, compactado
$ 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), linked estaticamente, BuildID[sha1]=be45f9a321cccc5c139cc1991a4042907f9673b6, para GNU/Linux 3.2.0, compactado

Chroot

Um script simples:

ch-mount.sh

#!/bin/bash

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

function umnt() {
    echo "UNMOUNTING"
    sudo umount ${2}proc
    sudo umount ${2}sys
    sudo umount ${2}dev/pts
    sudo umount ${2}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 : ${1}
    echo 2nd parameter : ${2}
fi

Admiremos o resultado obtido:

$ ./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

Por curiosidade, vamos medir o tamanho antes e depois da instalação do conjunto mínimo (para mim) de pacotes:

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

Vamos atualizar:

# apt update
# apt upgrade --yes

Instalaremos os pacotes que nos interessam:

# 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

Os arquivos de cabeçalho do kernel, módulos, é uma conversa separada. O carregador de inicialização, kernel, módulos, o device tree não conseguimos instalar via Ubuntu. Eles virão de fora, ou nós os montaremos, ou o fabricante da placa nos fornecerá; de qualquer forma, isso está além das instruções desta.

Até certo ponto, a discrepância de versões é aceitável, mas é melhor obtê-las da construção do kernel.

# apt install --yes linux-headers-generic

Vamos ver o que obtivemos e não foi pouco:

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

Não se esqueça de definir uma senha.

Estamos compactando a imagem

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

Além disso, podemos instalar o etckeeper com a configuração de autopush

Digamos que distribuímos nossa construção, o trabalho começou, como seria melhor reunir depois várias versões do nosso sistema.

O etckeeper pode nos ajudar.

Segurança é uma questão pessoal:

  • você pode proteger ramificações específicas
  • gerar uma chave única para cada dispositivo
  • proibir force push
  • etc. …
# ssh-keygen
# apt install etckeeper
# etckeeper init
# cd /etc
# git remote add origin ...

Vamos configurar o autopush

Podemos, claro, criar ramificações no dispositivo com antecedência (por exemplo, criar um script ou serviço que funcione na primeira inicialização).

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

Ou podemos agir de maneira mais astuta…

Caminho preguiçoso

Vamos supor que temos um identificador único, digamos o número de série do processador (ou MAC — empresas sérias compram um intervalo):

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

Então podemos usá-lo como nome da ramificação para a qual vamos fazer o push:

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

Vamos criar um script simples:

# 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

E pronto — depois de algum tempo, podemos ver as mudanças e formar uma lista de pacotes para o firmware alvo.

Materiais recomendados

BINFMT_MISC
Suporte do Kernel para formatos binários diversos (binfmt_misc)
Compilando com qemu user chroot
Construindo o rootfs do Ubuntu para ARM
Como criar um Ubuntu live personalizado do zero
Crossdev qemu-static-user-chroot
etckeeper

problema getdents64

readdir() retorna NULL (errno=EOVERFLOW) para qemu user-static de 32 bits em host de 64 bits
Ext4 64 bits hash quebra glibc 2.28+ de 32 bits
detecção de compiler_id falha para armhf usando emulação de modo usuário QEMU
CMake não funciona corretamente sob qemu-arm

Fonte: habr.com

Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS 🔥 Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS | ProHoster