|
| 1 | +#!/usr/bin/env bash |
| 2 | +# |
| 3 | +# SPDX-License-Identifier: GPL-2.0 |
| 4 | +# |
| 5 | +# Native armhf-on-aarch64 acceleration. |
| 6 | +# |
| 7 | +# Activate with ENABLE_EXTENSIONS=native-armhf-on-arm64. |
| 8 | +# |
| 9 | +# WHAT |
| 10 | +# On an aarch64 host building an armhf target, disable qemu-arm in |
| 11 | +# binfmt_misc for the duration of the build. The kernel's binfmt_elf |
| 12 | +# path then runs armhf ELF binaries natively via CONFIG_COMPAT — ~10× |
| 13 | +# faster than qemu-user-static emulation. mmdebstrap, chroot apt-get, |
| 14 | +# dpkg --configure, post-install scripts and customize_image all |
| 15 | +# benefit. |
| 16 | +# |
| 17 | +# REQUIREMENTS |
| 18 | +# * Host architecture aarch64. |
| 19 | +# * Host kernel with CONFIG_COMPAT=y (32-bit ARM EL0 support). |
| 20 | +# CONFIG_COMPAT_VDSO is not strictly required — full mmdebstrap + |
| 21 | +# post-customize runs were validated on stock Ubuntu Noble (6.8.x) |
| 22 | +# without COMPAT_VDSO. |
| 23 | +# * Build target ARCH=armhf (a no-op for any other ARCH). |
| 24 | +# * qemu-user-static (or qemu-user-binfmt) registered with qemu-arm |
| 25 | +# enabled in /proc/sys/fs/binfmt_misc/qemu-arm at build start. |
| 26 | +# The extension verifies this and refuses to run otherwise. |
| 27 | +# * CAP_SYS_ADMIN on host (the build already runs as root). |
| 28 | +# |
| 29 | +# RESTRICTIONS |
| 30 | +# * NO CONCURRENT ARMBIAN BUILDS on the same host while this extension |
| 31 | +# is active. The extension globally disables qemu-arm in binfmt_misc |
| 32 | +# for the build window. Any other parallel armhf-cross workload on |
| 33 | +# the host (CI runners, container builds depending on qemu-arm) will |
| 34 | +# have its armhf binaries routed through the kernel's binfmt_elf |
| 35 | +# fallback instead of qemu — which works on this host, but may |
| 36 | +# surprise code that depends on qemu being in the chain. |
| 37 | +# The operator owns the host while this extension runs. |
| 38 | +# * If the build is SIGKILL'd or the box crashes before the cleanup |
| 39 | +# handler fires, qemu-arm stays disabled. Re-enable manually: |
| 40 | +# sudo update-binfmts --enable qemu-arm |
| 41 | +# or |
| 42 | +# echo 1 | sudo tee /proc/sys/fs/binfmt_misc/qemu-arm |
| 43 | +# |
| 44 | +# DESIGN |
| 45 | +# No flock dance, no userspace coordination, no concurrent-safety |
| 46 | +# claims. The acceleration is mechanically simple: write 0 to the |
| 47 | +# binfmt_misc entry, write 1 back on cleanup. The in-core flock-based |
| 48 | +# variant lives at PR #9769 if you need acceleration that coexists |
| 49 | +# with concurrent armbian builds on the same host. |
| 50 | + |
| 51 | +function host_dependencies_ready__native_armhf_on_arm64() { |
| 52 | + # Self-gate: target arch must be armhf. Anything else: no-op, silent. |
| 53 | + # (We can't pre-compute via extension_prepare_config — that hook runs |
| 54 | + # only inside the docker container, after the relaunch.) |
| 55 | + [[ "${ARCH}" == "armhf" ]] || return 0 |
| 56 | + |
| 57 | + declare host_arch |
| 58 | + host_arch="$(arch)" |
| 59 | + if [[ "${host_arch}" != "aarch64" ]]; then |
| 60 | + exit_with_error "${EXTENSION}: enabled but host arch '${host_arch}' is not aarch64; this extension only accelerates armhf on aarch64 hosts." |
| 61 | + fi |
| 62 | + |
| 63 | + if [[ ! -e /proc/sys/fs/binfmt_misc/qemu-arm ]]; then |
| 64 | + exit_with_error "${EXTENSION}: qemu-arm is not registered in binfmt_misc on the host. Install qemu-user-static (or qemu-user-binfmt) and run 'update-binfmts --enable qemu-arm' before retrying." |
| 65 | + fi |
| 66 | + |
| 67 | + declare qemu_arm_state |
| 68 | + qemu_arm_state="$(head -n1 /proc/sys/fs/binfmt_misc/qemu-arm 2> /dev/null || true)" |
| 69 | + if [[ "${qemu_arm_state}" != "enabled" ]]; then |
| 70 | + display_alert "${EXTENSION}: qemu-arm already not enabled" "state='${qemu_arm_state}' — nothing to do, leaving alone" "info" |
| 71 | + return 0 |
| 72 | + fi |
| 73 | + |
| 74 | + # Probe-with-rollback: disable qemu-arm, verify the kernel can still |
| 75 | + # run armhf binaries natively, and re-enable + bail if it can't. This |
| 76 | + # catches misconfigured hosts (kernel without CONFIG_COMPAT) BEFORE |
| 77 | + # the docker container launches and starts an mmdebstrap that would |
| 78 | + # silently fail in chroot. |
| 79 | + display_alert "${EXTENSION}: disabling qemu-arm" "/proc/sys/fs/binfmt_misc/qemu-arm → 0" "info" |
| 80 | + if ! echo 0 > /proc/sys/fs/binfmt_misc/qemu-arm 2> /dev/null; then |
| 81 | + exit_with_error "${EXTENSION}: cannot write to /proc/sys/fs/binfmt_misc/qemu-arm. CAP_SYS_ADMIN missing on host?" |
| 82 | + fi |
| 83 | + |
| 84 | + if ! arch-test armhf > /dev/null 2>&1; then |
| 85 | + echo 1 > /proc/sys/fs/binfmt_misc/qemu-arm 2> /dev/null || true |
| 86 | + exit_with_error "${EXTENSION}: host kernel cannot run armhf natively after disabling qemu-arm (no CONFIG_COMPAT?). Restored qemu-arm and aborting; disable this extension on this host." |
| 87 | + fi |
| 88 | + |
| 89 | + declare -g _NATIVE_ARMHF_EXTENSION_DISABLED_QEMU_ARM=yes |
| 90 | + add_cleanup_handler trap_handler_native_armhf_on_arm64_restore |
| 91 | + display_alert "${EXTENSION}: armhf chroot execs will run natively via kernel binfmt_elf" "qemu-arm disabled for this build; will be restored on exit" "info" |
| 92 | +} |
| 93 | + |
| 94 | +function trap_handler_native_armhf_on_arm64_restore() { |
| 95 | + [[ "${_NATIVE_ARMHF_EXTENSION_DISABLED_QEMU_ARM:-no}" == "yes" ]] || return 0 |
| 96 | + [[ -e /proc/sys/fs/binfmt_misc/qemu-arm ]] || return 0 |
| 97 | + if echo 1 > /proc/sys/fs/binfmt_misc/qemu-arm 2> /dev/null; then |
| 98 | + display_alert "${EXTENSION}: qemu-arm restored" "/proc/sys/fs/binfmt_misc/qemu-arm → 1" "info" |
| 99 | + else |
| 100 | + display_alert "${EXTENSION}: failed to restore qemu-arm" "manual fix: echo 1 | sudo tee /proc/sys/fs/binfmt_misc/qemu-arm" "wrn" |
| 101 | + fi |
| 102 | + unset _NATIVE_ARMHF_EXTENSION_DISABLED_QEMU_ARM |
| 103 | +} |
0 commit comments