#!/bin/bash ## Edit this comand before using it ## Required software ## DNSmasq ## samba ## iproute2 ## qemu ## ovmf ## Maybe I am missing something else ## For fucking Windows to be able to install stuff on a network drive ## https://community.spiceworks.com/topic/366976-trying-to-install-a-program-to-a-network-drive ## All you need to do is run Registry Editor (regedit.exe), locate the key ## HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Windows/CurrentVersion/Policies/System, and create a new DWORD entry with the name ## EnableLinkedConnections and value 1: ## Check if the script is being run as root [[ "$EUID" -ne 0 ]] && echo "Please run as root" && exit 1 ## Load the config file. source config ## Memory lock limit. ## By default, at least in Arch, a user can not lock so much memory, so you have to allow it plus 10 just to be sure. ## The `+100000` is just because it ay use a little shade over 12G and that is 100M ## If you get an error that QEMU can't allocate memory just do `ulimit -l unlimited` as root. Revert it after you shutdown the VM. if [ $(ulimit -a | grep "max locked memory" | awk '{print $6}') != $(( $(echo $RAM | tr -d 'G')*1048576+100000 )) ]; then ulimit -l $(( $(echo $RAM | tr -d 'G')*1048576+100000 )) fi ## Kill X and related. You can change `lightdm` and `i3` for whatever you use. ## Make shure you don't have anything important open because this will kill the X session. ## You can also just do "killall xorg" or "killall xinit" systemctl stop lightdm > /dev/null 2>&1 killall i3 > /dev/null 2>&1 # killall xorg # killall xinit sleep 2 ## START ## Network stuff ## THIS REQUIRES A FIREWALL BACKEND LIKE IPTABLES. If there is nothing routing the traffic this will be useless. ## Also open ports 53 tcp/udp 67,68 tcp/udp and icmp in ther firewall rules for the $TAP_INTERFACE tap_interface(){ tap_stop(){ if [[ $(ip tuntap list | grep $1) ]] then sudo ip link set dev $1 down sudo ip tuntap del mode tap name tap0 fi } tap_start(){ if [[ ! $(ip tuntap list | grep $1) ]] then #sudo tunctl -u $VM_USER sudo ip tuntap add mode tap user $USER name tap0 sudo ip addr add dev $1 10.10.10.1/24 sudo ip link set dev $1 up fi } if [ $1 == start ] then tap_start $TAP_INTERFACE elif [ $1 == stop ] then tap_stop $TAP_INTERFACE fi } dhcp_server(){ dhcp_stop(){ [[ -f /var/run/dnsmasq.pid ]] && sudo kill -15 $(cat /var/run/dnsmasq.pid) && sudo rm /var/run/dnsmasq.pid } dhcp_start(){ [[ -f /var/run/dnsmasq.pid ]] || dnsmasq --conf-file=$DNSMASQ_CONF } if [ $1 == start ] then dhcp_start elif [ $1 == stop ] then dhcp_stop fi } ## Samba server controls samba_server(){ samba_stop(){ [[ -f /var/run/smbd.pid ]] && echo "Stopping samba" && sudo kill -15 $(cat /var/run/smbd.pid) || echo "Samba was already stopped" } samba_start(){ [[ -f /var/run/smbd.pid ]] && echo "Samba was already started" || echo "Starting samba" && sudo smbd --configfile=$SMB_CONF } if [ $1 == start ] then samba_start elif [ $1 == stop ] then samba_stop fi } ## Start the network tap_interface start dhcp_server start samba_server start ## For samba to work you must run as root `smbpasswd -a ` ## END ## Network stuff ## Kill the console to free the GPU. ## The console, by default, is attached to the boot GPU, in this case there is only one and we need to left it unused to remove it from the system. echo 0 > /sys/class/vtconsole/vtcon0/bind echo 0 > /sys/class/vtconsole/vtcon1/bind echo efi-framebuffer.0 > /sys/bus/platform/drivers/efi-framebuffer/unbind ## Unload the Kernel Modules that use the GPU ## You may need to unload more or other kernel modules (drivers) for your specific GPU. ## This command allows you to see which modules depend uppon others. Change nvidia for noveau if you are using the OSS drivers or for the AMD equivalent. # lsmod | grep nvidia ## The left column are the modules and the right are the modules that are using that module. The modules should be unloaded only if there is no other modules using it; if there are, then unload them first. # nvidia_drm 49152 7 # nvidia_modeset 1044480 23 nvidia_drm # nvidia 16605184 1038 nvidia_modeset modprobe -r nvidia_drm modprobe -r nvidia_modeset modprobe -r nvidia ## Also unload the audio modules, may be more than one, because they use the audio device in the GPU modprobe -r snd_hda_intel ## Load the kernel module related to vfio modprobe vfio modprobe vfio_iommu_type1 modprobe vfio-pci ## Detach the GPU from drivers and attach to vfio. Also the usb. ## This may not be the right way, but it works for me and there is no symptoms of anything not working. echo $videoid > /sys/bus/pci/drivers/vfio-pci/new_id echo $videobusid > /sys/bus/pci/devices/$videobusid/driver/unbind echo $videobusid > /sys/bus/pci/drivers/vfio-pci/bind echo $videoid > /sys/bus/pci/drivers/vfio-pci/remove_id echo $audioid > /sys/bus/pci/drivers/vfio-pci/new_id echo $audiobusid > /sys/bus/pci/devices/$audiobusid/driver/unbind echo $audiobusid > /sys/bus/pci/drivers/vfio-pci/bind echo $audioid > /sys/bus/pci/drivers/vfio-pci/remove_id echo $usbid > /sys/bus/pci/drivers/vfio-pci/new_id echo $usbbusid > /sys/bus/pci/devices/$usbbusid/driver/unbind echo $usbbusid > /sys/bus/pci/drivers/vfio-pci/bind echo $usbid > /sys/bus/pci/drivers/vfio-pci/remove_id ## QEMU (VM) command qemu-system-x86_64 -runas $USER -enable-kvm \ -nographic -vga none -parallel none -serial none \ -m $RAM \ -cpu host,kvm=off,hv_relaxed,hv_spinlocks=0x1fff,hv_time,hv_vapic,hv_vendor_id=0xDEADBEEFFF \ -rtc clock=host,base=localtime \ -smp $CORES,sockets=1,cores=$(($CORES/2)),threads=2 \ -device vfio-pci,host=$IOMMU_GPU,multifunction=on,x-vga=on,romfile=$VBIOS \ -device vfio-pci,host=$IOMMU_GPU_AUDIO \ -device vfio-pci,host=$IOMMU_USB \ -device virtio-net-pci,netdev=net0 \ -netdev tap,id=net0,ifname=$TAP_INTERFACE,script=no,downscript=no,vhost=on \ -drive if=pflash,format=raw,readonly,file=$OVMF \ -device virtio-scsi-pci,id=scsi0 \ -device scsi-hd,bus=scsi0.0,drive=rootfs \ -drive id=rootfs,file=$IMG,media=disk,format=raw,if=none > /dev/null 2>&1 & ## Wait for QEMU to finish before continuing wait sleep 1 ## Stop the network tap_interface stop dhcp_server stop samba_server stop ## Unload the vfio module. This leaves the GPU without drivers. modprobe -r vfio-pci modprobe -r vfio_iommu_type1 modprobe -r vfio ## Reload the kernel modules that previously were unloaded. Load them inversed as you unloaded. The las unloaded should be the first to be loaded and viceversa. THIS MAY NOT BE THE CASE FOR YOU. This SHOULD load the drivers for the GPU. modprobe snd_hda_intel modprobe nvidia_drm modprobe nvidia_modeset modprobe nvidia ## Bind the USB. If you passed the USB controller to the VM. #echo $usbid > /sys/bus/pci/drivers/xhci_hcd/new_id ## This does not exist, so... #echo $usbbusid > /sys/bus/pci/devices/$usbbusid/driver/unbind echo $usbbusid > /sys/bus/pci/drivers/xhci_hcd/bind #echo $usbid > /sys/bus/pci/drivers/xhci_hcd/remove_id #ls -la /sys/bus/pci/devices/$usbbusid/ ## Rebind the EFI-Framebuffer and Rebind to virtual consoles ## [Source] [https://github.com/joeknock90/Single-GPU-Passthrough/blob/master/README.md#vm-stop-script] echo 1 > /sys/class/vtconsole/vtcon0/bind sleep 1 ## This just creates a file called `tee` #echo 1 > tee /sys/class/vtconsole/vtcon1/bind sleep 1 ## Reload the Display Manager to access X. You can also run startx, though I don't know how to do it and `startx` will probably not work. systemctl start lightdm sleep 2 ## Restore the Frame Buffer. ## I tried MANY different ways and timings of doing this, and this is the only one that works, FOR ME. echo efi-framebuffer.0 > /sys/bus/platform/drivers/efi-framebuffer/bind sleep 1 ## Restore ulimit to the previous state. ulimit -l $ULIMIT