{ config, lib, pkgs, ... }: let cfg = config.ptw.virtualisation.gaming; in { options.ptw.virtualisation.gaming = { enable = lib.mkEnableOption "Configure virtualisation for gaming purposes"; }; config = lib.mkIf cfg.enable { environment.etc = { "evdev-proxy/config.toml".source = pkgs.writeText "config.toml" '' log_level = "INFO" [[device]] [device.Simple] name = "EvdevProxyMouse" vendor = 0x1337 model = 0x1337 class = "Mouse" [[device.Simple.selector]] USBIDClass = {vendor=0x046d, model=0xc531, class="Mouse"} [[device.Simple.selector]] USBIDClass = {vendor=0x046d, model=0xc07c, class="Mouse"} [[device]] [device.Simple] name = "EvdevProxyTartarus" vendor = 0x1337 model = 0x1338 class = "Keyboard" # This is to give a deterministic /dev/input/by-id/ path to # the mapped version of the Tartarus evdev node. # (Useful for qemu) [[device.Simple.selector]] EVDEVClass = {phys="\"key-mapper\""} ''; "libvirt/hooks/qemu".source = let vfio-isolate-state = "/tmp/vfio-isolate-state"; in pkgs.writeScript "qemu" '' #!${pkgs.stdenv.shell} guest=$1 action=$2 phase=$3 extra=$4 if [[ "$guest" = "win10" ]]; then case "$action" in prepare) # Only do this while in preparation [[ ! "$phase" = "prepare" ]] && exit 0 sudo -u alexander systemctl --user start evdev-proxy.service sudo -u alexander systemctl --user start scream.service sleep 2 ${pkgs.vfio-isolate}/bin/vfio-isolate \ -u ${vfio-isolate-state} \ cpu-governor performance "$GUEST_CORES" \ cpuset-create --cpus "$GUEST_CORES" /guest.slice \ cpuset-create --cpus C0,4 /host.slice \ move-tasks / /host.slice \ irq-affinity mask "$GUEST_CORES" ;; stopped) # Only run when the VM is fully stopped [[ ! "$phase" = "end" ]] && exit 0 sudo -u alexander systemctl --user stop evdev-proxy.service sudo -u alexander systemctl --user stop scream.service ${pkgs.vfio-isolate}/bin/vfio-isolate \ restore ${vfio-isolate-state} esac fi ''; }; # NOTE: Workaround for libvirt's SYSCONFDIR being set to /var/lib # (See https://github.com/NixOS/nixpkgs/issues/51152#issuecomment-899374407) system.activationScripts.libvirt-hooks.text = '' ln -Tfs /etc/libvirt/hooks /var/lib/libvirt/hooks ''; services.udev.packages = with pkgs; [ evdev-proxy ]; systemd = { services = { libvirtd.path = with pkgs; [ vfio-isolate systemd bash ]; virtiofsd = { description = "vhost-user virtio-fs device backend written in Rust"; wantedBy = [ "default.target" "libvirtd.service" ]; serviceConfig = { Type = "simple"; ExecStart = "${pkgs.virtiofsd}/bin/virtiofsd --socket-path=/tmp/vfsd.sock --shared-dir /mnt/Storage/Games --announce-submounts --inode-file-handles=mandatory"; Restart = "always"; }; }; }; user.services.evdev-proxy = { description = "Creates virtual device to proxy evdev devices events"; wantedBy = [ "default.target" ]; serviceConfig = { Type = "simple"; ExecStart = "${pkgs.evdev-proxy}/bin/evdev-proxy"; Restart = "always"; }; }; }; }; }