#!/bin/sh # This is the script I used to use to launch QEMU, emulating # Windows 8 with direct physical access to a powerful GPU # and the Intel audio controller. This isn't a complete guide, # more a collection of tips for any other brave people mad # enough to try this. Look up "{VFIO,VGA,GPU,PCI} passthrough" # if you want to get proper guidance for this. # This was working on QEMU 2.12, but note that I'm not updating it anymore. # Create hugepage FS. Don't forget to actually populate it # with available RAM using kernel boot arguments or sysctl. # The user you run QEMU under (not root!) must be in the kvm group. sudo mkdir /dev/hugepages sudo mount -t hugetlbfs hugetlbfs /dev/hugepages sudo chown root:kvm /dev/hugepages sudo chmod 1777 /dev/hugepages # Necessary apparently # The block devices you use for the VM are up to you; # mine are at /dev/mapper/win{dows,data}. # I recommend refreshing udev after you've created them: udevadm trigger ### USEFUL KERNEL ARGUMENTS # intel_iommu=on : VFIO won't work otherwise. I don't have an AMD CPU, # but there should be a similar option for that. # vfio-pci.ids=XXXX:YYYY,.. : Only available if VFIO is built into the kernel # (requires custom kernel build). Reserves devices at boot. # hugepages=X hugepagesz=Y : Create X hugepages at boot of size Y ("2M" or "1G"). # nohugeiomap : This fixed something, but I can't remember what. # intremap=no_x2apic_optout : Some UEFIs don't support the x2apic and disable it. # This force-enables it. Had no negative consequences for me. # pci=pcie_bus_peer2peer : This was the silver bullet for me, but YMMV. # isolcpus=X-Y nohz_full=X-Y rcu_nocbs=X-Y : You MUST use CPU pinning if you add this. # X-Y is an inclusive range of CPU cores to # to reserve for the vCPUs. ### MACHINE OPTIONS QEMU="-name debug-threads=on -enable-kvm -machine q35,accel=kvm,kernel_irqchip=on,vmport=off,mem-merge=off" # -debug-threads=on : name the vCPU threads, useful for CPU pinning. # -enable-kvm : enable KVM acceleration. # -machine q35 : emulate the Q35 chipset, which is closer to a modern PC. # accel=kvm : probably identical to -enable-kvm, but just in case. # kernel_irqchip=on : emulate an IRQ chip in the kernel instead of in QEMU. # vmport=off : don't emulate a VMWare I/O port. # mem-merge=off : disable KSM, since there is only one VM. ### CPU OPTIONS QEMU="$QEMU -cpu host,kvm=off,hv_time,hv_relaxed,hv_spinlocks=0x1fff,hv_vpindex,hv_reset,hv_runtime,hv_crash,hv_vendor_id=NvidiaFix" # -cpu host : use the host CPU instead of emulating one. # kvm=off : don't declare self as KVM to the guest, as Windows doesn't care. # hv_* : pretend to be Hyper-V, so Windows can optimize itself for running as a guest. # https://www.reddit.com/r/VFIO/comments/479xnx/guests_with_nvidia_gpus_can_enable_hyperv/ QEMU="$QEMU -smp sockets=1,cores=4,threads=1" # -smp ... : processor layout to emulate. ### RAM OPTIONS QEMU="$QEMU -m 12G -mem-path /dev/hugepages -mem-prealloc" # -m x : amount of guest RAM. # -mem-path ... : allocate memory from this pool. Can be a file or "hugepages" (mount -t hugetlbfs). # -mem-prealloc : allocate all memory from -mem-path at startup rather than on demand. ### EMULATION OPTIONS QEMU="$QEMU -vga none -nodefaults -rtc base=utc,clock=host,driftfix=none" # -vga none : don't emulate a graphics card, since we're using a physical one. # -nodefaults : don't emulate any of the default devices. # -rtc base=utc : emulate an RTC starting at host's local time. # clock=host : use the host's accurate clock for VM timekeeping. # driftfix=none : don't fix Windows' clock drifting, as that involves injecting interrupts. QEMU="$QEMU -drive if=pflash,format=raw,file=/usr/share/edk2-ovmf/OVMF_CODE.fd,readonly" QEMU="$QEMU -drive if=pflash,format=raw,file=/usr/share/edk2-ovmf/OVMF_VARS.fd" # These options enable using the OVMF virtual UEFI instead of SeaBIOS. # The exact file locations might vary; this is for Gentoo. ### PCI PASSTHROUGH QEMU="$QEMU -device ioh3420,chassis=1,port=1,multifunction=on,bus=pcie.0,addr=1c.0,id=pcie.1" # -device ioh3420 : emulate a PCIe I/O hub to attach the GPU to. # chassis=1,port=1 : ? # bus=pcie.0,addr=1c.0 : place it at 00:1c.0 on the guest. # id=pcie.1 : refer to this device as "pcie.1" below. QEMU="$QEMU -device vfio-pci,host=04:00.0,multifunction=on,bus=pcie.1,addr=00.0" # GPU VGA controller. # multifunction=on : this device isn't just a VGA controller. # bus=pcie.1,addr=00.0 : attach it to the I/O hub as function 0. QEMU="$QEMU -device vfio-pci,host=04:00.1,bus=pcie.1,addr=00.1" # GPU HDMI audio controller. # bus=pcie.1,addr=00.1 : attach it to the I/O hub as function 1. QEMU="$QEMU -device vfio-pci,host=00:1b.0,bus=pcie.0,addr=1b.0" # Intel HDA audio controller. # bus=pcie.0,addr=1b.0 : place the device where Intel usually puts its HDA controller. #QEMU="$QEMU -device intel-iommu,intremap=on" # Expose the IOMMU to the guest too. Probably useless in this case. ### USB PASSTHROUGH QEMU="$QEMU -usb" # Enable USB support. QEMU="$QEMU -device usb-host,vendorid=0xXXXX,productid=0xYYYY" # If one of these can't be found, it's simply ignored. Use lsusb to find the IDs. ### STORAGE OPTIONS QEMU="$QEMU -drive if=ide,format=raw,discard=unmap,detect-zeroes=unmap,file=/dev/mapper/windows" QEMU="$QEMU -drive if=ide,format=raw,discard=unmap,detect-zeroes=unmap,file=/dev/mapper/windata" # if=ide : emulate an IDE (SATA) drive. NVMe is possible too, but more of a hassle. # format=raw : talk directly to the drive. Take care: your guest GPT might end up inside a partition. # discard=unmap,detect-zeroes=unmap : send discards to the physical device if the guest asks for it. # file=x : backing physical block device. #QEMU="$QEMU -cdrom ~/Windows.iso" # For recovery purposes. I strongly recommend keeping the ISO around. ### NETWORK OPTIONS QEMU="$QEMU -netdev user,id=usermode" # -netdev user : emulate a user-mode NIC, which is more than fast enough in my experience. # id=usermode : call the interface "usermode" QEMU="$QEMU -device e1000,netdev=usermode,mac=88:88:88:88:88:88,bus=pcie.0,addr=19.0" # -device e1000 : emulate a gigabit ethernet device. # netdev=usermode : use "usermode" as the host backend. # mac=x : set the MAC address as seen by the guest. # bus=pcie.0,addr=19.0 : place the device where Intel usually puts its NIC. # GERONIMO! qemu-system-x86_64 $QEMU -daemonize # -daemonize : fork once the VM has been initialized. QEMU_PID=`pidof qemu-system-x86_64` echo -e "\033[37;1mQEMU started at PID $QEMU_PID, emulating Windows in Hyper-V mode.\033[m" sleep 2 # Make sure the VM threads have been spawned. # Move kernel processes to the housekeeping core (core 0 here). #echo 00001 > /sys/bus/workqueue/devices/writeback/cpumask #echo 00001 > /sys/bus/workqueue/devices/nvme-wq/cpumask # Do CPU pinning using "taskset -pc X Y" here, # using /proc/$QEMU_PID/task/*/stat to find the thread name. # Sorry, my old script for this was so horrible that I don't want to share it.