Skip to content

Instantly share code, notes, and snippets.

@eirikb
Last active March 29, 2024 18:12
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save eirikb/5bbf85d935c512827f859b614799de94 to your computer and use it in GitHub Desktop.
Save eirikb/5bbf85d935c512827f859b614799de94 to your computer and use it in GitHub Desktop.
Arch Linux in QEMU for Raspberry Pi 2
#!/usr/bin/env bash
echo
echo "Arch Linux in QEMU with love from eirikb"
echo
set -x
TARGET=ArchLinuxARM-rpi-armv7-latest.tar.gz
wget "http://os.archlinuxarm.org/os/$TARGET"
qemu-img create arch.img 32G
fdisk arch.img <<EOF
n
+200M
t
c
n
w
EOF
LOOPS=$(sudo kpartx -av arch.img | grep -Po 'loop.p.')
BOOT=$(echo "$LOOPS" | head -n1)
ROOT=$(echo "$LOOPS" | tail -n1)
sudo mkfs.vfat /dev/mapper/"$BOOT"
sudo mkfs.ext4 /dev/mapper/"$ROOT"
sudo rm -rf boot root b
mkdir boot root b
sudo mount /dev/mapper/"$BOOT" boot
sudo mount /dev/mapper/"$ROOT" root
sudo bsdtar -xpf "$TARGET" -C root
sudo sync
sudo mv root/boot/* boot
sudo cp -r boot/* b/
sudo umount boot root
sudo kpartx -d arch.img
sudo rm -rf root boot
echo 'qemu-system-arm \
-M raspi2b \
-kernel b/kernel7.img \
-drive "format=raw,file=arch.img" \
-append "console=ttyAMA0 root=/dev/mmcblk0p2 rw rootwait" \
-dtb b/bcm2709-rpi-2-b.dtb \
-serial stdio \
-display none' > boot.sh
# How I think it works, people please correct me if I'm wrong, I don't know much about this:
# -M raspi2:
# QEMU has a 'machine' raspi2, this will set up correct hardware on correct locations/buses (network, usb etc.)
# -kernel b/kernel7.img:
# Even though kernel is located on the disk QEMU must load it from outside the guest (from host).
# Raspberry has a two-stage bootloader, where the first bootloader is on-board (hardware).
# QEMU doesn't have this, and therefore can't simply kick start the second stage (called bootcode.bin).
# Instead kernel is loaded and run from outside, and no bootloader is used at all.
# -append "console=ttyAMA0 root=/dev/mmcblk0p2 rootwait":
# Append flags for kernel, these will be passed to the preivously loaded kernel.
# console=ttyAMA0 is used for virtual console, this makes QEMU able to print and read to/from stdio.
# root is where the kernel can find the root disk. I'm not sure about the path, it seems wrong to me.
# For the first I don't usee -sd (see below) so I expected /dev/sda, not /dev/mmcblk0p2.
# Secondly I expected this path to be hard coded as default in the kernel (that's the sd card path on rpi).
# rootwait does this: https://unix.stackexchange.com/a/636509 . Basically wait for rootfs to be available.
# Without number provided it probably do polling. Not 100% why this is required, beside the obivous that rootfs isn't availalbe at correct time.
#
# -dtb b/bcm2709-rpi-2-b.dtb:
# Set device tree binary. This is a file describing where devices are located for the kernel. Devices on rpi are non-discoverable, and the same
# kernel supports multiple (all) versions of raspberry. I'm not sure if QEMU uses this internally, beside passing it to kernel, in theory it should be enough with -M raspi2.
# An alternative is to have a kernel with this built into the kernel. I believe some OSes do this. archisoarm, at least for rpi, have these available for multiple rpi versions.
# Tip: You can see them by "ls b" after running this script.
#
# -drive "format=raw,file=arch.img":
# Tell QEMU about where the disk is. This disk will be created by this script. Used -drive instead of -sd in order to remove the warning about "raw" format.
#
# -serial stdio -display none:
# Make QEMU use terminal for input/output. Note ctrl+c closes. Change this as needed
set +x
echo
echo 'All is well. To run: sh boot.sh. Give it some seconds'
@ubdussamad
Copy link

The drive location parameter is very flaky in x86 as well. The drive you provided (drive=whatever) could become visible as sda or sdb or anything else after each run. I'm not sure why, but I think the reason in x86 was that QEMU was creating an extra drives as sda or sdb for some propose thus the drive provided by me became sdb, and thus kernel panicked because sda didn't contain any rootfs.

@eirikb
Copy link
Author

eirikb commented Jul 13, 2021

@ubdussamad I expected /dev/sda, was surprised it worked. I wonder if it could be something -m raspi2 does. It would be safer to use -sd, but then I got a warning about raw image, and I didn't find an option to set it. Perhaps if the image is converted to qcowit will detect the type automatically and not complain.

@correabuscar
Copy link

correabuscar commented Mar 29, 2024

Thanks so much for writing this, it got me started with first time using qemu and trying to run any aarch64 image, whereas before I was only getting black screen with high cpu usage and no progress (-d int would show Undefined Instruction or so).

I've tried arch linux arm but it just wouldn't sense the keyboard(and it's nearly impossible to get it to run getty or login prompt on AMA0, I think that I didn't manage to somehow run systemctl daemon-reload, which I can't from x86_64, or for some other reason it just would start me a login prompt there), then I tried Raspberry Pi OS (Legacy, 64-bit)->Raspberry Pi OS (Legacy) Lite image from https://www.raspberrypi.com/software/operating-systems/#raspberry-pi-os-64-bit which did sense the keyboard AND has login prompt on serial0 tab of qemu (aka ttyAMA0), but (this odd quirk) it would only sense the keyboard input if I used -device usb-kbd -netdev user,id=net0,hostfwd=tcp::2222-:22 -device usb-net,netdev=net0 and NOT when I used just -device usb-kbd, I've no idea why as the dmesg is exactly the same with the addition of the net usb part.

I wrote a bit about how to do it here, to remind future me: https://github.com/correabuscar/knowhow_and_TODO/blob/9769a51726228d78b3ad8ec3439dacfaa3112e09/knowhow.wofl#L1-L37 (or updated unless it moved)

but tl;dr is:
$ img="2024-03-12-raspios-bullseye-arm64-lite.img"
$ qemu-img resize "$img" 4G
$ qemu-system-aarch64 -M raspi3b -kernel b/kernel8.img -drive "format=raw,file=$img" -append "console=ttyAMA0 root=/dev/mmcblk0p2 rw rootwait" -dtb b/bcm2710-rpi-3-b.dtb -device usb-kbd -netdev user,id=net0,hostfwd=tcp::2222-:22 -device usb-net,netdev=net0

but of course the boot/* files were already extracted in b dir using your instructions from this gist.

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