summaryrefslogtreecommitdiff
path: root/public/software/winvm.sh
blob: 09283e0a9deba6a07254bf9a08ab0ed23a9e7405 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#!/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.