add work host
This commit is contained in:
parent
0742e53e45
commit
d390530b35
6 changed files with 411 additions and 0 deletions
26
modules/_hardware/work-hardware.nix
Normal file
26
modules/_hardware/work-hardware.nix
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
{ config, lib, modulesPath, ... }: {
|
||||
imports = [ (modulesPath + "/installer/scan/not-detected.nix") ];
|
||||
|
||||
# AMD Ryzen 7 4000 series (Renoir)
|
||||
boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "nvme" "usbhid" "sd_mod" ];
|
||||
boot.kernelModules = [ "kvm-amd" "amdgpu" ];
|
||||
boot.extraModulePackages = [];
|
||||
|
||||
networking.useDHCP = lib.mkDefault true;
|
||||
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
|
||||
|
||||
hardware.cpu.amd.updateMicrocode =
|
||||
lib.mkDefault config.hardware.enableRedistributableFirmware;
|
||||
|
||||
# PRIME offload: AMD iGPU renders the display, NVIDIA dGPU handles offloaded workloads.
|
||||
# Fill in the Bus IDs before building the ISO.
|
||||
# To get them: boot any live Linux on the machine and run:
|
||||
# lspci -D | grep -E '(VGA|3D|Display)'
|
||||
# Then convert the PCI address (e.g. 0000:06:00.0) to the format "PCI:6:0:0".
|
||||
hardware.nvidia.prime = {
|
||||
offload.enable = true;
|
||||
offload.enableOffloadCmd = true;
|
||||
amdgpuBusId = "PCI:FILL:IN:ME"; # TODO: AMD Radeon Vega iGPU bus ID
|
||||
nvidiaBusId = "PCI:FILL:IN:ME"; # TODO: NVIDIA GTX 1650 dGPU bus ID
|
||||
};
|
||||
}
|
||||
|
|
@ -17,6 +17,11 @@ in {
|
|||
home-manager.module = inputs.home-manager.nixosModules.home-manager;
|
||||
};
|
||||
|
||||
den.hosts.x86_64-linux.work = {
|
||||
users.zoty = {};
|
||||
home-manager.module = inputs.home-manager.nixosModules.home-manager;
|
||||
};
|
||||
|
||||
den.aspects.notebook = {
|
||||
includes = [
|
||||
den.provides.hostname
|
||||
|
|
@ -33,6 +38,21 @@ in {
|
|||
};
|
||||
};
|
||||
|
||||
den.aspects.work = {
|
||||
includes = [
|
||||
den.provides.hostname
|
||||
den.aspects.core
|
||||
den.aspects.wayland
|
||||
den.aspects.niri
|
||||
den.aspects.work-specific
|
||||
den.aspects.dev
|
||||
den.aspects.secrets
|
||||
];
|
||||
nixos = {
|
||||
system.stateVersion = "25.11";
|
||||
};
|
||||
};
|
||||
|
||||
den.aspects.desktop = {
|
||||
includes = [
|
||||
den.provides.hostname
|
||||
|
|
@ -46,6 +66,7 @@ in {
|
|||
den.aspects.steam
|
||||
den.aspects.prismlauncher
|
||||
den.aspects.androidcam
|
||||
den.aspects.winvm
|
||||
];
|
||||
nixos = {
|
||||
system.stateVersion = "25.11";
|
||||
|
|
|
|||
|
|
@ -2,6 +2,11 @@
|
|||
den.aspects.desktop-specific.nixos = { config, pkgs, ... }: {
|
||||
imports = [ ./_hardware/desktop-hardware.nix ];
|
||||
|
||||
local.winvm = {
|
||||
gpuPcieAddress = "0000:01:00.0"; # NVIDIA GeForce RTX 3060 Ti
|
||||
gpuAudioPcieAddress = "0000:01:00.1"; # NVIDIA RTX 3060 Ti HDMI audio
|
||||
};
|
||||
|
||||
hardware = {
|
||||
graphics = {
|
||||
enable = true;
|
||||
|
|
|
|||
146
modules/winvm.nix
Normal file
146
modules/winvm.nix
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
{ den, ... }:
|
||||
{
|
||||
den.aspects.winvm.nixos = { config, pkgs, lib, ... }:
|
||||
let
|
||||
cfg = config.local.winvm;
|
||||
|
||||
hookScript = pkgs.writeShellScript "qemu-hook" ''
|
||||
#!/usr/bin/env bash
|
||||
# Libvirt QEMU hook for single-GPU passthrough.
|
||||
# Libvirt calls this script as: qemu <vm-name> <operation> <sub-operation>
|
||||
# On "prepare begin": unbind GPU from NVIDIA and hand it to the VM.
|
||||
# On "release end": return GPU to the host and restart the display session.
|
||||
|
||||
VM_NAME="$1"
|
||||
OPERATION="$2"
|
||||
|
||||
GPU_ADDR="${cfg.gpuPcieAddress}"
|
||||
GPU_AUDIO_ADDR="${cfg.gpuAudioPcieAddress}"
|
||||
|
||||
# Exit early for any VM that is not the configured passthrough target
|
||||
if [[ "$VM_NAME" != "${cfg.vmName}" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
bind_vfio() {
|
||||
# Stop greetd before terminating the session so it cannot restart tuigreet
|
||||
systemctl stop greetd.service
|
||||
|
||||
# Terminate the Niri/Wayland session and all user services (including Pipewire)
|
||||
loginctl terminate-user zoty 2>/dev/null || true
|
||||
sleep 3
|
||||
|
||||
# Detach virtual console framebuffer bindings held by the NVIDIA driver
|
||||
echo 0 > /sys/class/vtconsole/vtcon0/bind 2>/dev/null || true
|
||||
echo 0 > /sys/class/vtconsole/vtcon1/bind 2>/dev/null || true
|
||||
|
||||
# Detach the EFI framebuffer platform driver if it is present
|
||||
echo efi-framebuffer.0 \
|
||||
> /sys/bus/platform/drivers/efi-framebuffer/unbind 2>/dev/null || true
|
||||
|
||||
sleep 1
|
||||
|
||||
# Unload NVIDIA kernel modules in reverse dependency order
|
||||
modprobe -r nvidia_drm nvidia_modeset nvidia_uvm nvidia
|
||||
|
||||
# Bind GPU and its HDMI audio function to the vfio-pci driver
|
||||
echo "$GPU_ADDR" > /sys/bus/pci/drivers/vfio-pci/bind
|
||||
echo "$GPU_AUDIO_ADDR" > /sys/bus/pci/drivers/vfio-pci/bind
|
||||
}
|
||||
|
||||
unbind_vfio() {
|
||||
# Release both GPU PCI functions from vfio-pci
|
||||
echo "$GPU_ADDR" > /sys/bus/pci/drivers/vfio-pci/unbind
|
||||
echo "$GPU_AUDIO_ADDR" > /sys/bus/pci/drivers/vfio-pci/unbind
|
||||
|
||||
sleep 1
|
||||
|
||||
# Reload NVIDIA modules in dependency order
|
||||
modprobe nvidia
|
||||
modprobe nvidia_modeset
|
||||
modprobe nvidia_uvm
|
||||
modprobe nvidia_drm
|
||||
|
||||
# Restore the EFI framebuffer driver
|
||||
echo efi-framebuffer.0 \
|
||||
> /sys/bus/platform/drivers/efi-framebuffer/bind 2>/dev/null || true
|
||||
|
||||
# Restore virtual console framebuffer bindings
|
||||
echo 1 > /sys/class/vtconsole/vtcon0/bind 2>/dev/null || true
|
||||
echo 1 > /sys/class/vtconsole/vtcon1/bind 2>/dev/null || true
|
||||
|
||||
# Restart greetd so tuigreet is available for login
|
||||
systemctl start greetd.service
|
||||
}
|
||||
|
||||
case "$OPERATION" in
|
||||
prepare) bind_vfio ;;
|
||||
release) unbind_vfio ;;
|
||||
esac
|
||||
'';
|
||||
|
||||
screamService = {
|
||||
Unit = {
|
||||
Description = "Scream audio receiver for Windows VM";
|
||||
# Start after Pipewire is ready so the sink exists when scream connects
|
||||
After = [ "pipewire-pulse.service" ];
|
||||
Requires = [ "pipewire-pulse.service" ];
|
||||
};
|
||||
Service = {
|
||||
# virbr0 is the default libvirt NAT bridge; scream listens for UDP audio
|
||||
# streams from Windows and forwards them to the PulseAudio/Pipewire sink
|
||||
ExecStart = "${pkgs.scream}/bin/scream -i virbr0 -o pulse";
|
||||
Restart = "on-failure";
|
||||
};
|
||||
Install.WantedBy = [ "default.target" ];
|
||||
};
|
||||
in
|
||||
{
|
||||
options.local.winvm = {
|
||||
gpuPcieAddress = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "PCIe address of the NVIDIA GPU (e.g. 0000:01:00.0).";
|
||||
};
|
||||
gpuAudioPcieAddress = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "PCIe address of the NVIDIA GPU HDMI audio device (e.g. 0000:01:00.1).";
|
||||
};
|
||||
vmName = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "windows";
|
||||
description = "Libvirt VM name that triggers the GPU handoff hook.";
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
boot.kernelParams = [ "intel_iommu=on" "iommu=pt" ];
|
||||
|
||||
boot.kernelModules = [ "vfio" "vfio_pci" "vfio_iommu_type1" ];
|
||||
|
||||
virtualisation.libvirtd = {
|
||||
enable = true;
|
||||
qemu = {
|
||||
package = pkgs.qemu_kvm;
|
||||
runAsRoot = true;
|
||||
swtpm.enable = true;
|
||||
};
|
||||
};
|
||||
|
||||
programs.virt-manager.enable = true;
|
||||
|
||||
environment.systemPackages = [ pkgs.virtio-win ];
|
||||
|
||||
users.users.zoty.extraGroups = [ "libvirtd" "kvm" ];
|
||||
|
||||
environment.etc."libvirt/hooks/qemu" = {
|
||||
source = hookScript;
|
||||
mode = "0755";
|
||||
};
|
||||
|
||||
# Scream receiver runs as a user service so it has access to the
|
||||
# Pipewire session. It starts automatically after login and waits
|
||||
# for audio packets from the Windows VM on the libvirt bridge.
|
||||
home-manager.users.zoty.systemd.user.services.scream = screamService;
|
||||
};
|
||||
};
|
||||
}
|
||||
68
modules/work.nix
Normal file
68
modules/work.nix
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
{ den, inputs, lib, config, ... }: {
|
||||
den.aspects.work-specific.nixos = { pkgs, config, ... }: {
|
||||
imports = [
|
||||
./_hardware/work-hardware.nix
|
||||
./_hardware/notebook-disko.nix
|
||||
inputs.disko.nixosModules.disko
|
||||
];
|
||||
|
||||
hardware.graphics = {
|
||||
enable = true;
|
||||
enable32Bit = true;
|
||||
};
|
||||
|
||||
hardware.nvidia = {
|
||||
modesetting.enable = true;
|
||||
open = false;
|
||||
nvidiaSettings = true;
|
||||
# Fine-grained power management is required for PRIME offload mode.
|
||||
powerManagement.enable = true;
|
||||
powerManagement.finegrained = true;
|
||||
package = config.boot.kernelPackages.nvidiaPackages.stable;
|
||||
};
|
||||
|
||||
# amdgpu drives the display; nvidia is available for offloaded rendering.
|
||||
services.xserver.videoDrivers = [ "amdgpu" "nvidia" ];
|
||||
|
||||
environment.sessionVariables = {
|
||||
GBM_BACKEND = "nvidia-drm";
|
||||
__GLX_VENDOR_LIBRARY_NAME = "nvidia";
|
||||
};
|
||||
|
||||
security.sudo.wheelNeedsPassword = lib.mkForce true;
|
||||
|
||||
networking.firewall = {
|
||||
enable = true;
|
||||
allowedTCPPorts = [];
|
||||
allowedUDPPorts = [];
|
||||
};
|
||||
|
||||
services.thermald.enable = true;
|
||||
powerManagement.enable = true;
|
||||
|
||||
services.tlp = {
|
||||
enable = true;
|
||||
settings = {
|
||||
CPU_SCALING_GOVERNOR_ON_AC = "performance";
|
||||
CPU_SCALING_GOVERNOR_ON_BAT = "powersave";
|
||||
};
|
||||
};
|
||||
|
||||
boot = {
|
||||
initrd.kernelModules = [ "dm-crypt" "amdgpu" ];
|
||||
initrd.compressor = "zstd";
|
||||
kernelParams = [ "nvidia.NVreg_PreserveVideoMemoryAllocations=1" ];
|
||||
|
||||
loader = {
|
||||
grub = {
|
||||
enable = true;
|
||||
device = "nodev";
|
||||
efiSupport = true;
|
||||
configurationLimit = 2;
|
||||
enableCryptodisk = true;
|
||||
};
|
||||
efi.canTouchEfiVariables = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue