FreeBSD / bhyve / PCI passthrough
Handing a physical PCI function to a guest and taking it back are the two moments where an unreset or half-configured device can corrupt the host. bhyve's ppt driver and the generic PCI layer already reset a device around assignment, but the reset is only as safe as the state it leaves behind. This page proposes four guardrails that tighten that path, and shows the one place where the missing guardrail is not just an attach failure but a host killer.
None of this has landed. The post-reset readiness poll is submitted upstream and awaiting review; the config-space restore and the ppt checks live on a local branch. The drawings describe the intended behaviour, not the current tree.
ppt_assign_device runs, the device is quiesced and reset before its config is restored, and PCIM_CMD_BUSMASTEREN is set last of all. Unassign runs the mirror image: clear the decode and mastering bits, save, reset, restore, then remove the device from the domain, so a device handed back to the host carries none of the guest's state.pcie_flr() returns false when a function has no FLR capability, and a function can also advertise FLR yet leave itself unchanged after one - capability is not efficacy. Either way the narrow reset did not take, so recovery escalates to the bridge's secondary bus reset and, failing that, to detaching and power-cycling the parent bridge and re-enumerating its subtree. Each step resets more of the machine, which is why the cheapest reset that actually works is the one to reach for.| rung | mechanism | scope | escalate when |
|---|---|---|---|
| Function-Level Reset | pcie_flr(): quiesce, then set the FLR bit in the PCIe Device Control register | one function | no FLR capability, or the FLR is issued but the function does not reset |
| Secondary bus reset | bridge control bit PCIB_BCR_SECBUS_RESET, or downstream link disable and retrain | everything on the secondary bus | the function is still wedged after a function reset |
| Parent-bridge / fabric reset | detach the bridge, pci_power_reset it, re-enumerate the subtree | the whole bridge subtree | last resort before a host reboot; recovers a subtree without one |
DEVF_RESET_DETACH path of pci_reset_child the FLR-success branch reattaches the child against config space the reset just zeroed. The saved bases survive in cfg.maps, so the fix is one call - pci_restore_state(child) before device_probe_and_attach. The plain devctl reset (suspend path) is unaffected, because pci_resume_child already restores. Proven on real hardware across two device classes, with opposite severities.These are separate changes that share a code path, not one patch. They are stated here at the honesty the evidence supports: two are proven on hardware or by fault injection, one rests on code reading alone, and none has landed. The config restore is the one with a hardware A/B behind it and the one that turns a corrupted host into a recoverable one; recovery itself needs no reboot, either by escalating the reset up the ladder to the parent bridge or by writing the saved BARs and command register back and reattaching.
| guardrail | status | evidence |
|---|---|---|
| Map the IOMMU domain before assigning the device | proposed, not landed | reasoned from a recorded IO_PAGE_FAULT trace; the fault it prevents was not reproduced on hardware here |
| Quiesce the device before resetting it, extending the wait to 1000 ms | proposed, not landed | proven by fail(9) injection against a control; no device on hand held transactions pending naturally |
| Poll for the function to answer config reads after a reset | submitted upstream, pending review | proven by fail(9) injection; matches an independent upstream bug report against real hardware |
| Restore config state after resetting a detached child | proposed, on a local branch | proven on hardware: failed attach on an aq 10G NIC, host-killing fatal PCIe error on an Intel I350, both fixed |
| Refuse to assign a device that cannot be reset | proposed, not landed | refusal path proven on a card with no FLR that sets No_Soft_Reset; over-refusal checked against an FLR-capable card |
Source: sys/dev/pci/pci.c (pcie_flr, pci_reset_child, pci_power_reset), sys/dev/pci/pci_pci.c (pcib_reset_child), sys/amd64/vmm/io/ppt.c (ppt_pci_reset, ppt_assign_device), sys/amd64/vmm/vmm.c (vm_assign_pptdev, vm_iommu_map).