#!/bin/bash ## Creates and runs a virtual machine running Raspberry Pi OS Desktop or Lite with a GUI. ## Read it's documentation at https://gist.github.com/emendir/922d6914a1705ed2e8e4e96db726c422 ## Developed based on https://gist.github.com/cGandom/23764ad5517c8ec1d7cd904b923ad863 ## check the parameters if [ ! -f "$1" ] then echo "No valid raspberry pi os image supplied" echo "Usage: build-qemu-kernel.sh origial_image_file kernel_version domain_name" exit 1 fi if [ -z "$2" ] then echo "Version missing" echo "Usage: build-qemu-kernel.sh origial_image_file kernel_version domain_name" exit 1 fi if [ -z "$3" ] then echo "domainname missing" echo "Usage: build-qemu-kernel.sh origial_image_file kernel_version domain_name" exit 1 fi # Directory in which the VM's disk and linux kernel images will be stored #WORK_DIR=$(pwd)/$VM_NAME WORK_DIR=/opt/VirtualMachines/$VM_NAME # create some variables RPOS_LOCATION="$1" VERSION="$2" VM_NAME="$3" # name of the virtual machine registered in virt-manager ## Generate the password hash using the following command: # openssl passwd -6 PASSWORD_HASH='$6$2mB/106BUYDnL6oT$xPwTaarKh624nsI95IwR.U8xKBup1mbJHZxaSii9BcHuq2q2lSfc28LVlm1kPnd2fj/3YdIwoGhCDIR2Y0//Q0' # this value is for the password 'raspberry' USERNAME='pi' # Download URL of the Linux kernel used KERNEL_URL=https://cdn.kernel.org/pub/linux/kernel/v${VERSION//.*/.x}/linux-${VERSION}.tar.xz # Download URL of the Raspberry Pi OS image to use (the following options have been tested with the above kernel) # RPOS_IMAGE_URL=https://downloads.raspberrypi.com/raspios_lite_arm64/images/raspios_lite_arm64-2024-07-04/2024-07-04-raspios-bookworm-arm64-lite.img.xz RPOS_IMAGE_URL=https://downloads.raspberrypi.com/raspios_full_arm64/images/raspios_full_arm64-2024-07-04/2024-07-04-raspios-bookworm-arm64-full.img.xz # Flags to force rebuilding kernel or Raspberry Pi OS images # RECREATE_RPOS_IMAGE implies REBUILD_KERNEL RECREATE_RPOS_IMAGE=false # CAREFULL: will delete any existing image REBUILD_KERNEL=false # will reuse existing kernel build files if found, delete $WORK_DIR/linux-kernel to start from scratch # amount by which to resize the the VM's Raspberry Pi OS disk image # (requires manually running `raspi-config nonint do_expand_rootfs` to apply) RPOS_IMAGE_EXTRA_SPACE=6G # location of the VM's Raspberry Pi OS disk image RPOS_IMAGE=$WORK_DIR/${VM_NAME}_vm_disk.img # Setup mounting directories to edit the Raspberry Pi OS image RPI_BOOT_MNT=$WORK_DIR/rpi_boot RPI_ROOT_MNT=$WORK_DIR/rpi_root if ! [ -e $WORK_DIR ];then sudo mkdir -p $WORK_DIR fi sudo chmod -R a+rwx $WORK_DIR if ! [ -d $RPI_BOOT_MNT ];then mkdir $RPI_BOOT_MNT fi if ! [ -d $RPI_ROOT_MNT ]; then mkdir $RPI_ROOT_MNT fi cd $WORK_DIR || exit 1 ## Setup disk image for VM with Raspberry Pi OS pre-installed RECREATED_RPOS_IMAGE=false if $RECREATE_RPOS_IMAGE || ! [ -e $RPOS_IMAGE ];then RECREATED_RPOS_IMAGE=true # delete any old images if [ -e $RPOS_IMAGE ];then rm $RPOS_IMAGE fi ## Download and resize Raspberry Pi OS image #wget $RPOS_IMAGE_URL -O $RPOS_IMAGE.xz cp $RPOS_LOCATION ${RPOS_IMAGE}_unsized.xz xz -d ${RPOS_IMAGE}_unsized.xz cp ${RPOS_IMAGE}_unsized $RPOS_IMAGE truncate -s +$RPOS_IMAGE_EXTRA_SPACE $RPOS_IMAGE sudo virt-resize --expand /dev/sda2 ${RPOS_IMAGE}_unsized $RPOS_IMAGE #qemu-img resize -f raw $RPOS_IMAGE +$RPOS_IMAGE_EXTRA_SPACE fi # ensure we can work with the image now # we'll change its permissions again before running the VM chown $USER:kvm $RPOS_IMAGE # calculate image partition details for the boot and root partitions in the Raspberry Pi OS image ###### ATTENTION: The output text of the 'Sector size' is locate dependent and must be adjusted to your Country ! :-| # sector_size=$(fdisk -l $RPOS_IMAGE | grep 'Sector size' | awk '{print $4}') sector_size=$(fdisk -l $RPOS_IMAGE | grep -m1 'Sektorgröße'| awk '{print $3}') start_sector_boot=$(fdisk -l $RPOS_IMAGE | grep -m1 "^${RPOS_IMAGE}1" | awk '{print $2}') start_sector_root=$(fdisk -l $RPOS_IMAGE | grep -m1 "^${RPOS_IMAGE}2" | awk '{print $2}') start_sector_bytes_boot=$((sector_size * start_sector_boot)) start_sector_bytes_root=$((sector_size * start_sector_root)) echo "Image sector size: $sector_size" echo "Boot partition starts at sector $start_sector_boot (byte offset $start_sector_bytes_boot)" echo "Root partition starts at sector $start_sector_root (byte offset $start_sector_bytes_root)" if $RECREATED_RPOS_IMAGE;then ## Preconfigure username & password, enable SSH # mount the first partition of the Raspberry Pi OS image (the boot partition) sudo mount -o loop,offset=$start_sector_bytes_boot $RPOS_IMAGE $RPI_BOOT_MNT echo "${USERNAME}:${PASSWORD_HASH}"| sudo tee $RPI_BOOT_MNT/userconf sudo touch $RPI_BOOT_MNT/ssh # enable SSH sudo umount $RPI_BOOT_MNT fi ## Build Linux Kernel KERNEL_IMAGE=$WORK_DIR/linux-kernel/arch/arm64/boot/Image REBUILT_KERNEL=false if $REBUILD_KERNEL || $RECREATED_RPOS_IMAGE || ! [ -e $KERNEL_IMAGE ];then REBUILT_KERNEL=true # download linux kernel if necessary if ! [ -e $KERNEL_IMAGE ];then rm -r linux-* # remove old files wget $KERNEL_URL -O linux-kernel.tar.xz tar xvJf linux-kernel.tar.xz rm linux-kernel.tar.xz mv linux-* linux-kernel # rename linux-VERSION to known name fi cd linux-kernel || exit 1 # 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 ## manual kernel configuration DON'T DO THIS ON A MACHINE OF DIFFERENT ARCHITECTURE # make menu config sed -i 's/# CONFIG_DRM_QXL is not set/CONFIG_DRM_QXL=m/' .config sed -i 's/# CONFIG_FB_SIMPLE is not set/CONFIG_FB_SIMPLE=y/' .config sed -i 's/# CONFIG_FB_VIRTUAL is not set/CONFIG_FB_VIRTUAL=y/' .config sed -i 's/# CONFIG_NETLINK_DIAG is not set/CONFIG_NETLINK_DIAG=y/' .config # Build the kernel ARCH=arm64 CROSS_COMPILE=/bin/aarch64-linux-gnu- make -j8 ## Install kernel modules into the guest filesystem # mount the second partition of the Raspberry Pi OS image (the root partition) sudo mount -o loop,offset=$start_sector_bytes_root $RPOS_IMAGE $RPI_ROOT_MNT # Install kernel modules into the guest filesystem ARCH=arm64 CROSS_COMPILE=/bin/aarch64-linux-gnu- sudo make modules_install INSTALL_MOD_PATH=$RPI_ROOT_MNT sudo umount $RPI_ROOT_MNT cd .. || exit 1 # return to parent directory fi sudo chown libvirt-qemu:kvm $RPOS_IMAGE ## Run unregistered VM directly using qemu (CLI only) # qemu-system-aarch64 -machine virt -cpu cortex-a72 -smp 6 -m 4G \ # -kernel $KERNEL_IMAGE -append "root=/dev/vda2 rootfstype=ext4 rw panic=0 console=ttyAMA0" \ # -drive format=raw,file=$RPOS_IMAGE,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 ## Create registered VM for virt-manager (with graphics!) echo " $VM_NAME 4 6 hvm $KERNEL_IMAGE root=/dev/vda2 rootfstype=ext4 rw panic=0 console=ttyAMA0 cortex-a72