Skip to content

Commit 46be373

Browse files
authored
Merge pull request #60 from sid115/feature/vfio
feature/vfio: Expand virtualisation NixOS module with options for VFIO
2 parents b4f9f4f + 08e2dce commit 46be373

File tree

13 files changed

+413
-74
lines changed

13 files changed

+413
-74
lines changed
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
# Virtualization
1+
# Virtualisation
22

3-
Home Manager module to go with the [Virtualization NixOS module](../nixos/virtualization.md).
3+
Home Manager module to go with the [Virtualisation NixOS module](../nixos/virtualisation.md).
44

5-
View the [*nix-core* Home Manager module on GitHub](https://github.com/sid115/nix-core/tree/master/modules/home/virtualization).
5+
View the [*nix-core* Home Manager module on GitHub](https://github.com/sid115/nix-core/tree/master/modules/home/virtualisation).
66

77
## Setup
88

9-
1. Import this module in your Home Manager configuration and the corresponding [NixOS module](../nixos/virtualization.md) in your NixOS configuration.
9+
1. Import this module in your Home Manager configuration and the corresponding [NixOS module](../nixos/virtualisation.md) in your NixOS configuration.
1010
1. Rebuild and reboot: `rebuild all && sudo reboot now`
1111
1. Start the default network: `virsh net-autostart default`
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
# Virtualisation
2+
3+
Virtualisation using QEMU via libvirt and managed through Virt-manager with VFIO support.
4+
5+
View the [*nix-core* NixOS module on GitHub](https://github.com/sid115/nix-core/tree/master/modules/nixos/virtualisation).
6+
7+
## Overview
8+
9+
1. **QEMU** is the hypervisor that provides the core virtualisation capabilities.
10+
1. **libvirt** is a toolkit and API that manages virtualisation platforms, such as QEMU.
11+
1. **Virt-manager** is a GUI tool that interacts with libvirt to manage VMs.
12+
1. **virsh** is a CLI tool that interacts with libvirt to manage VMs.
13+
14+
## Docs
15+
16+
### QEMU
17+
18+
- [Official docs](https://www.qemu.org/docs/master/)
19+
20+
### libvirt
21+
22+
- [Official docs](https://libvirt.org/docs.html)
23+
- [Arch Wiki](https://wiki.archlinux.org/title/Libvirt)
24+
- [virsh CLI](https://www.libvirt.org/manpages/virsh.html)
25+
26+
> If you are using the [Home Manager module](../home/virtualisation.md) as well, then `virsh` is aliased to `virsh --connect qemu:///system`
27+
28+
### Virt-manager
29+
30+
- [GitHub Repository](https://github.com/virt-manager/virt-manager)
31+
- [NixOS Official Wiki](https://wiki.nixos.org/wiki/Virt-manager)
32+
- [NixOS Community Wiki](https://nixos.wiki/wiki/Virt-manager)
33+
- [Arch Wiki](https://wiki.archlinux.org/title/Virt-manager)
34+
35+
## Setup
36+
37+
1. Import this module in your NixOS config. It is recommended to use the [Virtualisation Manager module](../home/virtualisation.md) as well.
38+
1. Add your user to the `libvirtd` and `qemu-libvirtd` group:
39+
```nix
40+
users.extraGroups.libvirtd.members = [ "<you>" ];
41+
users.extraGroups.qemu-libvirtd.members = [ "<you>" ];
42+
```
43+
1. Rebuild and reboot: `rebuild all && sudo reboot now`
44+
1. Enable and start the default network and reboot again: `virsh net-autostart default && virsh net-start default`
45+
46+
## VFIO
47+
48+
### Setup
49+
50+
For successful PCI device passthrough, devices must be properly isolated by IOMMU groups. A device can be safely passed through if:
51+
- It is the **only device** in its IOMMU group (recommended), OR
52+
- **All devices** in its IOMMU group are passed through together
53+
54+
This module includes an `iommu-groups` command to help identify IOMMU groups:
55+
56+
```bash
57+
iommu-groups
58+
```
59+
60+
In this example, IOMMU group 9 contains only the Nvidia GPU which will get passed to the VM:
61+
62+
```
63+
IOMMU Group 9 01:00.0 3D controller [0302]: NVIDIA Corporation TU117M [GeForce GTX 1650 Mobile / Max-Q] [10de:1f9d] (rev a1)
64+
```
65+
66+
Take not of the PCI device ID. In this case: `10de:1f9d`.
67+
68+
### Config
69+
70+
This is an example with the Nvidia GPU above:
71+
72+
```nix
73+
{ inputs, ... }:
74+
75+
{
76+
imports = [ inputs.core.nixosModules.virtualisation ];
77+
78+
virtualisation = {
79+
vfio = {
80+
enable = true;
81+
IOMMUType = "amd";
82+
devices = [
83+
"10de:1f9d"
84+
];
85+
blacklistNvidia = true;
86+
};
87+
hugepages.enable = true;
88+
};
89+
}
90+
```
91+
92+
### Virt Manager
93+
94+
#### 1. Open VM Hardware Settings
95+
96+
- Select your VM in Virt Manager
97+
- Click *"Show virtual hardware details"*
98+
99+
#### 2. Add PCI Host Device
100+
101+
- Click *"Add Hardware"* button at bottom
102+
- Select *"PCI Host Device"* from the list
103+
- Click *"Finish"*
104+
105+
You may repeat this process for as many devices as you want to add to your VM.
106+
107+
### Looking Glass
108+
109+
TODO
110+
111+
### Troubleshooting
112+
113+
#### Check Kernel Parameters
114+
115+
View current kernel parameters:
116+
117+
```bash
118+
cat /proc/cmdline
119+
```
120+
121+
Check VFIO-related parameters:
122+
123+
```bash
124+
dmesg | grep -i vfio
125+
```
126+
127+
Verify IOMMU is enabled:
128+
129+
```bash
130+
dmesg | grep -i iommu
131+
```
132+
133+
#### Verify device binding
134+
135+
```bash
136+
lscpi -k
137+
```
138+
139+
Look for your device you want to pass through. It should say:
140+
141+
```
142+
Kernel driver in use: vfio-pci
143+
```
144+
145+
For example:
146+
147+
```
148+
01:00.0 3D controller: NVIDIA Corporation TU117M [GeForce GTX 1650 Mobile / Max-Q] (rev a1)
149+
Subsystem: Lenovo Device 380d
150+
Kernel driver in use: vfio-pci
151+
Kernel modules: nvidiafb, nouveau
152+
```
153+
154+
#### Verify module status
155+
156+
Ensure blacklisted modules are not loaded:
157+
158+
```bash
159+
lsmod | grep nvidia
160+
lsmod | grep nouveau
161+
```
162+
163+
These should return nothing.
164+
165+
#### `vfio-pci.ids` not appearing
166+
167+
Check generated bootloader config:
168+
169+
```bash
170+
cat /boot/loader/entries/nixos-*.conf
171+
```

docs/modules/nixos/virtualization.md

Lines changed: 0 additions & 40 deletions
This file was deleted.

mkdocs.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ nav:
6767
- torrenting: modules/nixos/torrenting.md
6868
- tt-rss: modules/nixos/tt-rss.md
6969
- vaultwarden: modules/nixos/vaultwarden.md
70-
- virtualization: modules/nixos/virtualization.md
70+
- vfio: modules/nixos/vfio.md
7171
- webPage: modules/nixos/webpage.md
7272
- xrdp: modules/nixos/xrdp.md
7373
- Home Manager:
@@ -83,7 +83,7 @@ nav:
8383
- password-manager: modules/home/password-manager.md
8484
- sops: modules/home/sops.md
8585
- stylix: modules/home/stylix.md
86-
- virtualization: modules/home/virtualization.md
86+
- vfio: modules/home/vfio.md
8787
- waybar: modules/home/waybar.md
8888
- yazi: modules/home/yazi.md
8989
- Tips:

modules/home/default.nix

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@
1212
passwordManager = import ./password-manager;
1313
sops = import ./sops;
1414
stylix = import ./stylix;
15-
virtualization = import ./virtualization;
15+
virtualisation = import ./virtualisation;
1616
waybar = import ./waybar;
1717
}
File renamed without changes.

modules/nixos/default.nix

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
torrenting = import ./torrenting;
4040
tt-rss = import ./tt-rss;
4141
vaultwarden = import ./vaultwarden;
42-
virtualization = import ./virtualization;
42+
virtualisation = import ./virtualisation;
4343
webPage = import ./webPage;
4444
xrdp = import ./xrdp;
4545
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
{
2+
config,
3+
lib,
4+
pkgs,
5+
...
6+
}:
7+
8+
let
9+
cfg = config.virtualisation;
10+
11+
boolToZeroOne = x: if x then "1" else "0";
12+
13+
aclString = strings.concatMapStringsSep ''
14+
,
15+
'' strings.escapeNixString cfg.libvirtd.deviceACL;
16+
17+
inherit (lib)
18+
mkDefault
19+
mkOption
20+
optionals
21+
strings
22+
types
23+
;
24+
in
25+
{
26+
imports = [
27+
./hugepages.nix
28+
./vfio.nix
29+
];
30+
31+
options.virtualisation = {
32+
libvirtd = {
33+
deviceACL = mkOption {
34+
type = types.listOf types.str;
35+
default = [ ];
36+
example = [
37+
"/dev/kvm"
38+
"/dev/net/tun"
39+
"/dev/vfio/vfio"
40+
];
41+
description = "List of device paths that QEMU processes are allowed to access.";
42+
};
43+
44+
clearEmulationCapabilities = mkOption {
45+
type = types.bool;
46+
default = true;
47+
description = "Whether to remove privileged Linux capabilities from QEMU processes after they start.";
48+
};
49+
};
50+
};
51+
52+
config = {
53+
virtualisation = {
54+
libvirtd = {
55+
enable = mkDefault true;
56+
onBoot = mkDefault "ignore";
57+
onShutdown = mkDefault "shutdown";
58+
qemu.ovmf.enable = mkDefault true;
59+
qemu.runAsRoot = mkDefault false;
60+
qemu.verbatimConfig = ''
61+
clear_emulation_capabilities = ${boolToZeroOne cfg.libvirtd.clearEmulationCapabilities}
62+
cgroup_device_acl = [
63+
${aclString}
64+
]
65+
'';
66+
};
67+
spiceUSBRedirection.enable = mkDefault true;
68+
};
69+
70+
users.users."qemu-libvirtd" = {
71+
extraGroups = optionals (!cfg.libvirtd.qemu.runAsRoot) [
72+
"kvm"
73+
"input"
74+
];
75+
isSystemUser = true;
76+
};
77+
78+
programs.virt-manager.enable = mkDefault true;
79+
80+
environment.systemPackages = [
81+
(pkgs.writeShellScriptBin "iommu-groups" (builtins.readFile ./iommu-groups.sh))
82+
pkgs.dnsmasq
83+
pkgs.qemu_full
84+
];
85+
};
86+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
{
2+
config,
3+
lib,
4+
...
5+
}:
6+
7+
let
8+
cfg = config.virtualisation.hugepages;
9+
10+
inherit (lib)
11+
mkEnableOption
12+
mkOption
13+
optionals
14+
types
15+
;
16+
in
17+
{
18+
options.virtualisation.hugepages = {
19+
enable = mkEnableOption "Whether to enable huge pages.";
20+
21+
defaultPageSize = mkOption {
22+
type = types.strMatching "[0-9]*[kKmMgG]";
23+
default = "1M";
24+
description = "Default size of huge pages. You can use suffixes K, M, and G to specify KB, MB, and GB.";
25+
};
26+
27+
pageSize = mkOption {
28+
type = types.strMatching "[0-9]*[kKmMgG]";
29+
default = "1M";
30+
description = "Size of huge pages that are allocated at boot. You can use suffixes K, M, and G to specify KB, MB, and GB.";
31+
};
32+
33+
numPages = mkOption {
34+
type = types.ints.positive;
35+
default = 1;
36+
description = "Number of huge pages to allocate at boot.";
37+
};
38+
};
39+
40+
config.boot.kernelParams = optionals cfg.enable [
41+
"default_hugepagesz=${cfg.defaultPageSize}"
42+
"hugepagesz=${cfg.pageSize}"
43+
"hugepages=${toString cfg.numPages}"
44+
];
45+
}

0 commit comments

Comments
 (0)