Add USB device passthrough design document

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Davíð Steinn Geirsson 2026-03-17 11:59:06 +00:00
parent 0f52236f80
commit a5582f4b29

View file

@ -0,0 +1,119 @@
# USB Device Passthrough Design
## Overview
USB device passthrough for vmsilo VMs via crosvm's USB hotplug interface. Supports both persistent attachment (declared in VM config, attached at VM start) and runtime attachment via `vmsilo-usb` CLI.
## Identifiers
- **Persistent config:** VID:PID (required) + optional serial number
- **Runtime CLI:** VID:PID or devpath (physical USB port path like `1-3`)
When VID:PID matches multiple physical devices, all are attached (unless narrowed by serial).
## NixOS Options
New per-VM option in `programs.vmsilo.nixosVms.<vm>`:
```nix
usbDevices = [
{ vendorId = "17ef"; productId = "60e0"; }
{ vendorId = "046d"; productId = "c52b"; serial = "A02019100900"; }
];
```
A NixOS assertion ensures no two VMs declare the same VID:PID+serial combination.
## State File
`/run/vmsilo/usb-state.json` — single global file tracking all USB attachments:
```json
{
"1-3": {
"vid": "17ef",
"pid": "60e0",
"serial": null,
"busnum": 1,
"devnum": 2,
"vm": "banking",
"port": 1
}
}
```
Keyed by devpath. All operations acquire an exclusive `flock` on `/run/vmsilo/usb-state.lock` for the full read-modify-crosvm call-write cycle.
Ephemeral (lives in `/run`), matching the VM lifecycle.
## Runtime CLI — `vmsilo-usb`
### `vmsilo-usb` (no args) — list devices
Displays all host USB devices with attachment status:
```
VID:PID Port Serial Manufacturer Product VM
17ef:60ee 1-3 - Lenovo TrackPoint Keyboard II -
05e3:0610 1-6 - - USB2.0 Hub -
26ce:01a2 9-1 A02019100900 ASRock LED Controller untrusted
```
Reads sysfs for device info, merges with state file for VM assignments. No root required.
### `vmsilo-usb attach <vm> <vid:pid|devpath>`
1. Resolve identifier to physical device(s) via sysfs
2. Acquire lock, read state file
3. For each matching device: if attached to another VM, detach from old VM first (`crosvm usb detach`)
4. Call `crosvm usb attach` on target VM
5. Update state file, release lock
Runs via polkit — invokes a systemd oneshot template that runs as root.
### `vmsilo-usb detach <vm> <vid:pid|devpath>`
1. Acquire lock, read state file
2. Find matching entries for that VM
3. Call `crosvm usb detach`
4. Remove entries from state file, release lock
Same polkit mechanism as attach.
## Persistent Attachment
### On VM Start
`vmsilo-<vm>-usb-attach.service` — a oneshot unit with `After=` and `Requires=` on the VM service:
1. Wait for the crosvm control socket to appear
2. For each configured `usbDevices` entry: scan sysfs for matching VID:PID (+ serial if specified)
3. Attach all matches via `crosvm usb attach`, update state file under lock
4. If a device isn't plugged in, log a warning and continue
### On VM Stop
ExecStopPost in the VM service removes that VM's entries from the state file under lock. No `crosvm usb detach` needed since the VM is shutting down.
## Permission Model
- `vmsilo-usb attach/detach` use polkit, same as `vm-start`/`vm-stop`
- Implemented as systemd oneshot template services authorized via existing vmsilo polkit rules
- `crosvm usb attach` runs as root (needs USB device fd + control socket access)
- `vmsilo-usb` (listing) needs no privileges — reads sysfs and the state file
## crosvm Interface
- `crosvm usb attach BUS_ID:ADDR:BUS_NUM:DEV_NUM DEV_PATH SOCKET_PATH` — opens device fd, passes to VM over control socket
- `crosvm usb detach PORT SOCKET_PATH` — detaches by crosvm port number
- `crosvm usb list SOCKET_PATH` — returns `{port, vendor_id, product_id}` per device
- Control socket at `/run/vmsilo/<vm>-crosvm-control.socket`
- USB enabled by default in crosvm (no extra flags needed)
## Files Modified
- `modules/options.nix``usbDevices` option
- `modules/assertions.nix` — duplicate VID:PID+serial assertion
- `modules/scripts.nix``vmsilo-usb` script, USB helper functions (sysfs enumeration, state file with flock, resolution logic)
- `modules/services.nix``vmsilo-<vm>-usb-attach.service`, ExecStopPost state cleanup, polkit oneshot templates for runtime
- `modules/package.nix` — symlink `vmsilo-usb` into `$out/bin/`