Skip to content

Instantly share code, notes, and snippets.

@mmguero
Forked from cGandom/RaspberryPi4-qemu.md
Last active December 9, 2024 05:34
Show Gist options
  • Save mmguero/5c13aa16a68783e6240458e0282f27da to your computer and use it in GitHub Desktop.
Save mmguero/5c13aa16a68783e6240458e0282f27da to your computer and use it in GitHub Desktop.
Emulating Raspberry Pi 4 with Qemu

Emulating Raspberry Pi 4 with Qemu

Just a quick update before we dive in: what we're actually doing here is running Raspberry Pi OS (64-bit) on a QEMU virtual ARM setup. This isn't full-blown hardware emulation of the Raspberry Pi 4, but more about creating a virtual environment for the OS. It doesn't mimic all the specific hardware features of the Pi 4, but it's pretty useful and great for general testing. I turned to this solution mainly to extract a modified sysroot from the Raspberry Pi OS, something not readily available in other resources. For those looking into detailed emulation of the actual Raspberry Pi 4's hardware in QEMU, check out this link for the latest updates: https://gitlab.com/qemu-project/qemu/-/issues/1208.

Hope it helps! :D

Shortcomings: No GUI yet, only console.

Steps

  1. Download Raspberry Pi OS (64-bit) from Raspberry Pi operating system images.
    Here we downloaded Raspberry Pi OS (64-bit) with desktop, Kernel version: 6.1, Debian version: 11 (bullseye), Release date: May 3rd 2023, named 2023-05-03-raspios-bullseye-arm64.img. We put it in /home/mydir.
  2. Install the required packages on your host system:
    $ # Cross compilers for arm64
    $ sudo apt install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu
    
    $ # Qemu itself
    $ sudo apt install qemu qemubuilder qemu-system-gui qemu-system-arm qemu-utils \
        qemu-system-data qemu-system
  3. Build the Linux kernel for qemu arm64 (You can download the kernel from Kernel.org):
    $ wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.1.34.tar.xz
    $ tar xvJf linux-6.1.34.tar.xz
    $ cd linux-6.1.34
    
    $ # create a .config file
    $ ARCH=arm64 CROSS_COMPILE=/bin/aarch64-linux-gnu- make defconfig
    $ # Use the kvm_guest config as the base defconfig, which is suitable for qemu
    $ ARCH=arm64 CROSS_COMPILE=/bin/aarch64-linux-gnu- make kvm_guest.config
    $ # Build the kernel
    $ ARCH=arm64 CROSS_COMPILE=/bin/aarch64-linux-gnu- make -j8
    
    $ cp arch/arm64/boot/Image /home/mydir
  4. Mount the image for enabling ssh and configuring username and password:
    1. Get the correct offset value with the help of fdisk utility:
      $ fdisk -l 2023-05-03-raspios-bullseye-arm64.img
      Disk 2023-05-03-raspios-bullseye-arm64.img: 4.11 GiB, 4412407808 bytes, 8617984 sectors
      Units: sectors of 1 * 512 = 512 bytes
      Sector size (logical/physical): 512 bytes / 512 bytes
      I/O size (minimum/optimal): 512 bytes / 512 bytes
      Disklabel type: dos
      Disk identifier: 0x3e247b30
      Device                                 Boot  Start     End Sectors  Size Id Type
      2023-05-03-raspios-bullseye-arm64.img1        8192  532479  524288  256M  c W95 FAT32 (LBA)
      2023-05-03-raspios-bullseye-arm64.img2      532480 8617983 8085504  3.9G 83 Linux
      As we can see, we have two partitions inside the downloaded image. The first device (partition) is the bootable partition, and the second one is the root filesystem. The first partition is what will be mounted as /boot in Raspberry Pi, and this is where we'll need to create some files.
      Obtain the correct offset of the first device by multiplying the start of the first partition (here 8192) by the sector size (here 512). Here it will be calculated as 8192 * 512 = 4194304
    2. Mount the image in /mnt/rpi directory:
      $ sudo mkdir /mnt/rpi
      $ sudo mount -o loop,offset=4194304 2023-05-03-raspios-bullseye-arm64.img /mnt/rpi
    3. Create a file named ssh to enable ssh:
      $ cd /mnt/rpi
      $ sudo touch ssh
    4. Additionally, create a file named userconf.txt in the same directory and put your desired username and password there, like <username>:<hashed-password> (might be better to leave the username as pi). This will be your default credentials:
      $ openssl passwd -6                                     # Generate the <hashed-password>
      $ echo 'pi:<hashed-password>' | sudo tee userconf.txt   # Put them inside `userconf.txt`
    5. Finally, unmount the image:
      $ sudo umount /mnt/rpi
  5. Make a copy of the .img file
$ cp 2023-12-11-raspios-bookworm-arm64-lite.img{,.expanded}
  1. Add some space to the end of it
$ truncate -s +40G 2023-12-11-raspios-bookworm-arm64-lite.img.expanded
  1. Resize the partition with virt-resize from the guestfs-tools package
$ virt-resize --expand /dev/sda2 2023-12-11-raspios-bookworm-arm64-lite.img 2023-12-11-raspios-bookworm-arm64-lite.img.bigger 
[   0.0] Examining 2023-12-11-raspios-bookworm-arm64-lite.img
 100% ⟦▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒⟧ 00:00
**********

Summary of changes:

virt-resize: /dev/sda1: This partition will be left alone.

virt-resize: /dev/sda2: This partition will be resized from 2.0G to 42.0G.  
The filesystem ext4 on /dev/sda2 will be expanded using the ‘resize2fs’ 
method.

**********
[  11.3] Setting up initial partition table on 2023-12-11-raspios-bookworm-arm64-lite.img.bigger
[  12.5] Copying /dev/sda1
[  13.4] Copying /dev/sda2
 100% ⟦▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒⟧ 00:00
[  20.7] Expanding /dev/sda2 using the ‘resize2fs’ method

virt-resize: Resize operation completed with no errors.  Before deleting 
the old disk, carefully check that the resized disk boots and works 
correctly.
  1. Replace the original with the expanded image file
$ mv 2023-12-11-raspios-bookworm-arm64-lite.img.expanded 2023-12-11-raspios-bookworm-arm64-lite.img
  1. Run qemu emulator:
    $ cd /home/mydir
    $ qemu-system-aarch64 -machine virt -cpu cortex-a72 -smp 6 -m 4G \
        -kernel Image -append "root=/dev/vda2 rootfstype=ext4 rw panic=0 console=ttyAMA0" \
        -drive format=raw,file=2023-05-03-raspios-bullseye-arm64.img,if=none,id=hd0,cache=writeback \
        -device virtio-blk,drive=hd0,bootindex=0 \
        -netdev user,id=mynet,hostfwd=tcp::2222-:22 \
        -device virtio-net-pci,netdev=mynet \
        -monitor telnet:127.0.0.1:5555,server,nowait
    This machine will be able to access the internet.
  2. After the machine is completely booted up, you can login to it from your computer by using ssh and the username and password you specified:
    $ ssh -l pi localhost -p 2222
  3. Done!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment