From ce79bcd03fc3028e78eda2de779293d3b7c28d72 Mon Sep 17 00:00:00 2001 From: OlivierChiasson Date: Thu, 11 Jun 2026 22:18:01 -0300 Subject: [PATCH] Add NixVirt support and GPU passthrough configuration --- flake.lock | 20 + flake.nix | 5 + modules/desktop/niri/default.nix | 19 +- modules/hosts/14900k/_private/displays.nix | 126 ++++-- .../hosts/14900k/_private/gpu-passthru.nix | 71 ++++ modules/hosts/14900k/_private/nvidia.nix | 4 +- .../14900k/_private/win11-domain-install.xml | 91 +++++ .../hosts/14900k/_private/win11-domain.xml | 177 ++++++++ modules/hosts/14900k/configuration.nix | 2 + modules/system/default.nix | 1 + modules/system/gpu-passthru.nix | 383 ++++++++++++++++++ 11 files changed, 852 insertions(+), 47 deletions(-) create mode 100644 modules/hosts/14900k/_private/gpu-passthru.nix create mode 100644 modules/hosts/14900k/_private/win11-domain-install.xml create mode 100644 modules/hosts/14900k/_private/win11-domain.xml create mode 100644 modules/system/gpu-passthru.nix diff --git a/flake.lock b/flake.lock index e790f68..72a0f31 100644 --- a/flake.lock +++ b/flake.lock @@ -706,6 +706,25 @@ "type": "github" } }, + "nixvirt": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1748140003, + "narHash": "sha256-DNBZmuk1YRM2PmwbHzVdXumRjCUzQkMarg4iI/37rOQ=", + "rev": "5dfe108fd859b122f9a96981cb6bc12297653d6c", + "revCount": 407, + "type": "tarball", + "url": "https://api.flakehub.com/f/pinned/AshleyYakeley/NixVirt/0.6.0/0197059a-e45f-7446-86b5-411ccc894ab0/source.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://flakehub.com/f/AshleyYakeley/NixVirt/%2A.tar.gz" + } + }, "nur": { "inputs": { "flake-parts": "flake-parts_5", @@ -809,6 +828,7 @@ "nixcord": "nixcord", "nixos-raspberrypi": "nixos-raspberrypi", "nixpkgs": "nixpkgs_2", + "nixvirt": "nixvirt", "nur": "nur", "oom-hardware": "oom-hardware", "personal-website": "personal-website", diff --git a/flake.nix b/flake.nix index c396f2d..469de9c 100644 --- a/flake.nix +++ b/flake.nix @@ -105,6 +105,11 @@ }; navi.url = "github:cafkafk/navi"; + + nixvirt = { + url = "https://flakehub.com/f/AshleyYakeley/NixVirt/*.tar.gz"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; outputs = inputs: diff --git a/modules/desktop/niri/default.nix b/modules/desktop/niri/default.nix index 4b3a494..4324e57 100644 --- a/modules/desktop/niri/default.nix +++ b/modules/desktop/niri/default.nix @@ -139,9 +139,17 @@ let keyringExtra = lib.optionalString keyringEnable keyringNiriStartupKdl; extraConfigMerged = keyringExtra + rpi5Extra + (userExtra.extraConfig or ""); windowRules = (base.window-rules or [ ]) ++ (userExtra.window-rules or [ ]); + userExtraNoSpecial = lib.removeAttrs userExtra [ "window-rules" "extraConfig" "binds" ]; + mergedBinds = + (base.binds or { }) + // (userExtra.binds or { }) + // (niriCfg.extraBinds or { }); in lib.recursiveUpdate base ( - lib.removeAttrs userExtra [ "window-rules" "extraConfig" ] + userExtraNoSpecial + // { + binds = mergedBinds; + } // lib.optionalAttrs (windowRules != [ ]) { window-rules = windowRules; } @@ -195,6 +203,15 @@ in Merged into shared defaults → `config.kdl` via wrapper-modules. Put raw KDL in `extraConfig`. ''; }; + + extraBinds = lib.mkOption { + type = lib.types.attrsOf lib.types.anything; + default = { }; + description = '' + Keybind overrides merged with shared defaults and `extraSettings.binds`. + Use this when multiple modules need to add binds without overwriting each other. + ''; + }; }; config = lib.mkMerge [ diff --git a/modules/hosts/14900k/_private/displays.nix b/modules/hosts/14900k/_private/displays.nix index f6b6f75..22529cc 100644 --- a/modules/hosts/14900k/_private/displays.nix +++ b/modules/hosts/14900k/_private/displays.nix @@ -1,61 +1,99 @@ -# Monitor layout for 14900k (ported from NixOS-New `hosts/clients/14900k/home.nix`). -# Niri: `chiasson.desktop.niri.extraSettings` (`extraConfig` KDL + `binds` merged with defaults). +# Monitor layout for 14900k. +# NVIDIA (default): DP-2 ultrawide, HDMI-A-3 + DP-4 side/top stack. +# Intel iGPU (gpu passthru): DP-1 ultrawide, HDMI-A-2 Samsung to the left. +# Niri: `extraSettings` (KDL via `extraConfig`, other wrapper-modules keys) + `extraBinds` (merged keybinds). # Hyprland: `chiasson.desktop.hyprland.settings` (merged in HM when `chiasson.desktop.hyprland.enable`). #TODO[epic=Moderate] Clean this up, move to host's configuration.nix. { config, lib, ... }: -{ - chiasson.desktop.niri.extraSettings = { - extraConfig = '' - output "DP-2" { - mode "2560x1080@144" - scale 1.0 - position x=0 y=0 - focus-at-startup - } - output "HDMI-A-3" { - mode "1920x1080@60" - scale 1.0 - position x=-1920 y=0 - } - output "DP-4" { - mode "1920x1080@144" - scale 1.0 - position x=0 y=-1080 - } +let + gpuPassthru = config.chiasson.system.gpuPassthru.enable; - ''; + niriOutputs = + if gpuPassthru then + '' + output "DP-1" { + mode "2560x1080@144" + scale 1.0 + position x=0 y=0 + focus-at-startup + } + output "HDMI-A-2" { + mode "1920x1080@60" + scale 1.0 + position x=-1920 y=0 + } + '' + else + '' + output "DP-3" { + mode "2560x1080@144" + scale 1.0 + position x=0 y=0 + focus-at-startup + } + output "HDMI-A-3" { + mode "1920x1080@60" + scale 1.0 + position x=-1920 y=0 + } + output "DP-4" { + mode "1920x1080@144" + scale 1.0 + position x=0 y=-1080 + } + ''; - binds."XF86Tools".spawn = [ - "wpctl" - "set-mute" - "@DEFAULT_AUDIO_SOURCE@" - "toggle" - ]; - }; - - chiasson.desktop.hyprland.settings = lib.mkIf config.chiasson.desktop.hyprland.enable ( - let - monitorList = [ - "DP-2, 2560x1080@144, 0x0, 1" + hyprlandMonitors = + if gpuPassthru then + [ + "DP-1, 2560x1080@144, 0x0, 1" + "HDMI-A-2, 1920x1080@60, -1920x0, 1" + ] + else + [ + "DP-3, 2560x1080@144, 0x0, 1" "DP-4, 1920x1080@144, 0x-1080, 1" "HDMI-A-3, 1920x1080@60, -1920x0, 1" ]; - workspaceList = [ + + hyprlandWorkspaces = + if gpuPassthru then + [ + "1, monitor:DP-1, default:true" + "2, monitor:DP-1" + "3, monitor:DP-1" + "4, monitor:HDMI-A-2, default:true" + "5, monitor:HDMI-A-2" + "6, monitor:HDMI-A-2" + ] + else + [ "1, monitor:DP-3, default:true" "2, monitor:DP-3" "3, monitor:DP-3" - "4, monitor:Unknown-2, default:true" - "5, monitor:Unknown-2" - "6, monitor:Unknown-2" + "4, monitor:HDMI-A-3, default:true" + "5, monitor:HDMI-A-3" + "6, monitor:HDMI-A-3" "7, monitor:DP-4" "8, monitor:DP-4" "9, monitor:DP-4" ]; - in - { - monitor = lib.mkBefore monitorList; - workspace = workspaceList; - } - ); +in +{ + chiasson.desktop.niri.extraSettings = { + extraConfig = niriOutputs; + }; + + chiasson.desktop.niri.extraBinds."XF86Tools".spawn = [ + "wpctl" + "set-mute" + "@DEFAULT_AUDIO_SOURCE@" + "toggle" + ]; + + chiasson.desktop.hyprland.settings = lib.mkIf config.chiasson.desktop.hyprland.enable { + monitor = lib.mkBefore hyprlandMonitors; + workspace = hyprlandWorkspaces; + }; } diff --git a/modules/hosts/14900k/_private/gpu-passthru.nix b/modules/hosts/14900k/_private/gpu-passthru.nix new file mode 100644 index 0000000..d567af4 --- /dev/null +++ b/modules/hosts/14900k/_private/gpu-passthru.nix @@ -0,0 +1,71 @@ +# 14900k: RTX 2070 VFIO passthrough + Windows 11 gaming VM on Crucial MX500. +{ config, lib, ... }: +let + cfg = config.chiasson.system.gpuPassthru; +in +{ + chiasson.system.gpuPassthru = { + enable = false; + pciIds = [ + "10de:1f07" # RTX 2070 VGA + "10de:10f9" # HDMI audio + "10de:1ada" # USB 3.1 + "10de:1adb" # USB-C UCSI + "14c3:7927" # MediaTek MT7927 WiFi (M.2, no Linux driver — Windows only) + ]; + windowsDisk = "/dev/disk/by-id/ata-CT1000MX500SSD1_1830E14B054A"; + lookingGlass = { + enable = true; + shmSizeMiB = 32; + captureOnFocus = false; + }; + vm = { + enable = true; + name = "win11"; + memoryGiB = 16; + vcpus = 8; + installMode = false; + }; + libvirtUsers = [ "olivier" ]; + }; + + chiasson.desktop.niri.extraBinds = lib.mkIf (cfg.enable && cfg.lookingGlass.enable) { + "Mod+J"."spawn-sh" = "looking-glass-toggle"; + }; + + # Applied last in configuration.nix imports — wins over system module defaults. + virtualisation.libvirtd.qemu.verbatimConfig = lib.mkIf (cfg.lookingGlass.enable) ( + lib.mkForce '' + namespaces = [] + seccomp_sandbox = 0 + security_driver = "none" + security_default_confined = 0 + security_require_confined = 0 + cgroup_device_acl = [ + "/dev/null", "/dev/full", "/dev/zero", + "/dev/random", "/dev/urandom", + "/dev/ptmx", "/dev/kvm", "/dev/kqemu", + "/dev/rtc", "/dev/hpet", + "/dev/vfio/", "/dev/vfio/vfio", + "/dev/kvmfr0", "c 234:* rwm", + "/dev/disk/", "/dev/disk/by-id/", + "/dev/bus/usb/", + ] + '' + ); + + virtualisation.libvirt = lib.mkIf (cfg.enable && cfg.vm.enable) { + enable = true; + swtpm.enable = true; + connections."qemu:///system".domains = [ + { + definition = + if cfg.vm.installMode then + ./win11-domain-install.xml + else + ./win11-domain.xml; + active = false; + } + ]; + }; +} diff --git a/modules/hosts/14900k/_private/nvidia.nix b/modules/hosts/14900k/_private/nvidia.nix index 9a83700..f0819fc 100644 --- a/modules/hosts/14900k/_private/nvidia.nix +++ b/modules/hosts/14900k/_private/nvidia.nix @@ -1,6 +1,6 @@ -# NVIDIA for host desktop. +# NVIDIA for host desktop (disabled when GPU passthrough binds the card to VFIO). { config, lib, pkgs, ... }: -{ +lib.mkIf (!config.chiasson.system.gpuPassthru.enable) { boot.kernelParams = [ "snd_hda_core.gpu_bind=0" ]; boot.kernelPackages = lib.mkDefault pkgs.linuxPackages_latest; diff --git a/modules/hosts/14900k/_private/win11-domain-install.xml b/modules/hosts/14900k/_private/win11-domain-install.xml new file mode 100644 index 0000000..db1652a --- /dev/null +++ b/modules/hosts/14900k/_private/win11-domain-install.xml @@ -0,0 +1,91 @@ + + + win11 + a7b3c8d1-4e2f-5a6b-9c0d-1e2f3a4b5c6d + 16 + 16 + 8 + + hvm + /run/libvirt/nix-ovmf/edk2-x86_64-secure-code.fd + /var/lib/libvirt/qemu/nvram/win11_VARS.fd + + + + + + + + + + + + + + + destroy + restart + preserve + + /run/libvirt/nix-emulators/qemu-system-x86_64 + + + + + +
+ + + + + + + +
+ + + +
+ + + + + +
+ + +
+ + + + + +
+ + + + /dev/urandom +
+ + + + + + + + + + + + +