git: 3b90096cf9bc - main - bhyve: Keep passthrough PCI power state virtual
Kevin Bowling <[email protected]>
| Newsgroups | gmane.os.freebsd.devel.cvs.src |
|---|---|
| Message-ID | <6a877c53.3ba1d.5088bd4e__36778.8459103153$1787264100$gmane$org@gitrepo.freebsd.org> |
The branch main has been updated by kbowling: URL: https://cgit.FreeBSD.org/src/commit/?id=3b90096cf9bcaec70b717e9ff0a9e23d14b600b6 commit 3b90096cf9bcaec70b717e9ff0a9e23d14b600b6 Author: Kevin Bowling <[email protected]> AuthorDate: 2026-08-17 17:09:23 +0000 Commit: Kevin Bowling <[email protected]> CommitDate: 2026-08-20 22:13:36 +0000 bhyve: Keep passthrough PCI power state virtual The passthrough Command register is emulated, but PMCSR writes were sent directly to the physical function. A guest D3hot-to-D0 transition can perform an internal reset and clear physical Command while its emulated copy remains enabled. Cache the Power Management capability and keep the physical D-state host-owned. Emulate the guest D-state and advertise No_Soft_Reset so the guest is not promised a function reset by a virtual power cycle. Restore the assignment-time virtual state after a managed FLR. Reviewed by: markj MFC after: 2 weeks Sponsored by: BBOX.io --- usr.sbin/bhyve/pci_passthru.c | 53 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/usr.sbin/bhyve/pci_passthru.c b/usr.sbin/bhyve/pci_passthru.c index a78ca06c9d83..24d96764964d 100644 --- a/usr.sbin/bhyve/pci_passthru.c +++ b/usr.sbin/bhyve/pci_passthru.c @@ -100,6 +100,11 @@ struct passthru_softc { struct { int capoff; } psc_msix; + struct { + int capoff; + uint16_t pmcsr; + uint16_t reset_pmcsr; + } psc_pm; struct { int capoff; uint16_t devctl; @@ -331,6 +336,8 @@ cfginitcaps(struct passthru_softc *sc) capptr += 4; msixcap_ptr += 4; } + } else if (cap == PCIY_PMG) { + sc->psc_pm.capoff = ptr; } else if (cap == PCIY_EXPRESS) { sc->psc_pcie.capoff = ptr; } @@ -338,6 +345,13 @@ cfginitcaps(struct passthru_softc *sc) 1); } } + if (sc->psc_pm.capoff != 0) { + sc->psc_pm.pmcsr = passthru_read_config(&sel, + sc->psc_pm.capoff + PCIR_POWER_STATUS, 2); + /* The physical function remains in its host-owned power state. */ + sc->psc_pm.pmcsr |= PCIM_PSTAT_NOSOFTRESET; + sc->psc_pm.reset_pmcsr = sc->psc_pm.pmcsr; + } if (sc->psc_pcie.capoff != 0) { sc->psc_pcie.devctl = passthru_read_config(&sel, sc->psc_pcie.capoff + PCIER_DEVICE_CTL, 2); @@ -1120,6 +1134,8 @@ msixcap_access(struct passthru_softc *sc, int coff) #define PASSTHRU_DEVCTL_NO_WRITE PCIEM_CTL_PHANTHOM_FUNCS #define PASSTHRU_DEVCTL2_VIRT (PCIEM_CTL2_COMP_TIMO_VAL | \ PCIEM_CTL2_COMP_TIMO_DISABLE) +#define PASSTHRU_PMCSR_VIRT (PCIM_PSTAT_DMASK | \ + PCIM_PSTAT_NOSOFTRESET) static uint32_t passthru_cfg_field_mask(int coff, int bytes, int fieldoff, uint16_t mask) @@ -1243,6 +1259,8 @@ passthru_reset_capability_state(struct passthru_softc *sc) (sc->psc_pcie.reset_devctl & ~PCIEM_CTL_MAX_PAYLOAD) | guest_mps; if (sc->psc_pcie.has_devctl2) sc->psc_pcie.devctl2 = sc->psc_pcie.reset_devctl2; + if (sc->psc_pm.capoff != 0) + sc->psc_pm.pmcsr = sc->psc_pm.reset_pmcsr; } static int @@ -1320,6 +1338,13 @@ passthru_cfgread_default(struct passthru_softc *sc, /* Everything else just read from the device's config space. */ *rv = passthru_read_config(&sc->psc_sel, coff, bytes); + if (sc->psc_pm.capoff != 0) { + int pmcsr; + + pmcsr = sc->psc_pm.capoff + PCIR_POWER_STATUS; + *rv = passthru_cfg_overlay_field(coff, bytes, *rv, pmcsr, + sc->psc_pm.pmcsr, PASSTHRU_PMCSR_VIRT); + } if (sc->psc_pcie.capoff != 0) { int devctl; @@ -1362,8 +1387,8 @@ passthru_cfgwrite_default(struct passthru_softc *sc, struct pci_devinst *pi, int coff, int bytes, uint32_t val) { uint32_t flr_mask, transport_mask; - uint16_t physical_devctl; - int devctl, devctl2, host_mps, guest_mrrs; + uint16_t physical_devctl, physical_pmcsr; + int devctl, devctl2, host_mps, guest_mrrs, pmcsr; int error, msix_table_entries, i; uint16_t cmd_old; @@ -1408,6 +1433,30 @@ passthru_cfgwrite_default(struct passthru_softc *sc, struct pci_devinst *pi, return (0); } + /* + * A physical D3hot-to-D0 transition may reset the function and clear + * Command behind bhyve's emulated copy. Keep the physical D-state + * host-owned, emulate the guest D-state, and advertise No_Soft_Reset so + * the guest does not rely on this cycle as a function reset. + */ + pmcsr = sc->psc_pm.capoff + PCIR_POWER_STATUS; + if (sc->psc_pm.capoff != 0 && coff < pmcsr + 2 && + coff + bytes > pmcsr) { + physical_pmcsr = passthru_read_config(&sc->psc_sel, pmcsr, 2); + if (physical_pmcsr == 0xffff) { + warnx("configuration space unavailable for passthru " + "device %d/%d/%d", sc->psc_sel.pc_bus, + sc->psc_sel.pc_dev, sc->psc_sel.pc_func); + return (0); + } + passthru_cfg_update_field(coff, bytes, val, pmcsr, + PCIM_PSTAT_DMASK, &sc->psc_pm.pmcsr); + val = passthru_cfg_overlay_field(coff, bytes, val, pmcsr, + physical_pmcsr, PASSTHRU_PMCSR_VIRT); + passthru_write_config(&sc->psc_sel, coff, bytes, val); + return (0); + } + /* * A direct FLR would clear physical Command while the guest sees its * emulated copy remain enabled. Route FLR through ppt so it restores