把 Arch Linux ARM 从 QEMU 命令行搬进 UTM
把 Arch Linux ARM 从 QEMU 命令行搬进 UTM
我的目标是在 Apple Silicon Mac 上保留一台小型 Linux VM:命令行安装 Arch Linux ARM、运行 Docker,再交给 UTM 管理。两份 KB 分别记了安装和导入过程,合起来才是一条完整的操作链。
历史记录确认 Arch Linux ARM 从磁盘启动,并在 UTM 中登录成功。但原文“使用 HVF”的说明与实际命令不一致;UTM 的安装来源、沙盒状态和嵌套虚拟化也被概括得过强。本文先纠正这些前提,再解释每步如何验证。
先确认架构、加速器和后端
ARM64 Linux 来宾与 Apple Silicon 同架构,可以使用硬件虚拟化,但不是任意 QEMU 命令都会自动使用 HVF。原记录使用 -cpu cortex-a72,未写 -accel hvf;这段命令只能作为那次可启动的记录,不能据此宣称当时启用了 HVF或测得了硬件加速性能。
uname -m
sysctl kern.hv_support
qemu-system-aarch64 -accel help
qemu-system-aarch64 --versionkern.hv_support=1 是宿主能力检查,-accel help 是软件支持列表,二者都不是当前 VM 正在使用 HVF 的证明。还要保留实际启动参数、QEMU 输出和 UTM 后端配置。
| 路线 | 应如何理解 |
|---|---|
| QEMU TCG | 软件模拟;可以模拟不同架构,性能须实测 |
| QEMU HVF | QEMU 使用 Hypervisor.framework;需要支持的宿主、来宾架构与显式配置 |
| UTM QEMU 后端 | 管理 QEMU 的设备与参数;可使用模拟或受支持的加速配置 |
| UTM Apple 后端 | 使用 Virtualization.framework;不是同一套 QEMU 配置,不能假设 qcow2 直接通用 |
原 KB 的“Apple Silicon 全系列不支持嵌套虚拟化”也应删除。Apple 给出了 Virtualization.framework 的运行时支持查询,相关能力涉及 M3 及后续芯片、系统版本和后端配置。本次 Linux 来宾能启动,不等于已在其中验证 /dev/kvm 或第二层 VM。Apple 嵌套虚拟化能力
安装输入必须能对应起来
准备一份 aarch64 Live ISO、Arch Linux ARM rootfs、一块新 qcow2 磁盘和 AArch64 UEFI 固件。原记录选择 Alpine standard ISO,遇到另一份介质的 X64 EFI 错误;应核查实际 ISO 的 EFI/BOOT/BOOTAA64.EFI,不要据此断言所有 Alpine virt 版本都不可用。
rootfs 的 latest、Homebrew 的 QEMU 和 Live ISO 会变化。下载后记录文件名、版本、哈希与来源;以下以已经下载好的文件为输入,不把历史版本号当成今天必须安装的版本。
brew install qemu
mkdir -p "$HOME/VMs/arch-arm"
qemu-img create -f qcow2 "$HOME/VMs/arch-arm/disk.qcow2" 30G
qemu-img info "$HOME/VMs/arch-arm/disk.qcow2"
shasum -a 256 "$HOME/VMs/arch-arm/live-aarch64.iso" "$HOME/VMs/arch-arm/rootfs.tar.gz"30 GiB 是虚拟容量,宿主实际占用会随写入增长。磁盘稀疏不意味着升级、复制或备份永远只需要初始那一点空间。rootfs 使用 Arch Linux ARM Generic AArch64 说明核对;Live 介质从 Alpine 官方下载选择。
启动 Live 环境,显式选择加速路线
下面是一份修订后的 TCG 安装示例,与历史 cortex-a72 路线保持一致。需要性能时另外验证 -accel hvf -cpu host 路线,不能只改文字说它已加速。固件路径通过 brew --prefix qemu 定位,并确认文件实际存在。
arch_vm_dir="$HOME/VMs/arch-arm"
arch_qemu_prefix="$(brew --prefix qemu)"
qemu-system-aarch64 \
-machine virt -accel tcg -cpu cortex-a72 -smp 2 -m 2048 \
-bios "$arch_qemu_prefix/share/qemu/edk2-aarch64-code.fd" \
-drive if=none,format=qcow2,id=hd0,file="$arch_vm_dir/disk.qcow2" \
-device virtio-blk-pci,drive=hd0 \
-cdrom "$arch_vm_dir/live-aarch64.iso" -boot d \
-netdev user,id=net0,hostfwd=tcp:127.0.0.1:2222-:22 \
-device virtio-net-pci,netdev=net0 -nographicQEMU virt 是通用虚拟平台,不是实体 Raspberry Pi。宿主转发端口显式绑定本机;来宾 SSH 服务尚未启动时,端口配置存在也不会完成登录。先用串口把安装做完,避免把 SSH 问题误当磁盘或内核失败。QEMU virt 平台
分区和 rootfs:先确认设备,再写入
以下在 Live 来宾中执行,会覆盖 /dev/vda,只能用于已核对为空的新 VM 磁盘。通过 lsblk 检查容量、现有分区和挂载点,别把宿主盘路径放进这些命令。
lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINTS
apk add util-linux e2fsprogs dosfstools libarchive-tools
sfdisk /dev/vda <<'EOF'
label: gpt
,512M,U
,+,L
EOF
mkfs.vfat -F 32 /dev/vda1
mkfs.ext4 /dev/vda2
mount /dev/vda2 /mnt
mkdir -p /mnt/boot
mount /dev/vda1 /mnt/bootESP 采用 512 MiB FAT32,剩余空间给 ext4 root。将已验证的 rootfs 放进 Live 环境可读的位置,先列出归档根目录结构,确认没有额外顶层目录,再以 root 解压,保留权限与符号链接:
tar -tzf /tmp/rootfs.tar.gz | head -20
bsdtar -xpf /tmp/rootfs.tar.gz -C /mnt
blkid /dev/vda1 /dev/vda2把实际 UUID 写到 /mnt/etc/fstab,不是照抄示例的字面占位符。Live 环境的 DHCP 与目标系统网络也要分别配置:安装时能下载文件,不证明重启进 Arch 后网络自动可用。检查 hostname、时区、locale,并在目标系统运行 locale 生成工具。
引导文件要与内核包保持一致
原记录把 loader 指向 /vmlinuz-linux,更新后包安装到 /boot/Image,引导仍用旧文件;这能解释“包已更新、运行内核没变”的一类故障。不能把某次 initramfs 缺驱动概括成 ARM64 永远不能直接内核启动。
本篇建议先检查实际内核文件、EFI 可启动格式及 initramfs 的 VirtIO/文件系统支持,再按所用发行版设置 loader。若继续沿用记录中的 vmlinuz-linux 副本,必须有明确的更新流程,并在每次升级后重启核对:
uname -r
ls -l /boot/Image /boot/vmlinuz-linux /boot/initramfs-linux.img
ls /usr/lib/modules
cat /boot/loader/entries/arch.conf若使用 systemd-boot,准备 chroot 的 /proc、/sys、/dev,确认 ESP 挂载点,再执行 bootctl install。EFI 变量未写成功时,还须检查架构匹配的 fallback 路径;不能只看到一行 copied 就结束。systemd bootctl
Docker 报 netfilter 错误时,对比 uname -r、模块目录和引导入口,确认正在运行的内核与模块匹配。历史更新后 Docker 可运行,但不代表任何同文报错都源自旧内核。留档服务日志再检查网络模块;不要一开始就修改宿主防火墙。
下面给出采用 EFI-stub Image 的修订引导示例,避免继续维护过期的内核副本。它假设本次分区、ESP 挂载和实际 initramfs 文件符合前文检查;缺文件或启动驱动时先修复,不能继续强行套用。官方 rootfs 有预设账户,应更改实际保留账号的密码,再开放网络服务。
# Run inside the Live guest after extracting the target rootfs.
arch_root_uuid="$(blkid -s UUID -o value /dev/vda2)"
arch_esp_uuid="$(blkid -s UUID -o value /dev/vda1)"
printf 'UUID=%s / ext4 defaults 0 1\nUUID=%s /boot vfat defaults 0 2\n' \
"$arch_root_uuid" "$arch_esp_uuid" > /mnt/etc/fstab
mount -t proc proc /mnt/proc
mount --rbind /sys /mnt/sys
mount --make-rslave /mnt/sys
mount --rbind /dev /mnt/dev
mount --make-rslave /mnt/dev
chroot /mnt /bin/bash# Run in the target chroot; inspect paths before writing the loader entry.
ls -l /boot/Image /boot/initramfs-linux.img
bootctl --esp-path=/boot install
mkdir -p /boot/loader/entries
printf 'default arch.conf\ntimeout 3\n' > /boot/loader/loader.conf
arch_root_uuid="$(blkid -s UUID -o value /dev/vda2)"
cat > /boot/loader/entries/arch.conf <<EOF
title Arch Linux ARM
linux /Image
initrd /initramfs-linux.img
options root=UUID=$arch_root_uuid rw console=ttyAMA0
EOF
pacman-key --init
pacman-key --populate archlinuxarm
passwd
exit离开 chroot 后,在 Live 环境执行 sync,卸载 /mnt 下的挂载再正常关机。再次启动时移除 -cdrom 和 -boot d,保持同一磁盘及设备配置。第一次成功进入 Arch 后复核挂载、内核和网络,再执行完整 pacman -Syu 并重启;Docker 安装使用 pacman -S docker 与 systemctl enable --now docker,以日志和实际容器启动验收。这里给的是修订示例,不能用历史截图替代它的重新执行结果。
转交 UTM:磁盘与虚拟硬件一起迁移
先在来宾正常关机并确认 QEMU 退出,备份磁盘,然后选择 UTM QEMU 后端、aarch64 架构、匹配的磁盘接口和 UEFI。单独导入 qcow2 不会携带命令行的全部设备、网络转发或固件变量。UTM 磁盘导入
原记录先报 No such file or directory,包内只有 EFI 变量文件,没有预期磁盘;创建指向外接卷的软链接后,错误变成 Operation not permitted;停止 VM 后把磁盘复制到配置所引用的包内位置,最终启动成功。
| 错误 | 先核对什么 | 能得出的结论 |
|---|---|---|
| No such file or directory | 配置引用路径、包内文件、外接卷是否挂载 | 预期文件未打开;不能单凭它认定是 UTM bug |
| Operation not permitted | 真实路径、用户授权、进程权限、应用 entitlement | 存在访问拒绝;需进一步区分具体限制 |
| 能启动但数据不一致 | 当前使用哪份磁盘、修改发生在哪里 | 可能已经运行复制副本而不是原盘 |
容器路径是线索,不是 App Store 安装来源的证明;Homebrew 安装也不自动说明无沙盒。符号链接不授予目标文件访问权限。优先通过 UTM 的受支持导入界面完成授权,不要靠盲改权限或硬链接规避问题。
迁移验收和日常维护
在 UTM 中确认登录、系统识别、挂载与网络,再检查 Docker 和重启后的持久状态。原记录 CLI 控制台用 ttyAMA0,UTM 画面用 tty1,终端改变不代表换了系统。
cat /etc/os-release
uname -r
findmnt /
findmnt /boot
ip address
systemctl status docker --no-pager此时选择一份磁盘作为日常唯一运行版本。复制副本会分叉,不能同时开两份再“同步一下文件”:先关闭所有写入者,备份目标,再执行有方向的替换或迁移。固件变量、UTM 配置、磁盘和安装来源一起备份。本文确认的是历史安装与导入可启动,修订后的命令和 HVF/嵌套路线仍需在各自版本上验收。
本文日期采用主 KB 首次 Git 提交日期 2026-07-15(UTC+8),提交 039bdbb。合并来源保留在元数据中;历史操作时间与入库时间分别注明。修订后的配置示例未在生产设备执行。
