Compiling QEMU from source – for Linux and Windows

QEMU (Quick Emulator) is a free, open-source program that acts as a machine emulator and virtualizer to run different operating systems and programs. QEMU emulates computer hardware (such as hard drives, network cards, and graphics controllers) and runs the actual virtual machine process in user space). You may prefer to use a management layer such as libvirtd (Linux) instead of QEMU/KVM directly. In this post, I will be compiling QEMU from source to get more control over its features that are enabled. While I am unsure what is exactly different between the version I compiled from source and the pre-built windows version from the QEMU website, the command I used for spinning up the VM doesn’t work with the pre-built QEMU binary. QEMu starts with the VM status as “paused”. My main goal is to create a VM where the O/S running in it believes it is running on a physical computer and not in a VM. This is to by-pass some limitations in certain games and other applications where they refuse to run (or don’t run correctly) when they detect they are being launched in a VM. This is sometimes called “Anti-VM” detection.

Physical Environment
Physical laptop is a DELL running Rocky 9.I decided to create a Ubuntu VM to use as my compiler machine for several reasons.

———————————————————– ————————————————————

COMPILING QEMU for Windows on Ubuntu VM

QEMU (Quick Emulator) is a free, open-source program that acts as a machine emulator and virtualizer to run different operating systems and programs. QEMU emulates computer hardware (such as hard drives, network cards, and graphics controllers) and runs the actual virtual machine process in user space). You may prefer to use a management layer such as libvirtd (Linux) instead of QEMU/KVM directly. In this post, I will be compiling QEMU from source to get more control over its features that are enabled. While I am unsure what is exactly different between the version I compiled from source and the pre-built windows version from the QEMU website, the command I used for spinning up the VM doesn’t work with the pre-built QEMU binary. QEMu starts with the VM status as “paused”. My main goal is to create a VM where the O/S running in it believes it is running on a physical computer and not in a VM. This is to by-pass some limitations in certain games and other applications where they refuse to run (or don’t run correctly) when they detect they are being launched in a VM. This is sometimes called “Anti-VM” detection.

Physical Environment: Physical laptop is a DELL running Rocky 9.I decided to create a Ubuntu VM to use as my compiler machine for several reasons.

  • Setup libvirtd on the physical host (CentOS Stream release 9) so we can create a Ubuntu VM
  1. Check Hardware Virtualization
    $ grep -e ‘vmx’ -e ‘svm’ /proc/cpuinfo
  2. Install Virtualization Packages
    $ sudo dnf install -y qemu-kvm libvirt virt-manager virt-install virt-viewer virt-top bridge-utils libguestfs-tools
  3. Start and Enable libvirtd
    $ sudo systemctl enable –now libvirtd
    $ sudo systemctl status libvirtd
  4. Configure User Permissions
    $ sudo usermod -aG libvirt $USER
    $ sudo usermod -aG kvm $USER
    $ newgrp libvirt
  5. Start the Default Network
    $ sudo virsh net-start default
    $ sudo virsh net-autostart default
  6. Enable ordinary user to create a VM and have it have networking without using sudo
    $ ip a #(find your bridge interface e.g., virbr0)
    $ sudo mkdir -p /etc/qemu
    $ echo “allow virbr0” | sudo tee /etc/qemu/bridge.conf
    $ sudo chmod 644 /etc/qemu/bridge.conf
    $ sudo chmod u+s /usr/libexec/qemu-bridge-helper

———————————————————– ————————————————————

Cross-compile QEMU for Windows (on Ubuntu)

– Download a disk image and create a VM from it:
$ mkdir /VMs
$ cd /VMs
$ wget https://cloud-images.ubuntu.com/minimal/releases/noble/release/ubuntu-24.04-minimal-cloudimg-amd64.img -O compiler.qcow2

– increase the size of the disk as it is only 3.5G

$ qemu-img resize compiler.qcow2 30g

$ cat >> user-data << EOF
# cloud-config
user: ubuntu
password: xxxxx123!
chpasswd: { expire: False }
ssh_pwauth: True
# Automatically updates the repositories and installs ping on first boot
packages:
– iputils-ping
EOF
$
$ sudo usermod -aG libvirt $USER
$ newgrp libvirt

– create the VM

$ sudo virt-install –name compiler –memory 8192 –vcpus 4 –disk compiler.qcow2,format=qcow2 –os-variant ubuntu24.04 –import –graphics none –network default –cloud-init user-data=user-data

– login to the Ubuntu VM with the credentials from the user-data file

ubuntu@ubuntu:~/qemu$ cat /etc/os-release | grep VERSION=
VERSION=”24.04.5 LTS (Noble Numbat)”

$ echo “export TERM=vt220” >> ~/.bashrc
$ echo “set enable-bracketed-paste off” >> ~/.bashrc
$ echo “export PS1='[\u@\h \W]\$'” >> ~/.bashrc
$ echo “PS2=’>'” >> ~/.bashrc
$ source ~/.bashrc

$ sudo apt update -y
$ sudo apt upgrade -y
$ sudo apt install -y mingw-w64
$ sudo apt install -y software-properties-common
$ sudo add-apt-repository -c universe -y
$ sudo apt update -y
$ sudo apt upgrade -y

$ DEBIAN_FRONTEND=noninteractive sudo -E apt install -y autoconf automake autopoint bash bison bzip2 flex gettext git g++ gperf intltool libc6-dev-i386 libgdk-pixbuf2.0-dev libltdl-dev libgl-dev libpcre3-dev libssl-dev libtool-bin libxml-parser-perl lzip make openssl p7zip-full patch perl pkg-config python3 python3-mako python3-pkg-resources ruby sed unzip wget xz-utils g++-multilib python-is-python3 python3-venv vim meson libusb-1.0-0-dev libibverbs-dev librdmacm-dev libcacard-dev libusbredirparser-dev cmake libffi-dev scons

– IF prompted for keyboard layout, enter 35 (English US) or 34 (English UK) for country of origin, then 1 (English US) for keyboard layout

$ cd
$ git clone https://github.com/mxe/mxe.git
$ cd mxe/
$ make -j$(nproc) JOBS=$(nproc) MXE_TARGETS=x86_64-w64-mingw32.static glib gtk3 pixman sdl2 openssl zlib jpeg opus libusb1 lz4
$ make MXE_TARGETS=’x86_64-w64-mingw32.static’ lz4
$ echo ‘export PATH=”/home/ubuntu/mxe/usr/bin:$PATH”‘ >> ~/.bashrc
$ source ~/.bashrc

$ cd
$ git clone https://gitlab.freedesktop.org/slirp/libslirp.git
$ cd libslirp
$ cat > mxe-cross.txt << ‘EOF’
[binaries]
c = ‘x86_64-w64-mingw32.static-gcc’
ar = ‘x86_64-w64-mingw32.static-ar’
strip = ‘x86_64-w64-mingw32.static-strip’
pkg-config = ‘x86_64-w64-mingw32.static-pkg-config’
windres = ‘x86_64-w64-mingw32.static-windres’

[host_machine]
system = ‘windows’
cpu_family = ‘x86_64’
cpu = ‘x86_64’
endian = ‘little’
EOF

$ meson setup build –cross-file mxe-cross.txt –prefix=/home/ubuntu/mxe/usr/x86_64-w64-mingw32.static –default-library=static
$ ninja -C build install
$ x86_64-w64-mingw32.static-pkg-config –modversion slirp

$ cd ~
$ git clone https://gitlab.freedesktop.org/spice/spice-protocol.git
$ cd spice-protocol
$ meson setup build –prefix=/tmp/spice-protocol-install
$ ninja -C build install

$ cp -r /tmp/spice-protocol-install/include/spice-1 ~/mxe/usr/x86_64-w64-mingw32.static/include/
$ cp /tmp/spice-protocol-install/share/pkgconfig/spice-protocol.pc ~/mxe/usr/x86_64-w64-mingw32.static/lib/pkgconfig/
$ sed -i “s|/tmp/spice-protocol-install|/home/ubuntu/mxe/usr/x86_64-w64-mingw32.static|g” ~/mxe/usr/x86_64-w64-mingw32.static/lib/pkgconfig/spice-protocol.pc
$ x86_64-w64-mingw32.static-pkg-config –modversion spice-protocol

$ cd ~
$ git clone https://gitlab.freedesktop.org/gstreamer/orc.git
$ cd orc
$ meson setup build –cross-file ~/libslirp/mxe-cross.txt –prefix=/home/ubuntu/mxe/usr/x86_64-w64-mingw32.static –default-library=static
$ ninja -C build install
$ x86_64-w64-mingw32.static-pkg-config –modversion orc-0.4

– Let’s verify the full set before moving to spice-server itself, since a missed one there would be a much more expensive failure to debug:

$ for pkg in openssl zlib libjpeg opus orc-0.4 glib-2.0 pixman-1 liblz4; do echo -n “$pkg: “; x86_64-w64-mingw32.static-pkg-config –modversion $pkg 2>&1; done

$ cd ~
$ cat >> ~/libslirp/mxe-cross.txt << ‘EOF’
$ EOF

$ sed -i “/^c = /a cpp = ‘x86_64-w64-mingw32.static-g++'” ~/libslirp/mxe-cross.txt
$ cat ~/libslirp/mxe-cross.txt

– the build might fail but keep going

$ wget https://www.spice-space.org/download/releases/spice-server/spice-0.15.2.tar.bz2
$ tar xjf spice-0.15.2.tar.bz2
$ cd ~/spice-0.15.2
$ rm -rf build
$ meson setup build –cross-file ~/libslirp/mxe-cross.txt –prefix=/home/ubuntu/mxe/usr/x86_64-w64-mingw32.static –default-library=static -Dgstreamer=no -Dlz4=true -Dsasl=false -Dsmartcard=disabled -Dmanual=false -Dstatistics=false -Dopus=enabled -Dspice-common:tests=false -Dtests=false -Dc_link_args=-Wl,–allow-multiple-definition

$ ninja -C build install
$ x86_64-w64-mingw32.static-pkg-config –modversion spice-server

$ cd
$ git clone https://git.qemu.org/git/qemu.git –depth 1
$ cd qemu
$ git submodule init
$ git submodule update –recursive
$ rm -rf build-win64
$ mkdir build-win64 && cd ~/qemu/build-win64
$ ../configure –cross-prefix=x86_64-w64-mingw32.static- –target-list=x86_64-softmmu –extra-cflags=”-D__USE_MINGW_ANSI_STDIO=1 -Wno-error=format -Wno-error=suggest-attribute=format -Wno-error=format-extra-args -DLIBSLIRP_STATIC” –extra-ldflags=”-lstdc++” –enable-slirp –enable-spice –enable-gtk –enable-vnc –enable-whpx –audio-drv-list=dsound,sdl –enable-libusb –enable-sdl –prefix=~/qemu-win
$ make -j$(nproc)
$ make install

$ cd ~/qemu-win
$ find ./ -iname ‘.exe’ -exec cp {} ~/qemu-win/ \; mkdir ~/qemu-win/share cp ../pc-bios/.bin ../pc-bios/.rom ./pc-bios/x86_64*.fd ~/qemu-win/share/
$ cd ~

– if you are not intending to support VMs of the following archi when running QEMU on Windows, remove the support files

$ cd ~/qemu-win/
$ find ./ -iname ‘aarch64‘ -exec rm -f {} \;
$ find ./ -iname ‘riscv‘ -exec rm -f {} \;
$ find ./ -iname ‘loongarch64‘ -exec rm -f {} \;

– create a zip file of the QEMU files and you can copy it to a Windows host.

$ cd
$ tar czf ~/qemu-win.tgz ./qemu-win

– You can transfer the zip file to your windows host, extract it into a folder such as C:\qemu-win\ and run qemu-system-x86_64.exe from there to create a VM

———————————————————– ————————————————————


Sample command to create a Windows 10 VM on QEMU running on a Windows physical host – this command includes sound in the VM but not Camera.

cd C:\qemu\
qemu-img create -f mypc.qcow2 35g
C:\qemu\qemu-system-x86_64.exe -accel whpx -cpu host,hv_relaxed,hv_vapic,hv_time -machine q35 -L C:\qemu\share -smbios type=0,vendor=”American Megatrends International,, LLC.”,version=”F10″ -smbios type=1,manufacturer=”ASUSTeK COMPUTER INC.”,product=”PRIME Z790-A WIFI”,version=”Rev 1.xx”,serial=”1234567890″ -smbios type=2,manufacturer=”ASUSTeK COMPUTER INC.”,product=”PRIME Z790-A WIFI”,version=”Rev 1.xx”,serial=”1234567890″ -smbios type=3,manufacturer=”ASUSTeK COMPUTER INC.”,version=”Rev 1.xx”,serial=”1234567890″ -netdev user,id=net0 -device e1000e,netdev=net0,mac=00:1A:2B:3C:4D:5E -drive file=C:\qemu\mypc.qcow2,if=none,id=drive-sata0,cache=writeback -device ide-hd,drive=drive-sata0,bus=ide.0,serial=”WDC-WD10EZEX-00BN7A0″,model=”Western Digital” -drive file=C:\qemu\Windows1022H2.iso,if=none,id=drive-cd0,media=cdrom -device ide-cd,drive=drive-cd0,bus=ide.1 -boot order=c,once=d,menu=on -m 16G -smp 4 -vga qxl -audiodev dsound,id=snd0 -device ich9-intel-hda -device hda-duplex,audiodev=snd0

———————————————————– ————————————————————

Compile QEMU for Linux (Red Hat variants) on Rocky 9.8 VM

– Download a disk image and create a VM from it:
$ mkdir /VMs
$ cd /VMs
$ wget https://dl.rockylinux.org/pub/rocky/9/images/x86_64/Rocky-9-GenericCloud-Base.latest.x86_64.qcow2 -O compiler.qcow2
$ qemu-img resize compiler.qcow2 30G
$ cat user-data
# cloud-config
user: vmuser
password: xxxxx123!
chpasswd: { expire: False }
ssh_pwauth: True
# Automatically updates the repositories and installs ping on first boot
packages:
– python3.14
$
$ id
uid=1000(aitayemi) gid=1000(aitayemi) groups=1000(aitayemi),10(wheel),36(kvm),980(docker)

$ sudo virt-install –name compiler –memory 8192 –vcpus 4 –disk compiler.qcow2,format=qcow2 –os-variant rhel9.8 –import –graphics none –network default –cloud-init user-data=user-data

– login to the VM with the credentials from the user-data file

$ cat /etc/os-release | grep VERSION=
VERSION=”9.8 (Blue Onyx)”
$ df -h /
Filesystem Size Used Avail Use% Mounted on
/dev/vda4 29G 1.3G 28G 5% /
$ sudo dnf update -yq

– install the full common set of QEMU build dependencies. On Rocky 9, this covers most of what QEMU needs. Also, python3.12 or higher is required:
$ sudo dnf install -y epel-release
$ sudo dnf config-manager –set-enabled crb
$ sudo dnf install -y ‘dnf-command(copr)’
$ sudo dnf copr enable -y ligenix/enterprise-qemu-spice

$ sudo dnf groupinstall -y “Development Tools”
$ sudo dnf install -y glib2-devel pixman-devel zlib-devel ninja-build meson python3-devel python3-pip SDL2-devel libusb1-devel libusbx-devel usbredir-devel rdma-core-devel libibverbs-devel libslirp-devel gnutls-devel nettle-devel libcap-ng-devel libattr-devel pciutils-devel bzip2-devel snappy-devel libcurl-devel numactl-devel libaio-devel python3.14 python3.14-pip spice-server-devel gtk3-devel alsa-lib-devel pulseaudio-libs-devel spice-server-devel wget python3.14-pip python3.14-setuptools python3.14-devel

– Switch the system from the default 3.9 to 3.14 as some of the utilities require version 3.12 or higher
$ sudo alternatives –install /usr/bin/python3 python3 /usr/bin/python3.14 1
$ sudo alternatives –set python3 /usr/bin/python3.14
$ python -V

$ git clone https://git.qemu.org/git/qemu.git –depth 1
$ cd qemu
$ git submodule init
$ git submodule update –recursive
$ rm -rf build
$ mkdir build && cd build
$ ../configure –target-list=x86_64-softmmu –extra-cflags=”-D__USE_MINGW_ANSI_STDIO=1 -Wno-error=format -Wno-error=suggest-attribute=format -Wno-error=format-extra-args -DLIBSLIRP_STATIC” –extra-ldflags=”-lstdc++” –enable-slirp –enable-spice –enable-gtk –enable-vnc –enable-alsa –enable-pa –prefix=/opt/qemu
$ make -j$(nproc)
$ sudo make install
$ cd /
$ tar czf ~/qemu-linux.tgz ./opt/qemu

– NOTE: if you want to compile QEMU for all supported platforms (POWER/PPC, SPARC, etc), remove (–target-list=x86_64-softmmu) and add –disable-werror to the configure command.
– You can copy the qemu-linux.zip to another physical Linux system/host for use to create VMs.

———————————————————– ————————————————————


Running Windows 10 VM on CentOS Stream 9 host (with audio and camera access in the VM)

– Get the Vendor and device ID of the Camera on the physical host
$ lsusb | grep -i cam
Bus 003 Device 002: ID 1bcf:2ba9 Sunplus Innovation Technology Inc. Integrated_Webcam_FHD
$
$ lsusb -d 1bcf:2ba9
Bus 003 Device 002: ID 1bcf:2ba9 Sunplus Innovation Technology Inc. Integrated_Webcam_FHD
$

– create the UDEV rule to enable access to the camera using its vendor and device ID
$ sudo tee /etc/udev/rules.d/99-qemu-usb.rules <<‘EOF’
SUBSYSTEM==”usb”, ATTR{idVendor}==”1bcf”, ATTR{idProduct}==”2ba9″, MODE=”0666″, GROUP=”kvm”
EOF

$ sudo udevadm control –reload-rules
$ sudo udevadm trigger
$ ls -l /dev/video*
crw-rw—-+ 1 root kvm 81, 0 Sep 25 21:54 /dev/video0
crw-rw—-+ 1 root kvm 81, 1 Sep 25 21:54 /dev/video1
crw-rw—-+ 1 root kvm 81, 2 Sep 25 21:54 /dev/video2
crw-rw—-+ 1 root kvm 81, 3 Sep 25 21:54 /dev/video3
$
$ ls -l /dev/bus/usb/003/002
crw-rw—- 1 root kvm 189, 257 Sep 25 21:18 /dev/bus/usb/003/002

$ cd /opt/qemu/bin
$ ./qemu-system-x86_64 -accel kvm \
-cpu host,hv_relaxed,hv_vapic,hv_time \
-machine q35 \
-L /opt/qemu/share/qemu \
-smbios type=0,vendor=”American Megatrends International,, LLC.”,version=”F10″ \
-smbios type=1,manufacturer=”ASUSTeK COMPUTER INC.”,product=”PRIME Z790-A WIFI”,version=”Rev 1.xx”,serial=”1234567890″ \
-smbios type=2,manufacturer=”ASUSTeK COMPUTER INC.”,product=”PRIME Z790-A WIFI”,version=”Rev 1.xx”,serial=”1234567890″ \
-smbios type=3,manufacturer=”ASUSTeK COMPUTER INC.”,version=”Rev 1.xx”,serial=”1234567890″ \
-netdev user,id=net0 \
-device e1000e,netdev=net0,mac=00:1A:2B:3C:4D:5E \
-drive file=~/mypc.qcow2,if=none,id=drive-sata0,cache=writeback,format=raw \
-device ide-hd,drive=drive-sata0,bus=ide.0,serial=”WDC-WD10EZEX-00BN7A0″,model=”Western Digital” \
-drive file=/VMs/Windows1022H2.iso,if=none,id=drive-cd0,media=cdrom -device ide-cd,drive=drive-cd0,bus=ide.1 \
-boot order=c,once=d,menu=on \
-m 16G -smp 4 -vga qxl \
-audiodev sdl,id=snd0 -device ich9-intel-hda -device hda-micro,audiodev=snd0 -device nec-usb-xhci,id=usbctrl \
-device usb-host,bus=usbctrl.0,vendorid=0x1BCF,productid=0x2BA9,suppress-remote-wake=on,pipeline=off

NOTE:
During the Windows 10 installation in the VM, Activation may fail with “Something went wrong: But you can try again”. OOBELOCAL – likely due to Windows not recognizing the NIC
Solution:
– On the error screen, Use the New Local Account Command: Press Shift + F10 (or Fn + Shift + F10) on the Microsoft account screen to open the Command Prompt. 
– Click on the black Command Prompt window to make sure it is active. 
– Type start ms-cxh:localonly and press Enter. 
– This opens the local account setup prompt so you can create a offline username. 

– The above launches the QEMU GUI on my X-Server since I am connected to the Linux server remotely using SSH from Mobaxterm on a Windows client

  • Running QEMU on Windows – (remove “once=d,” and the “-drrve” parameter for CDROM if not using an ISO image or you already set up the O/S on the VM)
  • alternative virtual machine definition:
    -smbios type=1,manufacturer=”Dell Inc.”,product=”OptiPlex 7090″,version=”1.0″,serial=”ABC123XYZ” -smbios type=2,manufacturer=”Dell Inc.”,product=”0XYZ12″,version=”A01″ -smbios type=3,manufacturer=”Dell Inc.”
  • Even the respondus lockdown browser was unable to detect it was in a VM

———————————————————– ————————————————————

Compile QEMU for Ubuntu

– Download a disk image and create a VM from it:
$ mkdir /VMs
$ cd /VMs
$ wget https://cloud-images.ubuntu.com/minimal/releases/noble/release/ubuntu-24.04-minimal-cloudimg-amd64.img -O compiler.qcow2

– increase the size of the disk as it is only 3.5G

$ qemu-img resize compiler.qcow2 30g
$ cat >> user-data << EOF
cloud-config
user: ubuntu
password: xxxxx123!
chpasswd: { expire: False }
ssh_pwauth: True
# Automatically updates the repositories and installs ping on first boot
packages:
– iputils-ping
EOF
$
$ sudo usermod -aG libvirt $USER
$ newgrp libvirt

– create the VM

$ sudo virt-install –name compiler –memory 8192 –vcpus 4 –disk compiler.qcow2,format=qcow2 –os-variant ubuntu24.04 –import –graphics none –network default –cloud-init user-data=user-data

– login to the Ubuntu VM with the credentials from the user-data file

ubuntu@ubuntu:~/qemu$ cat /etc/os-release | grep VERSION=
VERSION=”24.04.5 LTS (Noble Numbat)”

$ echo “export TERM=vt220” >> ~/.bashrc
$ echo “set enable-bracketed-paste off” >> ~/.bashrc
$ echo “export PS1='[\u@\h \W]\$'” >> ~/.bashrc
$ echo “PS2=’>'” >> ~/.bashrc
$ source ~/.bashrc

$ sudo apt update -y
$ sudo apt upgrade -y
$ sudo apt install -y mingw-w64
$ sudo apt install -y software-properties-common
$ sudo add-apt-repository -c universe -y
$ sudo apt update -y
$ sudo apt upgrade -y

$ DEBIAN_FRONTEND=noninteractive sudo -E apt install -y autoconf automake autopoint bash bison bzip2 flex gettext git g++ gperf intltool libc6-dev-i386 libgdk-pixbuf2.0-dev libltdl-dev libgl-dev libpcre3-dev libssl-dev libtool-bin libxml-parser-perl lzip make openssl p7zip-full patch perl pkg-config python3 python3-mako python3-pkg-resources ruby sed unzip wget xz-utils g++-multilib python-is-python3 python3-venv vim meson libusb-1.0-0-dev libibverbs-dev librdmacm-dev libcacard-dev libusbredirparser-dev libusb-1.0-0-dev

– IF prompted for keyboard layout, enter 35 (English US) or 34 (English UK) for country of origin, then 1 (English US) for keyboard layout

$ cd
$ sudo apt install -y build-essential ninja-build meson pkg-config libglib2.0-dev libpixman-1-dev libgtk-3-dev libvncserver-dev libslirp-dev libspice-server-dev libspice-protocol-dev libsdl2-dev libssl-dev zlib1g-dev
$ rm -rf qemu
$ git clone https://git.qemu.org/git/qemu.git –depth 1
$ cd qemu
$ git submodule init
$ git submodule update –recursive
$ rm -rf build
$ mkdir build && cd build
$ ../configure –target-list=x86_64-softmmu –extra-cflags=”-D__USE_MINGW_ANSI_STDIO=1 -Wno-error=format -Wno-error=suggest-attribute=format -Wno-error=format-extra-args -DLIBSLIRP_STATIC” –extra-ldflags=”-lstdc++” –enable-slirp –enable-spice –enable-gtk –enable-vnc –prefix=/opt/qemu
$ make -j$(nproc)
$ sudo make install
$ cd /
$ tar czf ~/qemu-linux.tgz ./opt/qemu

  • You can copy the qemu-linux.zip to another Linux physical host for use if necessary.

Running Windows 10 VM on Ubuntu host (with audio and camera access in the VM)

– Get the Vendor and device ID of the Camera on the physical host
$ lsusb | grep -i cam
Bus 003 Device 002: ID 1bcf:2ba9 Sunplus Innovation Technology Inc. Integrated_Webcam_FHD
$
$ lsusb -d 1bcf:2ba9
Bus 003 Device 002: ID 1bcf:2ba9 Sunplus Innovation Technology Inc. Integrated_Webcam_FHD
$

– create the UDEV rule to enable access to the camera using its vendor and device ID
$ sudo tee /etc/udev/rules.d/99-qemu-usb.rules <<‘EOF’
SUBSYSTEM==”usb”, ATTR{idVendor}==”1bcf”, ATTR{idProduct}==”2ba9″, MODE=”0666″, GROUP=”kvm”
EOF

$ sudo udevadm control –reload-rules
$ sudo udevadm trigger

$ ls -l /dev/video*
crw-rw—-+ 1 root kvm 81, 0 Sep 25 21:54 /dev/video0
crw-rw—-+ 1 root kvm 81, 1 Sep 25 21:54 /dev/video1
crw-rw—-+ 1 root kvm 81, 2 Sep 25 21:54 /dev/video2
crw-rw—-+ 1 root kvm 81, 3 Sep 25 21:54 /dev/video3
$
$ ls -l /dev/bus/usb/003/002
crw-rw—- 1 root kvm 189, 257 Sep 25 21:18 /dev/bus/usb/003/002

$ cd /opt/qemu/bin
$ ./qemu-system-x86_64 -accel kvm \
-cpu host,hv_relaxed,hv_vapic,hv_time \
-machine q35 \
-L /opt/qemu/share/qemu \
-smbios type=0,vendor=”American Megatrends International,, LLC.”,version=”F10″ \
-smbios type=1,manufacturer=”ASUSTeK COMPUTER INC.”,product=”PRIME Z790-A WIFI”,version=”Rev 1.xx”,serial=”1234567890″ \
-smbios type=2,manufacturer=”ASUSTeK COMPUTER INC.”,product=”PRIME Z790-A WIFI”,version=”Rev 1.xx”,serial=”1234567890″ \
-smbios type=3,manufacturer=”ASUSTeK COMPUTER INC.”,version=”Rev 1.xx”,serial=”1234567890″ \
-netdev user,id=net0 \
-device e1000e,netdev=net0,mac=00:1A:2B:3C:4D:5E \
-drive file=~/mypc.qcow2,if=none,id=drive-sata0,cache=writeback,format=raw \
-device ide-hd,drive=drive-sata0,bus=ide.0,serial=”WDC-WD10EZEX-00BN7A0″,model=”Western Digital” \
-drive file=/VMs/Windows1022H2.iso,if=none,id=drive-cd0,media=cdrom -device ide-cd,drive=drive-cd0,bus=ide.1 \
-boot order=c,once=d,menu=on \
-m 16G -smp 4 -vga qxl \
-audiodev sdl,id=snd0 -device ich9-intel-hda -device hda-micro,audiodev=snd0 -device nec-usb-xhci,id=usbctrl \
-device usb-host,bus=usbctrl.0,vendorid=0x1BCF,productid=0x2BA9,suppress-remote-wake=on,pipeline=off

NOTE:
During the Windows 10 installation in the VM, Activation may fail with “Something went wrong: But you can try again”. OOBELOCAL – likely due to Windows not recognizing the NIC
Solution:
– On the error screen, Use the New Local Account Command: Press Shift + F10 (or Fn + Shift + F10) on the Microsoft account screen to open the Command Prompt. 
– Click on the black Command Prompt window to make sure it is active. 
– Type start ms-cxh:localonly and press Enter. 
– This opens the local account setup prompt so you can create a offline username. 
– The above launches the QEMU GUI on my X-Server since I am connected to the Linux server remotely using SSH from Mobaxterm on a Windows client
– Running QEMU on Windows – (remove “once=d,” and the “-drrve” parameter for CDROM if not using an ISO image or you already set up the O/S on the VM)

  • Alternative virtual machine definition:
    -smbios type=1,manufacturer=”Dell Inc.”,product=”OptiPlex 7090″,version=”1.0″,serial=”ABC123XYZ” -smbios type=2,manufacturer=”Dell Inc.”,product=”0XYZ12″,version=”A01″ -smbios type=3,manufacturer=”Dell Inc.”

———————————————————– ————————————————————

Running QEMU on Linux (CentOS Stream 9) – Windows 10 VM (with audio and camera access in the VM)

$ cd /opt/qemu/bin
$ qemu-img create -f /opt/qemu/mypc.qcow2 35g
$ ./qemu-system-x86_64 -accel kvm \
-cpu host,kvm=off,-hypervisor,hv_vendor_id=null,hv_relaxed,hv_vapic,hv_time \
-machine q35 \
-L /opt/qemu \
-smbios type=0,vendor=”American Megatrends International,, LLC.”,version=”F10″ \
-smbios type=1,manufacturer=”ASUSTeK COMPUTER INC.”,product=”PRIME Z790-A WIFI”,version=”Rev 1.xx”,serial=”1234567890″ \
-smbios type=2,manufacturer=”ASUSTeK COMPUTER INC.”,product=”PRIME Z790-A WIFI”,version=”Rev 1.xx”,serial=”1234567890″ \
-smbios type=3,manufacturer=”ASUSTeK COMPUTER INC.”,version=”Rev 1.xx”,serial=”1234567890″ \
-netdev user,id=net0 \
-device e1000e,netdev=net0,mac=00:1A:2B:3C:4D:5E \
-drive file=/opt/mypc.qcow2,if=none,id=drive-sata0,cache=writeback \
-device ide-hd,drive=drive-sata0,bus=ide.0,serial=”WDC-WD10EZEX-00BN7A0″,model=”Western Digital” \
-drive file=/VMs/Windows1022H2.iso,if=none,id=drive-cd0,media=cdrom -device ide-cd,drive=drive-cd0,bus=ide.1 \
-drive file=/VMs/Windows1022H2.iso,if=none,id=drive-cd0,media=cdrom \
-boot order=c,once=d,menu=on \
-m 16G \
-smp 4 \
-vga qxl

NOTE: As you go through the installation wizard, Windows (10) Activation may fail with “Something went wrong: But you can try again”. OOBELOCAL – likely due to Windows not recognizing the NIC
On the error screen, Use the “New Local Account Command”: Press Shift + F10 (or Fn + Shift + F10) on the Microsoft account screen to open the Command Prompt. 

Click on the black Command Prompt window to make sure it is active. 
Type start ms-cxh:localonly and press Enter. 
This opens the local account setup prompt so you can create a offline username. 

It is also possible that every time the installation needs to restart, it just hangs on a black screen after initiating the shutdown. Just terminate the QEMU process (or close the window) and launch it again, the installation will continue from where it previously left off.

The above VM creation command launches the QEMU GUI on my Mobaterm X-Server since I am connected to the physical Linux server remotely using SSH from Mobaxterm on a Windows system.

Alternative machine definition:
-smbios type=1,manufacturer=”Dell Inc.”,product=”OptiPlex 7090″,version=”1.0″,serial=”ABC123XYZ” -smbios type=2,manufacturer=”Dell Inc.”,product=”0XYZ12″,version=”A01″ -smbios type=3,manufacturer=”Dell Inc.”

———————————————————– ————————————————————

Running QEMU on Windows 11 – Windows 10 VM

cd C:\qemu-win
qemu-img.exe create -f mypc.qcow2 35g
qemu-system-x86_64.exe -accel whpx -cpu host,hv_relaxed,hv_vapic,hv_time -machine q35 -L C:\qemu-win\share -smbios type=0,vendor=”American Megatrends International,, LLC.”,version=”F10″ -smbios type=1,manufacturer=”ASUSTeK COMPUTER INC.”,product=”PRIME Z790-A WIFI”,version=”Rev 1.xx”,serial=”1234567890″ -smbios type=2,manufacturer=”ASUSTeK COMPUTER INC.”,product=”PRIME Z790-A WIFI”,version=”Rev 1.xx”,serial=”1234567890″ -smbios type=3,manufacturer=”ASUSTeK COMPUTER INC.”,version=”Rev 1.xx”,serial=”1234567890″ -netdev user,id=net0 -device e1000e,netdev=net0,mac=00:1A:2B:3C:4D:5E -drive file=C:\qemu-win\mypc.qcow2,if=none,id=drive-sata0,cache=writeback -device ide-hd,drive=drive-sata0,bus=ide.0,serial=”WDC-WD10EZEX-00BN7A0″,model=”Western Digital” -boot order=c,once=d,menu=on -m 16G -smp 4 -vga qxl -audiodev dsound,id=snd0 -device ich9-intel-hda -device hda-micro,audiodev=snd0 -device nec-usb-xhci,id=usbctrl -device usb-host,bus=usbctrl.0,vendorid=0x1BCF,productid=0x2BA9

NOTE: the QEMU command must be executed from an elevated permission (run as adminstrator) command prompt if you want networking in the VM
NOTE: restart intiated from Windows may not always work. it just hangs instead (QEMU throws the error – qemu: WHPX: Unexpected VP exit code 4). if this happens, just close QEMU, and relaunch ir again.
NOTE: Sound (speaker and microphone works) but camera is not detected.
NOTE: you may remove “once=d,” and the “-drive” parameter for CDROM if not using an ISO image or you already set up the O/S on the VM)

– NOTE: Testing your MIC on the physical host to ensure it works

The microphone was not working in the VM even though it was detected. No error but no sound. I then discovered it wasn’t functioning on the host either (muted)
List the real control names

$ amixer -c 0 scontrols
Simple mixer control ‘Master’,0
Simple mixer control ‘Headphone’,0
Simple mixer control ‘Headphone Mic’,0
Simple mixer control ‘Headphone Mic Boost’,0
Simple mixer control ‘Speaker’,0
Simple mixer control ‘PCM’,0
Simple mixer control ‘IEC958’,0
Simple mixer control ‘IEC958’,1
Simple mixer control ‘IEC958’,2
Simple mixer control ‘IEC958’,3
Simple mixer control ‘Capture’,0
Simple mixer control ‘Auto-Mute Mode’,0
Simple mixer control ‘Headset Mic’,0
Simple mixer control ‘Headset Mic Boost’,0
Simple mixer control ‘Internal Mic’,0
Simple mixer control ‘Internal Mic Boost’,0
[aitayemi@rocky ~]$

– Unmute and boost whatever shows up – Substitute the real names, for example:
$ amixer -c 0 sset ‘Internal Mic Boost’ 2
$ amixer -c 0 sset ‘Front Mic Boost’ 2
$ amixer -c 0 sset ‘Input Source’ ‘Internal Mic’ # or ‘Front Mic’
$ amixer -c 0 sset ‘Capture’ cap unmute

– Re-test with the level meter – should go and and down with sound – if it stays at zero, there is still an issue
$ arecord -D hw:0,0 -f cd -vv /dev/null

– Point QEMU at ALSA directly – Since hw:0,0 is the device you’re testing, bypass SDL and the sound server:
-audiodev alsa,id=snd0,in.dev=hw:0,0,out.dev=default

– Store the config so even if the system is restarted, it takes effect
$ sudo dnf install -y alsa-utils
$ sudo alsactl store

Forwarding traffic from external hosts to guest VMs on KVM host

This is so simple but took me forever to find a concise source on the Internet even with the AI results provided by most search engines now.

Background: I set up KVM (qemu/libvirtd) on a Linux host, create a VM on it. I want to be able to SSH (goes for other in-bound traffic such as HTTP as well) from an external host e.g., a separate windows laptop to the guest VM running on the KVM Linux host.

<Windows Laptop> — SSH —> <KVM host> —> <Guest-VM>

The default network created for the guest-VM on KVM is NAT (and goes over the virbr0 interface created on the KVM host during the KVM installation)

To illustrate, I created two guest-VMs (both Linux) and I want to be able to SSH to them from another Windows system in my home network. This means I need to connect to a designated unused port (e.g., 2222 or 2223) on the KVM host via SSH, and have the KVM host forward that connection to the SSHD service running on the guest VM

1/ You MUST add a rule to the LIBVIRT_FWI that allows that traffic otherwise you get something like “Connection refused” for SSH for example. By default, only traffic that is part of on-going session is forwarded, which in effect means you can’t initiate traffic to the VM from outside the KVM host (inserts as rule #1 on top of the chain).
For some reason, the firewall-cmd command works until you restart the system or restart firewalld service (which fails that there is no table/chain with that name), so using the iptables command instead. The “-t filter” can also be omitted since it is the default table (as opposed to others such as nat, mangle, etc)
sudo firewall-cmd –permanent –direct –add-rule ipv4 filter LIBVIRT_FWI 0 -p tcp –dport=22 -m state –state NEW,ESTABLISHED -j ACCEPT
sudo iptables -t filter -I LIBVIRT_FWI -p tcp –dport 22 -m state –state NEW,ESTABLISHED -j ACCEPT

So to make it persistent using systemd methodology, I created a service for the command:
a) Create the file /etc/systemd/system/libvirt_fwi.service with the following content:
[Unit]
Description=Enable libvirtd SSH forwarding to VMs
After=graphical.target

[Service]
Type=oneshot
ExecStart=/usr/sbin/iptables -t filter -I LIBVIRT_FWI -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT

[Install]
WantedBy=graphical.target


b) Run the commands to create and start the service:
sudo systemctl restart firewalld.service
sudo systemctl restart libvirtd
sudo systemctl daemon-reload
sudo systemctl enable libvirt_fwi.service
sudo systemctl start libvirt_fwi.service

2/ Add the rules to forward the traffic from the KVM host to the target VM. In this example, any incoming traffic to the KVM host on port 2222/tcp is forwarded to a specific VM (e.g., 192.168.124.217) on port 22, and incoming traffic on 2223/tcp is forwarded to a second guest VM with IP 192.168.122.100.
sudo firewall-cmd –permanent –direct –add-rule ipv4 nat PREROUTING 0 -p tcp –dport 2222 -j DNAT –to-destination 192.168.124.217:22
sudo firewall-cmd –permanent –direct –add-rule ipv4 nat PREROUTING 0 -p tcp –dport 2222 -j DNAT –to-destination 192.168.124.100:22
sudo firewall-cmd –reload

3/ You can SSH from the KVM host to the VMs using their IP addresses.
These commands allows one to SSH to the VMs from the KVM host itself (the IPs belong to the VMs):
sudo iptables -t nat -A PREROUTING -p tcp –dport 2222 -j DNAT –to-destination 192.168.122.217:22
sudo iptables -t nat -A PREROUTING -p tcp –dport 2223 -j DNAT –to-destination 192.168.122.100:22

4/ SKIP THIS STEP – because KVM already added masquerade rules for the guest network to the NAT table – I am just including it here for reference in case it is needed for other purposes e.g., a hypervisor that requires you to do the NAT set up yourself: Enable NAT of out-going traffic using the KVM host interface the traffic will traverse out. In this example we only masquerade the subnet range used by KVM for the VMs, but you can also masquerade all outgoing traffic on that host interface wlp0s20f3 by leaving out the “-s network” parameter
sudo iptables -t nat -A POSTROUTING -s 192.168.124.0/24 -o wlp0s20f3 -j MASQUERADE

5/ Depending on your Linux distro, there are many ways to make the iptables changes permanent (across reboots). Below is one method for RHEL and its variants (CentOS, Fedora, Rocky, etc):
sudo yum install iptables-services
sudo systemctl enable iptables
sudo systemctl enable ip6tables
sudo systemctl status iptables
sudo systemctl status firewalld
sudo iptables-save > /etc/sysconfig/iptables

5/ To check the IP of your VM (outside from logging into it), you can use “sudo virsh domifaddr <guest-vm>” and the “sudo virsh net-dumpxml default” shows the IP range KVM allocates IP from to the guest VMs.

  • NOTE: You can use this guide to set up KVM on the host, but I didn’t need step 4 because I am not using the ethernet interface/NIC on my KVM host to create a bridge to my home WIFI (and it is very difficult to create a bridge using the WiFi interface – if this is something you need, better to use a hypervisor like VMWare or VirtualBox or Promox, etc.)
  • Making iptables rules permanent: https://www.cyberciti.biz/faq/how-to-save-iptables-firewall-rules-permanently-on-linux/
  • IF you have a multi-NIC KVM host, and your in-coming traffic that you want to forward to the VM is coming on a specific NIC, then you use a slightly different rule (where the SOURCE_IP_ADDRESS is the IP on that NIC):
    sudo firewall-cmd –permanent –direct –add-rule ipv4 nat PREROUTING 0 \
    -s <SOURCE_IP_ADDRESS> -p <PROTOCOL> –dport <PUBLIC_PORT> \
    -j DNAT –to-destination <INTERNAL_IP_ADDRESS>:<INTERNAL_PORT>
  • Use “sudo firewall-cmd –direct –get-all-rules” to display the direct rules

Minishift notes

Background: attempting to allow external hosts on the same network as the KVM host on which minishift VM is running to be able to access the minishift web UI and optionally via SSH. In this example, 192.168.42.62 is the IP assigned to minishift after starting it, and 192.168.10.0/24 is the IP range of the network on which the KVM host is running.

On my KVM Linux host, installing libvirtd had created the virbr0 bridge. Subsequently setting up minishift setup a second bridge named virbr1 to which the vNIC assigned to the minishift VM is slaved. Interestingly enough, the minishift VM is then given 2x interfaces with one attached to each bridge (virbr0 and virbr1). In the minishift VM, the default route is assigned to the interface (e.g., eth0) attached to the first bridge (virbr0) with IP 192.168.122.30, even though when minishift starts, it displays a URL with the IP address (192.168.42.62) assigned to the interface connected to the second bridge as the way to access the minishift web UI. This works fine as long as you are attempting to access the URL from the KVM host, but won’t work without some extra steps if you want to access the minishift UI from an external host.
wlp5s0 is the “public” interface on my KVM host.

Setup and start minishift (equivalent to RedHat CDK if you have the right subscription):
itababa@itamint:~$ sudo apt update -y
itababa@itamint:~$ sudo apt install qemu-kvm qemu-system qemu-utils python3 python3-pip libvirt-clients libvirt-daemon-system bridge-utils virtinst libvirt-daemon virt-manager cpu-checker -y
itababa@itamint:~$ usermod -aG libvirt $(whoami)
itababa@itamint:~$ newgrp libvirt
itababa@itamint:~$ sudo systemctl enable libvirtd
itababa@itamint:~$ sudo systemctl start libvirtd
itababa@itamint:~$ sudo virsh net-start default
itababa@itamint:~$ sudo virsh net-autostart default
itababa@itamint:~$ sudo curl -L https://github.com/dhiltgen/docker-machine-kvm/releases/download/v0.10.0/docker-machine-driver-kvm-ubuntu16.04 -o /usr/local/bin/docker-machine-driver-kvm
itababa@itamint:~$ sudo chmod +x /usr/local/bin/docker-machine-driver-kvm
itababa@itamint:~$ curl -L https://github.com/minishift/minishift/releases/download/v1.34.3/minishift-1.34.3-linux-amd64.tgz -o minishift-1.34.3-linux-amd64.tgz
itababa@itamint:~$ tar xf minishift-1.34.3-linux-amd64.tgz
itababa@itamint:~$ sudo cp minishift-1.34.3-linux-amd64/minishift /usr/local/bin/
itababa@itamint:~$ minishift start

NOTE: the “minishift start” command will display the minishift web UI at the end of its run.
Reference: https://docs.okd.io/3.11/minishift/getting-started/setting-up-virtualization-environment.html

Settings needed on the KVM host:
- configure the KVM host to redirect connections to it on 8443/tcp to the minishift host (192.168.42.62). Optionally redirect 2222/tcp to the minishift host as well:
iptables -F
iptables -F -t nat
echo 1 > /proc/sys/net/ipv4/ip_forward
echo 1 > /proc/sys/net/ipv4/conf/virbr0/proxy_arp
echo 1 > /proc/sys/net/ipv4/conf/virbr1/proxy_arp    (required so that the host can answer when the minishift VM tries to find the ARP of external hosts, otherwise the minshift will not respond to any connection attempts)
iptables -t nat -A POSTROUTING -o wlp5s0 -j MASQUERADE
iptables -t nat -A PREROUTING -i wlp5s0 -p tcp --dport 8443 -j DNAT --to-destination 192.168.42.62:8443
iptables -t nat -A PREROUTING -i wlp5s0 -p tcp --dport 2222 -j DNAT --to-destination 192.168.42.62:22
Settings needed on the minishift VM after it is running (execute "minishift ssh" to SSH into the minishift VM):
- NOTE: this whole section can be skipped if you choose to "DNAT" port 8443/tcp to the IP address on the virbr0 vNIC on the KVM host:
itababa@itamint:~$ minishift ssh
[docker@minishift ~]$
 sudo su -
[root@minishift ~]# ip a     (find the interface with the 192.168.42.x IP e.g., eth1)
[root@minishift ~]# ip route add 192.168.10.0/0 via dev eth1 (this is because the default route uses the NIC with 192.168.122.x IP and used by minishift to access the Internet)
[root@minishift ~]# ip route show
default via 192.168.122.1 dev eth0 proto dhcp metric 100
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
192.168.10.0/24 dev eth1 scope link
192.168.42.0/24 dev eth1 proto kernel scope link src 192.168.42.62 metric 101
192.168.122.0/24 dev eth0 proto kernel scope link src 192.168.122.30 metric 100
[root@minishift ~]#
NOTE: if you restart the minishift VM, you have to SSH into it again and re-add the route to the 192.168.10.0/24 network.

Settings needed on the external host:
In this example, I want to access the minishift web UI from a Windows system on my home network. You need to add a route to the minishift IP address using the KVM host IP as the gateway (using the command line):
C:\Windows\system32>route add 192.168.42.206 MASK 255.255.255.255 192.168.10.4

Launch a browser on the external host and go directly to https://192.168.42.206:8443/console/catalog

NOTE: if you attempt to login via https://192.168.42.206:8443/console , you will encounter this error/bug after entering your credentials (e.g., admin/admin)
Error: “Error. Invalid request. Client state could not be verified. Return to the console.”
Bug: “https://bugzilla.redhat.com/show_bug.cgi?id=1589072”
Found the solution (use the /console/catalog URL): https://github.com/openshift/openshift-azure/issues/236

after login with admin/admin credential

Other commands:
minishift delete –clear-cache (solution for error similar to: “Cannot start – error starting the VM: error getting the state for host: machine dies not exist-docker” when attempting to start minishift)
sudo virsh stop minishift; sudo undefine minishift; (might be needed if the “delete” command does not fix the issue)
sudo arp -a (on the KVM host to see the IPs of the minishift VM or SSH into the minishift VM)

– To restart the minishift VM (from the KVM host):
itababa@itamint:~$ minishift stop
itababa@itamint:~$ minishift start


INSTALL OSX VENTURA (13.x) ON QEMU/KVM (Linux)

NOTE: for educational purposes only

GUIDE: Used OSX-KVM with modifications: https://github.com/kholia/OSX-KVM

  • I installed the latest QEMU (compiled from source):
    root@itamint:~/# apt install -y gcc make ninja-build libglib2.0-dev libpixman-1-dev ncurses-dev libusb-dev libusb-1.0-0-dev libusbredirparser1 libusbredirhost1 usbutils
    root@itamint:~# git clone https://github.com/qemu/qemu.git
    root@itamint:~#cd qemu
    root@itamint:~/qemu# ./configure –enable-vde –enable-libusb –prefix=/usr/local –target-list=”i386-softmmu x86_64-softmmu”
    root@itamint:~/qemu# make
    root@itamint:~/qemu# make install
    root@itamint:~/qemu# qemu-system-x86_64 –version
    QEMU emulator version 7.2.50 (v7.2.0-2313-g9832009d9d)

  • Install libvirtd/QEMU
    root@itamint:~# apt update -y
    root@itamint:~# apt install qemu-kvm qemu-system qemu-utils python3 python3-pip libvirt-clients libvirt-daemon-system bridge-utils virtinst libvirt-daemon virt-manager cpu-checker -y
    root@itamint:~# usermod -aG root
    root@itamint:~# newgrp kvm
    root@itamint:~# systemctl enable libvirtd
    root@itamint:~# systemctl start libvirtd
    root@itamint:~# virsh net-start default
    root@itamint:~# virsh net-autostart default

    NOTE: this is to make networking config easy. We won’t use the qemu version 6.x installed above. We will use the much newer version we compiled earlier which we installed in /usr/local/bin/ whereas the one installed by the apt command puts qemu in /usr/bin/. To confirm you are using the right qemu, run “which qemu-system-x86_64”

  • Setup networking so that the VM has network access:
    I have put all the networking and firewall rules in a script which can then be executed with a command such as “sudo /home/itababa/setup_firewall.sh”

    itababa@itamint:~/OSX-KVM$ cat /home/itababa/setup_firewall.sh
    #!/usr/bin/bash

    # create a VDE switch and add a subnet range to it. also add an IP address to the switch as it will be the gateway for connected VMs:
    vde_switch -tap vde0 -daemon
    ip link set dev vde0 up
    ip addr add 10.0.2.1/24 dev vde0
    ip route add 10.0.2.0/24 dev vde0
    echo 1 > /proc/sys/net/ipv4/conf/vde0/proxy_arp

    # NOTE: it is possible to setup DHCP on the switch but I will be using static IPs in this guide

    # Internet access to the VMs (execute on the QEMU hypervisor host) where wlp5s0 is the WAN NIC on the Ubuntu QEMU host:
    echo 1 > /proc/sys/net/ipv4/ip_forward
    iptables -t nat -A POSTROUTING -o wlp5s0 -j MASQUERADE
    iptables -I FORWARD 1 -i vde0 -j ACCEPT
    iptables -I FORWARD 1 -o vde0 -m state –state RELATED,ESTABLISHED -j ACCEPT

    # On the QEMU hypervisor (Linux) host, configure the rules for the ports to be forwarded to the OSX VM:
    iptables -A INPUT -p tcp –dport 2222 -j ACCEPT
    iptables -t nat -A PREROUTING -p tcp –dport 2222 -j DNAT –to-destination 10.0.2.100:22
    iptables -A INPUT -p tcp –dport 5905 -j ACCEPT
    iptables -t nat -A PREROUTING -p tcp –dport 5905 -j DNAT –to-destination 10.0.2.100:5900
    # end of networking script

  • “Install” OSX in the VM using OSX-KVM:

itababa@itamint:~/$ sudo echo 1 | sudo tee /sys/module/kvm/parameters/ignore_msrs
itababa@itamint:~/$ sudo cp kvm.conf /etc/modprobe.d/kvm.conf

itababa@itamint:~/$ sudo groupadd kvm
itababa@itamint:~/$ sudo groupadd libvirt
itababa@itamint:~/$ sudo groupadd input

itababa@itamint:~/$ sudo usermod -aG kvm $(whoami)
itababa@itamint:~/$ sudo usermod -aG libvirt $(whoami)
itababa@itamint:~/$ sudo usermod -aG input $(whoami)

itababa@itamint:~/$ cd ~
itababa@itamint:~/$ git clone –depth 1 –recursive https://github.com/kholia/OSX-KVM.git
itababa@itamint:~/$ cd OSX-KVM
itababa@itamint:~/OSX-KVM$ pwd
/home/itababa/OSX-KVM

  • Fetch the Ventura Installer (option 6):
    itababa@itamint:~/OSX-KVM$ ./fetch-macOS-v2.py

  • Convert the downloaded BaseSystem.dmg file into the BaseSystem.img file.
    itababa@itamint:~/OSX-KVM$ dmg2img -i BaseSystem.dmg BaseSystem.img

  • Create a virtual HDD image where macOS will be installed.
    itababa@itamint:~/OSX-KVM$ qemu-img create -f qcow2 mac_hdd_ng.img 128G

  • Edit the OpenCore-Boot.sh script and make the following changes:
    – increase the RAM from 4096MB to 8192MB (ALLOCATED_RAM variable)
    – add avx2 to the CPU flags list (MY_OPTIONS variable)
    – change the CPU from Penryn to Cascadelake-Server-noTSX (only one that didn’t cause random freezing after installation and reboot loop during installation from the several I tested. Unfortunately the VM still freezes/hangs consistently between 10 and 12 minutes after startup.)
    – since we are VDE, comment out the default NIC entry starting with “-netdev user” and replace with our VDE line (see below).
    – Below is how the relevant lines looked in my modified OpenCore-Boot.sh file:
    root@itamint:/home/itababa/OSX-KVM# diff OpenCore-Boot.sh OpenCore-Boot.sh.org
    MY_OPTIONS=”+ssse3,+sse4.2,+popcnt,+avx,+avx2,+aes,+xsave,+xsaveopt,check”
    ALLOCATED_RAM=”8192″ # MiB
    -enable-kvm -m “$ALLOCATED_RAM” -cpu Cascadelake-Server-noTSX,kvm=on,vendor=GenuineIntel,+invtsc,vmware-cpuid-freq=on,”$MY_OPTIONS”
    -device virtio-net-pci,netdev=net0,mac=52:54:00:e6:5d:16 -netdev vde,id=net0
  • Run the OpenCore-Boot.sh script (with admin rights as root or using sudo) to create the VM and start the installation (this starts a VNC session listening on localhost:5901 for example. Use Mobaxterm/VNC or putty tunnel+VNC client to access the VNC session)
    itababa@itamint:~/OSX-KVM$ sudo ./OpenCore-Boot.sh
    VNC server running on 127.0.0.1:5901 (output of executing the OpenCore-Boot.sh script)
    NOTE: without “sudo” or “root” user, you will get an error similar to “Could not open vde: No such file or directory”

  • Connnect to the VNC session (using a VNC client):

  • Go through the installation steps (use Disk Utility to erase the target disk, then install Ventura)

  • Optional: Login into the OSX and enabled “remote management” (VNC) and “remote login” (SSH) (from “System Settings …” – it is no longer called “System Preferences”)

  • Optional: if you enabled “remote management” (VNC) and/or “remote login” (SSH), you can connect directly to the OSX VM via a VNC client (using the IP address of the Linux host and port 5901) and/or via SSH (using the IP address of the Linux host and port 2222). The connections work because we included the relevant forwarding rules in the firewall script (above).

  • Naviate to “System Settings” > Network > Ethernet > “Details…”. Assign a static IP address such as 10.0.2.100 (see the setup_firewall.sh script above) to the OSX with subnet mask 255.255.255.0 and Router 10.0.2.1 (IP address of the VDE switch). Also assign the VDE switch IP address as the DNS Server.

  • Optional: Update OpenCore-Boot.sh and comment out the 2 lines that attaches the installer disk to the VM i.e.,
    # -device ide-hd,bus=sata.3,drive=InstallMedia
    # -drive id=InstallMedia,if=none,file=”$REPO_PATH/BaseSystem.img”,format=raw
  • Optional: shutdown OSX. Modify OpenCore-Boot.sh so that it starts the VM “headless” (i.e., running as a background process):
    Comment out the line “-monitor stdio” and add a line below it with “-daemonize” without the double-quotes.

  • Start the VM in the (OSX-KVM directory) with the command: sudo ./OpenCore-Boot.sh

  • Configure the VM to auto-boots from the OSX disk, run the command at the UEFI shell:
    bcfg boot add 0 FS0:\EFI\boot\BOOT_X64.efi “my_boot”

  • Boot to OSX from the UEFI shell with the command sequence (ENTER key after each one): FS0: > cd EFI > cd BOOT > BOOTx64.efi

  • You can now connect to the OSX VM with VNC (Linux host IP and port 5905 in this example) or SSH (Linux host IP and port 2222 in this example).

NOTE: Several methods to attach a USB stick/device (e.g., one containing TimeMachine backups) to a QEMU VM

Option 1: In Linux Mint (Debian/Ubuntu), attached USB storage gets the group “disk”. So add your normal user to that group.
$ sudo usermod -aG disk itababa
$ newgrp disk

– Then add the device nodename of the USB device to the “args” list for QEMU in the OpenBoot-core.sh file (get the device file from the output of “blkid” or “lsblk” (e.g., /dev/sdb)
-hdc /dev/sdb

– you can also use the following 2x lines (equivalent to the single “-hdc /dev/sdb” line above)
-drive id=USBstick,if=none,file=”/dev/sdb”
-device ide-hd,bus=sata.5,drive=USBstick

Note: in OSX, since the USB is “direct-attached”, it is treated as a HDD. To make the volume appear on the desktop, go to “Finder” (menu) > “Settings”. Check/select/enable the “Hard disks” option.

Option 2: Add the USB device’s “location” to the args list for QEMU in the OpenBoot-core.sh file (get the hostbus and hostaddr from the output of the “lsusb” commmand). You must also change the permission on the device files (e.g., “sudo chmod o+rw /dev/bus/usb/002/*”). This operation does not survive a reboot of the physical host or re-insertion of the USB, so if you want it permanent consider UDEV rules):
-device qemu-xhci,id=usbxhci
-usb -device usb-host,hostbus=2,hostaddr=6

For example, in the “lsusb” output below, “Bus” is “hostbus” and “Device” is “hostaddr”
root@itamint:/home/itababa/OSX-KVM# lsusb
…
Bus 002 Device 006: ID 05dc:a838 Lexar Media, Inc. JumpDrive Tough

IMPORTANT NOTE: to prevent the OSX on QEMU from freezing/hanging (after about 10 mins uptime):
– Go to “System Settings …” > Energy Saver > Conserve battery > “Put hard disks to sleep when possible” disabled.
– Go to “System Settings …” > “Lock Screen” > “Turn display off when inactive” to “Never”
– Also, you MUST login to an account, if you leave the system on the login screen, it will freeze/hang.

* If you restore a time machine back along with “System & Networking”, check afterwards that the two settings above have not reverted to their defaults.

  • NOTE: Take snapshots often (e.g., before using “Migration Assistant” to restore TimeMachine backups from another OSX)
    – take an internal snapshot: qemu-img snapshot -c mac_hdd_ng.ss.030823.img mac_hdd_ng.img
    – list internal snapshots: qemu-img snapshot -l mac_hdd_ng.img
    – revert/restore to a snapshot: qemu-img snapshot -a mac_hdd_ng.ss.030823.img mac_hdd_ng.img
  • NOTE: IF “managed device attestation” is configured on your source OSX from which you restored a Time Machine backup, you may notice ACME (or a similar policy enforcement) app is installed and running.
    IF your source OSX is part of a corporate network, unless you decide to connect to the corporate VPN (unlikely for the purpose of this procedure), ACME won’t be able to validate your OSX VM config and may isolate your restored OSX VM (no networking access). To fix this, temporarily stop the ACME app, or permanently remove it:
    – Use the Utilities > “Activity Monitor” > to stop the ACME process
    – Go to Applications folder and delete (“move to trash”) the ACME app shortcut
    – Go to folder ~/Library/ and delete (“move to trash”) the ACME folder

References:
https://wiki.qemu.org/Documentation/Networking
https://documentation.suse.com/sles/15-SP2/html/SLES-all/cha-qemu-running.html
https://qemu-project.gitlab.io/qemu/system/qemu-cpu-models.html
https://kb.nmsu.edu/page.php?id=99123 (enable remote management i.e., VNC)
https://setapp.com/how-to/how-to-access-your-mac-remotely
https://www.qemu.org/2017/11/22/haxm-usage-windows/
https://qemu-project.gitlab.io/qemu/system/devices/usb.html
https://askubuntu.com/questions/15570/configure-udev-to-change-permissions-on-usb-hid-device/15643
https://unix.stackexchange.com/questions/141255/give-a-specific-user-permissions-to-a-device-without-giving-access-to-other-user
https://blog.programster.org/qemu-img-cheatsheet
https://www.linux-kvm.org/images/6/65/02x08B-Max_Reitz-Backups_with_QEMU.pdf
https://unix.stackexchange.com/questions/530674/qemu-doesnt-respect-the-boot-order-when-booting-with-uefi-ovmf
https://github.com/sickcodes/Docker-OSX
https://www.cyberciti.biz/faq/kvm-forward-ports-to-guests-vm-with-ufw-on-linux/
https://support.apple.com/guide/deployment/managed-device-attestation-dep28afbde6a/web