Сборка дистрибутива Arch Linux ARMx64 для RaspberryPi 4

Существует множество причин по которым необходимо собрать свой дистрибутив Linux и столько же причин этого не делать, но зачастую перед вами будет стоять выбор - собирать или искать дистрибутив подходящий под ваши нужды и наплевать на оптимальность.
С выходом RaspberryPI 3 а теперь ещё и RaspberryPI 4 с ARM x64 процессорами, люди столкнулись с тем что отсутствуют x64 дистрибутивы под новую платформу, в свою очередь RPI зарекомендовал себя как вполне стабильная система для применения в ответсвеных конструкциях и учитывая цену он очень вкусно выглядит на фоне различных специализированных платформ.
На сколько оправдано использование x64 на RaspberryPi и остальных, вопрос открытый и каждый решает сам.
В моём случае, необходимо перетащить софт скомпилированный под aarch64, на базе очень специфической железки, за которую хотят очень много денег, а выходит из строя она до безобразия часто :)

Подготовка

Сразу создадим окружение для работы

mkdir -p buildroot/{sources,release,build,toolchain}
cd  buildroot
dirs -c 

Загружаем исходные коды и всё остальное что понадобится нам

Основное ядро для RaspberryPi на данный момент - 4.19.y а также 5.3.y.
При клонировании репозитория с ядром, укажите желаемую версию:
-b rpi-4.19.y - 4.19 (linux kernel 4.19.81)
-b rpi-5.3.y - 5.3 (linux kernel 5.3.8)

pushd sources
wget https://ftp.gnu.org/gnu/gcc/gcc-9.2.0/gcc-9.2.0.tar.gz
wget https://ftp.gnu.org/gnu/binutils/binutils-2.33.1.tar.xz
# rootfs arch linux aarch64
wget http://os.archlinuxarm.org/os/ArchLinuxARM-aarch64-latest.tar.gz
# official linux kernel for rpi
git clone https://github.com/raspberrypi/linux.git rpi-kernel --depth=1 -b rpi-4.19.y
# armstubs
git clone https://github.com/raspberrypi/tools.git rpi-tools --depth=1
# vendor firmware
git clone https://github.com/RPi-Distro/firmware-nonfree --depth=1
# /boot/overlays ... etc.
git clone https://github.com/raspberrypi/firmware --depth=1

# extract gcc and binutils
tar -xpJf binutils-2.33.1.tar.xz
tar -xpf gcc-9.2.0.tar.gz
popd

Общий объём “исходников” примерно 4GB, так что учитывайте это, если у вас ограничен трафик или низкая скорость соединения.

Подготовка toolchain

Для удобства зададим переменные

export ROOT=`pwd`
export SOURCES=${ROOT}/sources
export BUILD=${ROOT}/build
export RELEASE=${ROOT}/release
export TOOLCHAIN=${ROOT}/toolchain
export PATH=$PATH:$TOOLCHAIN/bin

Собираем binutils

mkdir ${BUILD}/binutils
pushd ${BUILD}/binutils
${SOURCES}/binutils-2.33.1/configure \
            --prefix="$TOOLCHAIN" \
            --target=aarch64-linux-gnu \
            --disable-nls
make -j4
make install
popd

Собираем gcc

mkdir ${BUILD}/gcc
pushd ${BUILD}/gcc
${SOURCES}/gcc-9.2.0/configure \
            --prefix="$TOOLCHAIN" \
            --target=aarch64-linux-gnu \
            --with-newlib \
            --without-headers \
            --disable-nls \
            --disable-shared \
            --disable-threads \
            --disable-libssp \
            --disable-decimal-float \
            --disable-libquadmath \
            --disable-libvtv \
            --disable-libgomp \
            --disable-libatomic \
            --enable-languages=c
make all-gcc -j4
make install-gcc
popd

Сборка ядра Linux

В систему необходимо доставить следующие пакеты bc bison flex
В зависимости от того для какого Raspberry Pi вы собираете ядро, необходимо указать соответственный конфигурационный файл:
bcm2711_defconfig – Raspberry Pi 4
bcmrpi3_defconfig – Raspberry Pi 3

pushd ${SOURCES}/rpi-kernel
make -j4 O=${BUILD}/rpi-kernel/ \
         ARCH=arm64 \
         CROSS_COMPILE=aarch64-linux-gnu-  \
         bcm2711_defconfig # <-- Указать необходимый вам!

make -j4 O=${BUILD}/rpi-kernel/ \
         ARCH=arm64 \
         CROSS_COMPILE=aarch64-linux-gnu-

export KERNEL_VERSION=`cat ${BUILD}/rpi-kernel/include/generated/utsrelease.h | sed -e 's/.*"\(.*\)".*/\1/'` 
make -j4 O=${BUILD}/rpi-kernel/ \
         DEPMOD=echo \
         MODLIB=${RELEASE}/rpi-kernel/lib/modules/${KERNEL_VERSION} \
         INSTALL_FW_PATH=${RELEASE}/rpi-kernel/lib/firmware \
         modules_install 

make -j4 O=${BUILD}/rpi-kernel/ \
         ARCH=arm64 \
         INSTALL_HDR_PATH=${RELEASE}/rpi-kernel/usr \
         headers_install

depmod --basedir ${RELEASE}/rpi-kernel "${KERNEL_VERSION}"
echo ${KERNEL_VERSION} > ${RELEASE}/rpi-kernel/version
popd

Ядро и всё прилагающееся будет сохранено в release/rpi-kernel, этот каталог можно заархивировать и спрятать на будущее, но немного позже :)

Подготовка /boot

Собираем в кучу всё необходимое для каталога /boot.
У нас остался не собранным bootstub, исправим

pushd ${SOURCES}/rpi-tools/armstubs
make armstub8-gic.bin
popd

И окончательно комплектуем - kernel, bootstub, overlays, bootloader, ThreadX, device tree, etc.

mkdir    ${RELEASE}/boot
cp       ${SOURCES}/rpi-tools/armstubs/armstub8-gic.bin ${RELEASE}/boot/
cp -arf  ${SOURCES}/firmware/boot/overlays ${RELEASE}/boot/
cp -arf  ${SOURCES}/firmware/boot/*.dat ${RELEASE}/boot/
cp -arf  ${SOURCES}/firmware/boot/*.elf ${RELEASE}/boot/
cp -arf  ${SOURCES}/firmware/boot/*.bin ${RELEASE}/boot/
cp -arf  ${BUILD}/rpi-kernel/arch/arm64/boot/dts/broadcom/*.dtb ${RELEASE}/boot/
cp -arf  ${BUILD}/rpi-kernel/arch/arm64/boot/Image ${RELEASE}/boot/kernel8.img

# cmdline.txt & config.txt
cat > ${RELEASE}/boot/config.txt <<- EOM
initramfs initramfs-linux.img followkernel
armstub=armstub8-gic.bin
enable_gic=1
arm_64bit=1
EOM

echo -n "root=/dev/mmcblk0p2 rw rootwait console=ttyAMA0,115200 console=tty1 selinux=0 plymouth.enable=0 smsc95xx.turbo_mode=N dwc_otg.lpm_enable=0 kgdboc=ttyAMA0,115200 elevator=noop logo.nologo vt.global_cursor_default=1 audit=0 quiet" > ${RELEASE}/boot/cmdline.txt

Также рекомендую сохранить скомпилированное ядро где-нибудь, я предпочитаю каталог с модулями ядра rootfs/lib/modules/KERNEL_VERSION/ - при сборке, скомпилированное ядро остаётся в каталоге /build/rpi-kernel/arch/arm64/boot/.

cp -arf ${BUILD}/rpi-kernel/arch/arm64/boot/Image \
        ${RELEASE}/rpi-kernel/lib/modules/${KERNEL_VERSION}/Image

Окончательно собираем rootfs Arch Linux ARM x64

На этом этапе, все операции производятся с правами root пользователя!

Распаковываем rootfs Arch Linux, и сразу подчищаем лишнее, подчищать не обязательно если в дальнейшем вы собираетесь донастраивать систему в chroot окружении (Об этом в предыдущей статье), в этом случае, в chroot необходимо будет удалить пакеты linux-aarch64, linux-firmware, linux-api-headers – pacman -Rdd linux-aarch64 linux-firmware linux-api-headers

# !!! USER ROOT !!!
sudo su root
Password: ******
export ROOT=`pwd`
export SOURCES=${ROOT}/sources
export RELEASE=${ROOT}/release
export KERNEL_VERSION=`cat ${RELEASE}/rpi-kernel/version`
export ROOTFS=${RELEASE}/archlinux-aarch64-${KERNEL_VERSION}-rootfs

pushd ${RELEASE}

mkdir ${ROOTFS}
tar -xpf ${SOURCES}/ArchLinuxARM-aarch64-latest.tar.gz -C ${ROOTFS}

rm -rf ${ROOTFS}/boot/*
rm -rf ${ROOTFS}/lib/modules/*
rm -rf ${ROOTFS}/lib/firmware/*
rm -rf ${ROOTFS}/usr/include/{asm,asm-generic,drm,linux,misc,mtd,rdma,scsi,sound,video,xen}
rm -rf ${ROOTFS}/var/lib/pacman/local/linux-aarch64*
rm -rf ${ROOTFS}/var/lib/pacman/local/linux-firmware*
rm -rf ${ROOTFS}/var/lib/pacman/local/linux-api-headers*

popd

Копируем новое ядро, библиотеки, firmware

# ! use root user
cp -arf ${RELEASE}/boot/* ${ROOTFS}/boot/
cp -arf ${RELEASE}/rpi-kernel/lib/* ${ROOTFS}/lib/
cp -arf ${RELEASE}/rpi-kernel/usr/include/* ${ROOTFS}/usr/include/
cp -arf ${SOURCES}/firmware-nonfree/* ${ROOTFS}/lib/firmware/
chown -R root:root ${ROOTFS}/boot/* ${ROOTFS}/lib//modules/* \
                   ${ROOTFS}/usr/include/* ${ROOTFS}/lib/firmware/*
# fix kernel version in mkinitcpio preset
sed -i 's|^ALL_kver.*|ALL_kver="'${KERNEL_VERSION}'"|g' ${ROOTFS}/etc/mkinitcpio.d/linux-aarch64.preset
# fstab 
cat > ${ROOTFS}/etc/fstab <<- EOM
# <file system> <dir> <type> <options> <dump> <pass>
proc /proc proc defaults 0 0
/dev/mmcblk0p1 /boot vfat defaults,noatime,nodiratime 0 2
/dev/mmcblk0p2 / ext4 defaults,noatime,nodiratime 0 1
EOM

С этого момента ваш Arch Linux полностью готов, rootfs можно скопировать на SD карту и загрузить Raspberry Pi, донастроить уже по месту или использовать chroot для настройки.
Доступ к chroot для arm x64 из под x86_64 описан в предыдущей статье, подготовка SD карты в офф. документации.
5й пункт документации заменить на

sudo su
cp release/archlinux-aarch64-rootfs/* /MOUNT_POINT_ROOT/

Упаковать дистрибутив в архив - рекомендую именно в архив а не городить образ, инсталяция занимает гараздо меньше времени чем dd образа.

# !!! USE ROOT !!!
pushd ${ROOTFS}
tar -cvpzf ${RELEASE}/archlinux-aarch64-${KERNEL_VERSION}.tar.gz --one-file-system ./
popd

--one-file-system - на случай если забыли отмонтировать dev,proc etc.