Skip to content

Instantly share code, notes, and snippets.

@mdeanda
Last active July 11, 2023 00:44
Show Gist options
  • Save mdeanda/b11ba916fb93c9cd438b94c86f03e004 to your computer and use it in GitHub Desktop.
Save mdeanda/b11ba916fb93c9cd438b94c86f03e004 to your computer and use it in GitHub Desktop.
Scripted generation of Arch Linux ARM image to run in QEMU in an Arch Linux host (x86_64)
#!/bin/bash
#
# Script to build and run Arch Linux ARM in QEMU inside of Arch Linux (x86_64)
#
#
# Original how-to and script from:
# * https://gist.github.com/thalamus/561d028ff5b66310fac1224f3d023c12
# * https://git.sr.ht/~whynothugo/m1-vm/tree/main/item/prepare.sh
set -euo pipefail
TARBALL=ArchLinuxARM-aarch64-latest.tar.gz
TARGET_IMAGE_BASE=archlinux
TIMEZONE=America/Los_Angeles
# Check if all the dependencies are present.
check() {
which qemu-img > /dev/null && \
which fdisk > /dev/null && \
which kpartx > /dev/null && \
which bsdtar > /dev/null && \
which curl > /dev/null ||
return 1
}
# Install dependencies.
install_deps() {
DEPS=(
curl
qemu-headless # qemu-img
multipath-tools # kpartx
util-linux # fdisk
libarchive # bsdtar
)
# shellcheck disable=SC2068
sudo pacman -S --needed --asdeps ${DEPS[@]}
}
check || install_deps
ls QEMU_EFI.fd 2> /dev/null || curl -LO https://releases.linaro.org/components/kernel/uefi-linaro/latest/release/qemu64/QEMU_EFI.fd
dd if=/dev/zero of=flash0.img bs=1M count=64
dd if=QEMU_EFI.fd of=flash0.img conv=notrunc
dd if=/dev/zero of=flash1.img bs=1M count=64
ls $TARBALL 2> /dev/null || curl -LO http://os.archlinuxarm.org/os/$TARBALL
qemu-img create $TARGET_IMAGE_BASE.img 32G
# Partition the disk:
sfdisk $TARGET_IMAGE_BASE.img <<EOF
label: gpt
label-id: 1745B3CF-E63C-4440-8A20-13CC871B5BE8
device: $TARGET_IMAGE_BASE.img
unit: sectors
first-lba: 2048
last-lba: 67108830
sector-size: 512
$TARGET_IMAGE_BASE.img1 : start= 2048, size= 614400, type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B
$TARGET_IMAGE_BASE.img2 : start= 616448, size= 66492383, type=B921B045-1DF0-41C3-AF44-4C6F280D3FAE
EOF
set -x
# Treat this image as a block device.
# Extract both loop devices from the output.
LOOP_DEVS=$(sudo kpartx -av $TARGET_IMAGE_BASE.img | grep -Po 'loop.p.')
BOOT_DEV=$(echo "$LOOP_DEVS" | head -n1)
ROOT_DEV=$(echo "$LOOP_DEVS" | tail -n1)
# Format the partitions
sudo mkfs.vfat /dev/mapper/"$BOOT_DEV"
sudo mkfs.ext4 /dev/mapper/"$ROOT_DEV"
# Mount the partitions
mkdir -p root
sudo mount /dev/mapper/"$ROOT_DEV" root
sudo mkdir -p root/boot
sudo mount /dev/mapper/"$BOOT_DEV" root/boot
sudo bsdtar -xpf $TARBALL -C root
BOOT_UUID=$(lsblk -no uuid /dev/mapper/$BOOT_DEV)
ROOT_UUID=$(lsblk -no uuid /dev/mapper/$ROOT_DEV)
echo "UUID=$ROOT_UUID / ext4 defaults 0 0" | sudo tee root/etc/fstab
echo "UUID=$BOOT_UUID /boot vfat defaults 0 0" | sudo tee -a root/etc/fstab
echo "Image root=UUID=$ROOT_UUID rw initrd=\initramfs-linux.img" | \
sudo tee root/boot/startup.nsh
cat > first_run.sh << EOF
#!/bin/bash
set -xe
pacman-key --init
pacman-key --populate archlinuxarm
pacman -Syu --noconfirm
pacman -S efibootmgr --noconfirm
BLKID=\$(lsblk -no uuid /dev/vda2)
efibootmgr --disk /dev/vda --part 1 --create --label "Arch Linux ARM" --loader /Image --unicode "root=UUID=\$BLKID rw initrd=\initramfs-linux.img" --verbose
ln -sf /usr/share/zoneinfo/$TIMEZONE /etc/localtime
echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen
locale-gen
localectl set-locale LANG=en_US.UTF-8
EOF
chmod +x first_run.sh
sudo mv first_run.sh root/root/
sudo umount root/boot
sudo umount root/
sudo kpartx -d $TARGET_IMAGE_BASE.img
sync
qemu-img convert -O qcow2 $TARGET_IMAGE_BASE.img $TARGET_IMAGE_BASE.qcow2
rm -rf root $TARGET_IMAGE_BASE.img
cat > run.sh << EOF
#!/bin/bash
qemu-system-aarch64 -m 4096 -smp 2 -cpu cortex-a72 -M virt -nographic \
-pflash flash0.img \
-pflash flash1.img \
-drive "if=virtio,media=disk,id=drive2,file=archlinux.qcow2,cache=writethrough,format=qcow2" \
-device virtio-net-device,netdev=user0 \
-netdev user,id=user0,hostfwd=tcp::2222-:22
EOF
chmod +x run.sh
cat << EOF
************************************************************
After starting the VM run ./first_boot.sh to finish setting
it up as described in the original how-to. The script may
time out if the mirror takes long so you may need to run it
a few times until it works.
Default user/pass is: root/root
************************************************************
EOF
@metzenseifner
Copy link

You could replace your regular expressions to find the UUID with lsblk -no uuid "$BOOT_DEV".

@mdeanda
Copy link
Author

mdeanda commented Aug 12, 2022

You could replace your regular expressions to find the UUID with lsblk -no uuid "$BOOT_DEV".

Thanks. This is nicer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment