Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F167638193
D58864.id184218.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
24 KB
Referenced Files
None
Subscribers
None
D58864.id184218.diff
View Options
diff --git a/lib/libvmmapi/internal.h b/lib/libvmmapi/internal.h
--- a/lib/libvmmapi/internal.h
+++ b/lib/libvmmapi/internal.h
@@ -67,7 +67,8 @@
VM_PPTDEV_MSI, \
VM_PPTDEV_MSIX, \
VM_UNMAP_PPTDEV_MMIO, \
- VM_PPTDEV_DISABLE_MSIX
+ VM_PPTDEV_DISABLE_MSIX, \
+ VM_RESET_PPTDEV
extern const cap_ioctl_t vm_ioctl_cmds[];
extern size_t vm_ioctl_ncmds;
diff --git a/lib/libvmmapi/ppt.c b/lib/libvmmapi/ppt.c
--- a/lib/libvmmapi/ppt.c
+++ b/lib/libvmmapi/ppt.c
@@ -62,6 +62,19 @@
return (ioctl(ctx->fd, VM_UNBIND_PPTDEV, &pptdev));
}
+int
+vm_reset_pptdev(struct vmctx *ctx, int bus, int slot, int func)
+{
+ struct vm_pptdev pptdev;
+
+ bzero(&pptdev, sizeof(pptdev));
+ pptdev.bus = bus;
+ pptdev.slot = slot;
+ pptdev.func = func;
+
+ return (ioctl(ctx->fd, VM_RESET_PPTDEV, &pptdev));
+}
+
int
vm_map_pptdev_mmio(struct vmctx *ctx, int bus, int slot, int func,
vm_paddr_t gpa, size_t len, vm_paddr_t hpa)
diff --git a/lib/libvmmapi/vmmapi.h b/lib/libvmmapi/vmmapi.h
--- a/lib/libvmmapi/vmmapi.h
+++ b/lib/libvmmapi/vmmapi.h
@@ -203,6 +203,7 @@
int val);
int vm_assign_pptdev(struct vmctx *ctx, int bus, int slot, int func);
int vm_unassign_pptdev(struct vmctx *ctx, int bus, int slot, int func);
+int vm_reset_pptdev(struct vmctx *ctx, int bus, int slot, int func);
int vm_map_pptdev_mmio(struct vmctx *ctx, int bus, int slot, int func,
vm_paddr_t gpa, size_t len, vm_paddr_t hpa);
int vm_unmap_pptdev_mmio(struct vmctx *ctx, int bus, int slot, int func,
diff --git a/sys/amd64/include/vmm.h b/sys/amd64/include/vmm.h
--- a/sys/amd64/include/vmm.h
+++ b/sys/amd64/include/vmm.h
@@ -232,6 +232,7 @@
int vm_unmap_mmio(struct vm *vm, vm_paddr_t gpa, size_t len);
int vm_assign_pptdev(struct vm *vm, int bus, int slot, int func);
int vm_unassign_pptdev(struct vm *vm, int bus, int slot, int func);
+int vm_reset_pptdev(struct vm *vm, int bus, int slot, int func);
int vm_get_register(struct vcpu *vcpu, int reg, uint64_t *retval);
int vm_set_register(struct vcpu *vcpu, int reg, uint64_t val);
diff --git a/sys/amd64/include/vmm_dev.h b/sys/amd64/include/vmm_dev.h
--- a/sys/amd64/include/vmm_dev.h
+++ b/sys/amd64/include/vmm_dev.h
@@ -306,6 +306,7 @@
IOCNUM_PPTDEV_MSIX = 44,
IOCNUM_PPTDEV_DISABLE_MSIX = 45,
IOCNUM_UNMAP_PPTDEV_MMIO = 46,
+ IOCNUM_RESET_PPTDEV = 47,
/* statistics */
IOCNUM_VM_STATS = 50,
@@ -420,6 +421,8 @@
_IOW('v', IOCNUM_PPTDEV_DISABLE_MSIX, struct vm_pptdev)
#define VM_UNMAP_PPTDEV_MMIO \
_IOW('v', IOCNUM_UNMAP_PPTDEV_MMIO, struct vm_pptdev_mmio)
+#define VM_RESET_PPTDEV \
+ _IOW('v', IOCNUM_RESET_PPTDEV, struct vm_pptdev)
#define VM_INJECT_NMI \
_IOW('v', IOCNUM_INJECT_NMI, struct vm_nmi)
#define VM_STATS \
diff --git a/sys/amd64/vmm/io/ppt.h b/sys/amd64/vmm/io/ppt.h
--- a/sys/amd64/vmm/io/ppt.h
+++ b/sys/amd64/vmm/io/ppt.h
@@ -48,4 +48,5 @@
*/
int ppt_assign_device(struct vm *vm, int bus, int slot, int func);
int ppt_unassign_device(struct vm *vm, int bus, int slot, int func);
+int ppt_reset_device(struct vm *vm, int bus, int slot, int func);
#endif
diff --git a/sys/amd64/vmm/io/ppt.c b/sys/amd64/vmm/io/ppt.c
--- a/sys/amd64/vmm/io/ppt.c
+++ b/sys/amd64/vmm/io/ppt.c
@@ -90,6 +90,7 @@
struct pptdev {
device_t dev;
struct vm *vm; /* owner of this device */
+ bool resetting; /* guest FLR in progress */
TAILQ_ENTRY(pptdev) next;
struct pptseg mmio[MAX_MMIOSEGS];
struct {
@@ -233,19 +234,30 @@
PPT_ASSERT_LOCKED();
- TAILQ_FOREACH(ppt, &pptdev_list, next) {
- dev = ppt->dev;
- b = pci_get_bus(dev);
- s = pci_get_slot(dev);
- f = pci_get_function(dev);
- if (bus == b && slot == s && func == f)
+ for (;;) {
+ TAILQ_FOREACH(ppt, &pptdev_list, next) {
+ dev = ppt->dev;
+ b = pci_get_bus(dev);
+ s = pci_get_slot(dev);
+ f = pci_get_function(dev);
+ if (bus == b && slot == s && func == f)
+ break;
+ }
+
+ if (ppt == NULL)
+ return (ENOENT);
+ if (ppt->vm != vm) /* Make sure we own this device. */
+ return (EBUSY);
+ if (!ppt->resetting)
break;
+ /*
+ * Once resetting is set, every exit from ppt_reset_device()
+ * reacquires ppt_mtx, clears resetting, and wakes us. The FLR wait
+ * itself is bounded.
+ */
+ sx_sleep(ppt, &ppt_mtx, 0, "pptflr", 0);
}
- if (ppt == NULL)
- return (ENOENT);
- if (ppt->vm != vm) /* Make sure we own this device */
- return (EBUSY);
*pptp = ppt;
return (0);
}
@@ -474,6 +486,108 @@
return (error);
}
+int
+ppt_reset_device(struct vm *vm, int bus, int slot, int func)
+{
+ struct pptdev *ppt;
+ uint16_t cmd, enables, original_cmd;
+ int error;
+
+ PPT_LOCK();
+ error = ppt_find(vm, bus, slot, func, &ppt);
+ if (error != 0)
+ goto out_locked;
+
+ /*
+ * FLR takes at least 100 ms. Reserve this function, but do not let a
+ * guest hold the global PPT lock and delay operations on other VMs.
+ */
+ ppt->resetting = true;
+ PPT_UNLOCK();
+
+ original_cmd = pci_read_config(ppt->dev, PCIR_COMMAND, 2);
+ if (original_cmd == 0xffff) {
+ error = ENXIO;
+ goto out;
+ }
+ if (!pcie_flr_supported(ppt->dev)) {
+ error = ENOTSUP;
+ goto out;
+ }
+
+ /*
+ * Disable physical INTx before releasing its handler. This also makes
+ * an asserted Function send Deassert_INTx before FLR, as required by
+ * PCIe. Gate decoding and DMA before tearing down MSI or MSI-X.
+ */
+ cmd = original_cmd | PCIM_CMD_INTxDIS;
+ cmd &= ~(PCIM_CMD_PORTEN | PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN);
+ pci_write_config(ppt->dev, PCIR_COMMAND, cmd, 2);
+ ppt_teardown_msi(ppt);
+ ppt_teardown_msix(ppt);
+
+ /*
+ * Save the host-owned state after interrupt teardown, then restore BARs
+ * and PCIe controls after FLR. The IOMMU domain remains intact. bhyve
+ * removes guest BAR mappings before this ioctl; a later guest MEMEN write
+ * recreates them.
+ */
+ pci_save_state(ppt->dev);
+
+ /*
+ * A guest-requested FLR must not be escalated to a power reset. The
+ * support check above and force=true mean that pcie_flr() cannot fail
+ * for a stable function. If its support nevertheless disappears
+ * between the two checks, destructive preparation has already torn down
+ * host interrupt resources. Return EIO so bhyve discards the now-stale
+ * guest interrupt state even though the FLR was not initiated.
+ */
+ if (!pcie_flr(ppt->dev,
+ max(pcie_get_max_completion_timeout(ppt->dev) / 1000, 10), true)) {
+ device_printf(ppt->dev, "guest FLR could not be performed\n");
+ error = EIO;
+ goto restore;
+ }
+ error = 0;
+
+ /*
+ * Restore the decode and DMA enables which were set before the FLR;
+ * the guest command register is intentionally virtual. Do not infer
+ * writable enables from the BAR resources here. For example, a VF can
+ * use PF-owned BAR apertures while its own MEMEN bit is RsvdP.
+ *
+ * A post-reset readback failure does not undo the guest-visible reset.
+ */
+restore:
+ pci_restore_state(ppt->dev);
+ enables = original_cmd &
+ (PCIM_CMD_PORTEN | PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN);
+ cmd = pci_read_config(ppt->dev, PCIR_COMMAND, 2);
+ if (cmd == 0xffff) {
+ device_printf(ppt->dev,
+ "config space unavailable after guest FLR\n");
+ error = EIO;
+ goto out;
+ }
+ cmd &= ~(PCIM_CMD_PORTEN | PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN |
+ PCIM_CMD_INTxDIS);
+ cmd |= enables | (original_cmd & PCIM_CMD_INTxDIS);
+ pci_write_config(ppt->dev, PCIR_COMMAND, cmd, 2);
+ cmd = pci_read_config(ppt->dev, PCIR_COMMAND, 2);
+ if (cmd == 0xffff || (cmd & enables) != enables) {
+ device_printf(ppt->dev,
+ "failed to restore command register after guest FLR\n");
+ error = EIO;
+ }
+out:
+ PPT_LOCK();
+ ppt->resetting = false;
+ wakeup(ppt);
+out_locked:
+ PPT_UNLOCK();
+ return (error);
+}
+
int
ppt_unassign_all(struct vm *vm)
{
diff --git a/sys/amd64/vmm/vmm.c b/sys/amd64/vmm/vmm.c
--- a/sys/amd64/vmm/vmm.c
+++ b/sys/amd64/vmm/vmm.c
@@ -591,6 +591,13 @@
return (error);
}
+int
+vm_reset_pptdev(struct vm *vm, int bus, int slot, int func)
+{
+
+ return (ppt_reset_device(vm, bus, slot, func));
+}
+
int
vm_get_register(struct vcpu *vcpu, int reg, uint64_t *retval)
{
diff --git a/sys/amd64/vmm/vmm_dev_machdep.c b/sys/amd64/vmm/vmm_dev_machdep.c
--- a/sys/amd64/vmm/vmm_dev_machdep.c
+++ b/sys/amd64/vmm/vmm_dev_machdep.c
@@ -129,6 +129,8 @@
VMMDEV_IOCTL(VM_UNBIND_PPTDEV,
VMMDEV_IOCTL_XLOCK_MEMSEGS | VMMDEV_IOCTL_LOCK_ALL_VCPUS |
VMMDEV_IOCTL_PPT),
+ VMMDEV_IOCTL(VM_RESET_PPTDEV, VMMDEV_IOCTL_LOCK_ALL_VCPUS |
+ VMMDEV_IOCTL_PPT),
VMMDEV_IOCTL(VM_MAP_PPTDEV_MMIO, VMMDEV_IOCTL_LOCK_ALL_VCPUS |
VMMDEV_IOCTL_PPT),
@@ -316,6 +318,14 @@
pptdev->func);
break;
}
+ case VM_RESET_PPTDEV: {
+ struct vm_pptdev *pptdev;
+
+ pptdev = (struct vm_pptdev *)data;
+ error = vm_reset_pptdev(vm, pptdev->bus, pptdev->slot,
+ pptdev->func);
+ break;
+ }
case VM_INJECT_EXCEPTION: {
struct vm_exception *vmexc;
diff --git a/usr.sbin/bhyve/pci_emul.h b/usr.sbin/bhyve/pci_emul.h
--- a/usr.sbin/bhyve/pci_emul.h
+++ b/usr.sbin/bhyve/pci_emul.h
@@ -136,6 +136,7 @@
struct pci_irq irq;
pthread_mutex_t lock;
} pi_lintr;
+ pthread_mutex_t pi_cfg_lock;
struct {
int enabled;
diff --git a/usr.sbin/bhyve/pci_emul.c b/usr.sbin/bhyve/pci_emul.c
--- a/usr.sbin/bhyve/pci_emul.c
+++ b/usr.sbin/bhyve/pci_emul.c
@@ -1152,6 +1152,7 @@
pdi->pi_slot = slot;
pdi->pi_func = func;
pthread_mutex_init(&pdi->pi_lintr.lock, NULL);
+ pthread_mutex_init(&pdi->pi_cfg_lock, NULL);
pdi->pi_lintr.pin = 0;
pdi->pi_lintr.state = IDLE;
pci_irq_init_irq(&pdi->pi_lintr.irq);
@@ -1169,8 +1170,11 @@
err = (*pde->pe_init)(pdi, fi->fi_config);
if (err == 0)
fi->fi_devi = pdi;
- else
+ else {
+ pthread_mutex_destroy(&pdi->pi_cfg_lock);
+ pthread_mutex_destroy(&pdi->pi_lintr.lock);
free(pdi);
+ }
return (err);
}
@@ -2309,6 +2313,7 @@
}
pe = pi->pi_d;
+ pthread_mutex_lock(&pi->pi_cfg_lock);
/*
* Config read
@@ -2329,7 +2334,7 @@
/* Let the device emulation override the default handler */
if (pe->pe_cfgwrite != NULL &&
(*pe->pe_cfgwrite)(pi, coff, bytes, *valp) == 0)
- return;
+ goto out;
/*
* Special handling for write to BAR and ROM registers
@@ -2340,7 +2345,7 @@
* 4-byte aligned.
*/
if (bytes != 4 || (coff & 0x3) != 0)
- return;
+ goto out;
if (is_pcir_bar(coff)) {
idx = (coff - PCIR_BAR(0)) / 4;
@@ -2422,6 +2427,8 @@
CFGWRITE(pi, coff, *valp, bytes);
}
}
+out:
+ pthread_mutex_unlock(&pi->pi_cfg_lock);
}
#ifdef __amd64__
diff --git a/usr.sbin/bhyve/pci_passthru.c b/usr.sbin/bhyve/pci_passthru.c
--- a/usr.sbin/bhyve/pci_passthru.c
+++ b/usr.sbin/bhyve/pci_passthru.c
@@ -55,6 +55,8 @@
#include <err.h>
#include <errno.h>
#include <fcntl.h>
+#include <pthread.h>
+#include <stdatomic.h>
#include <sysexits.h>
#include <unistd.h>
@@ -99,7 +101,17 @@
struct {
int capoff;
} psc_msix;
+ struct {
+ int capoff;
+ uint16_t devctl;
+ uint16_t reset_devctl;
+ uint16_t devctl2;
+ uint16_t reset_devctl2;
+ bool has_devctl2;
+ } psc_pcie;
struct pcisel psc_sel;
+ pthread_mutex_t psc_io_mtx;
+ atomic_bool psc_resetting;
struct passthru_mmio_mapping psc_mmio_map[PASSTHRU_MMIO_MAX];
cfgread_handler psc_pcir_rhandler[PCI_REGMAX + 1];
@@ -263,9 +275,10 @@
#endif /* LEGACY_SUPPORT */
static int
-cfginitmsi(struct passthru_softc *sc)
+cfginitcaps(struct passthru_softc *sc)
{
int i, ptr, capptr, cap, sts, caplen, table_size;
+ uint16_t flags;
uint32_t u32;
struct pcisel sel;
struct pci_devinst *pi;
@@ -319,11 +332,32 @@
capptr += 4;
msixcap_ptr += 4;
}
+ } else if (cap == PCIY_EXPRESS) {
+ sc->psc_pcie.capoff = ptr;
}
ptr = passthru_read_config(&sel, ptr + PCICAP_NEXTPTR,
1);
}
}
+ if (sc->psc_pcie.capoff != 0) {
+ sc->psc_pcie.devctl = passthru_read_config(&sel,
+ sc->psc_pcie.capoff + PCIER_DEVICE_CTL, 2);
+ /*
+ * Use the assignment-time guest view as the virtual reset baseline.
+ * Host firmware and the PCI bus may already have tuned Device Control,
+ * so restoring the hardware reset defaults would expose a different
+ * configuration after the first guest FLR.
+ */
+ sc->psc_pcie.reset_devctl = sc->psc_pcie.devctl;
+ flags = passthru_read_config(&sel,
+ sc->psc_pcie.capoff + PCIER_FLAGS, 2);
+ if ((flags & PCIEM_FLAGS_VERSION) >= 2) {
+ sc->psc_pcie.has_devctl2 = true;
+ sc->psc_pcie.devctl2 = passthru_read_config(&sel,
+ sc->psc_pcie.capoff + PCIER_DEVICE_CTL2, 2);
+ sc->psc_pcie.reset_devctl2 = sc->psc_pcie.devctl2;
+ }
+ }
if (sc->psc_msix.capoff != 0) {
pi->pi_msix.pba_bar =
@@ -340,6 +374,8 @@
/* Allocate the emulated MSI-X table array */
table_size = pi->pi_msix.table_count * MSIX_TABLE_ENTRY_SIZE;
pi->pi_msix.table = calloc(1, table_size);
+ if (pi->pi_msix.table == NULL)
+ return (-1);
/* Mask all table entries */
for (i = 0; i < pi->pi_msix.table_count; i++) {
@@ -691,8 +727,8 @@
pci_set_cfgdata8(pi, PCIR_INTLINE, intline);
pci_set_cfgdata8(pi, PCIR_INTPIN, intpin);
- if (cfginitmsi(sc) != 0) {
- warnx("failed to initialize MSI for PCI %d/%d/%d",
+ if (cfginitcaps(sc) != 0) {
+ warnx("failed to initialize PCI capabilities for %d/%d/%d",
bus, slot, func);
goto done;
}
@@ -998,6 +1034,10 @@
}
sc = calloc(1, sizeof(struct passthru_softc));
+ if (sc == NULL)
+ goto done;
+ pthread_mutex_init(&sc->psc_io_mtx, NULL);
+ atomic_init(&sc->psc_resetting, false);
pi->pi_arg = sc;
sc->psc_pi = pi;
@@ -1043,6 +1083,8 @@
if (error) {
if (dev != NULL)
dev->deinit(pi);
+ if (sc != NULL)
+ pthread_mutex_destroy(&sc->psc_io_mtx);
free(sc);
vm_unassign_pptdev(pi->pi_vmctx, bus, slot, func);
}
@@ -1075,6 +1117,163 @@
coff < sc->psc_msix.capoff + MSIX_CAPLEN);
}
+#define PASSTHRU_DEVCTL_VIRT (PCIEM_CTL_MAX_PAYLOAD | \
+ PCIEM_CTL_MAX_READ_REQUEST)
+#define PASSTHRU_DEVCTL_NO_WRITE PCIEM_CTL_PHANTHOM_FUNCS
+#define PASSTHRU_DEVCTL2_VIRT (PCIEM_CTL2_COMP_TIMO_VAL | \
+ PCIEM_CTL2_COMP_TIMO_DISABLE)
+
+static uint32_t
+passthru_cfg_field_mask(int coff, int bytes, int fieldoff, uint16_t mask)
+{
+ uint32_t access_mask;
+ int i, pos;
+
+ access_mask = 0;
+ for (i = 0; i < bytes; i++) {
+ pos = coff + i;
+ if (pos >= fieldoff && pos < fieldoff + 2)
+ access_mask |= ((mask >> ((pos - fieldoff) * NBBY)) &
+ 0xff) << (i * NBBY);
+ }
+ return (access_mask);
+}
+
+static uint32_t
+passthru_cfg_field_value(int coff, int bytes, int fieldoff, uint16_t value)
+{
+ uint32_t access_value;
+ int i, pos;
+
+ access_value = 0;
+ for (i = 0; i < bytes; i++) {
+ pos = coff + i;
+ if (pos >= fieldoff && pos < fieldoff + 2)
+ access_value |= ((value >> ((pos - fieldoff) * NBBY)) &
+ 0xff) << (i * NBBY);
+ }
+ return (access_value);
+}
+
+static void
+passthru_cfg_update_field(int coff, int bytes, uint32_t value, int fieldoff,
+ uint16_t mask, uint16_t *field)
+{
+ uint16_t byte_mask, byte_value;
+ int i, pos, shift;
+
+ for (i = 0; i < bytes; i++) {
+ pos = coff + i;
+ if (pos < fieldoff || pos >= fieldoff + 2)
+ continue;
+ shift = (pos - fieldoff) * NBBY;
+ byte_mask = mask & (0xff << shift);
+ byte_value = ((value >> (i * NBBY)) & 0xff) << shift;
+ *field = (*field & ~byte_mask) | (byte_value & byte_mask);
+ }
+}
+
+static void
+passthru_reset_interrupt_state(struct passthru_softc *sc)
+{
+ struct pci_devinst *pi;
+ uint16_t msgctrl;
+ int caplen, i;
+
+ pi = sc->psc_pi;
+ if (sc->psc_msi.capoff != 0) {
+ caplen = msi_caplen(sc->psc_msi.msgctrl);
+ msgctrl = pci_get_cfgdata16(pi, sc->psc_msi.capoff + 2);
+ msgctrl &= ~(PCIM_MSICTRL_MME_MASK | PCIM_MSICTRL_MSI_ENABLE);
+ memset(pi->pi_cfgdata + sc->psc_msi.capoff + 4, 0, caplen - 4);
+ pci_set_cfgdata16(pi, sc->psc_msi.capoff + 2, msgctrl);
+ pi->pi_msi.enabled = 0;
+ pi->pi_msi.addr = 0;
+ pi->pi_msi.msg_data = 0;
+ pi->pi_msi.maxmsgnum = 0;
+ }
+ if (sc->psc_msix.capoff != 0) {
+ msgctrl = pci_get_cfgdata16(pi, sc->psc_msix.capoff + 2);
+ msgctrl &= ~(PCIM_MSIXCTRL_MSIX_ENABLE |
+ PCIM_MSIXCTRL_FUNCTION_MASK);
+ pci_set_cfgdata16(pi, sc->psc_msix.capoff + 2, msgctrl);
+ pi->pi_msix.enabled = 0;
+ pi->pi_msix.function_mask = 0;
+ bzero(pi->pi_msix.table, pi->pi_msix.table_count *
+ sizeof(pi->pi_msix.table[0]));
+ for (i = 0; i < pi->pi_msix.table_count; i++)
+ pi->pi_msix.table[i].vector_control =
+ PCIM_MSIX_VCTRL_MASK;
+ }
+}
+
+static void
+passthru_reset_capability_state(struct passthru_softc *sc)
+{
+ uint16_t devctl, guest_mps;
+ int offset;
+
+ /* MPS is explicitly preserved across FLR by the PCIe specification. */
+ guest_mps = sc->psc_pcie.devctl & PCIEM_CTL_MAX_PAYLOAD;
+ offset = sc->psc_pcie.capoff + PCIER_DEVICE_CTL;
+ devctl = passthru_read_config(&sc->psc_sel, offset, 2);
+ if (devctl != 0xffff) {
+ devctl &= ~PCIEM_CTL_MAX_READ_REQUEST;
+ devctl |= sc->psc_pcie.reset_devctl &
+ PCIEM_CTL_MAX_READ_REQUEST;
+ passthru_write_config(&sc->psc_sel, offset, 2, devctl);
+ }
+ sc->psc_pcie.devctl =
+ (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;
+}
+
+static int
+passthru_reset(struct passthru_softc *sc)
+{
+ struct pci_devinst *pi;
+ uint16_t command;
+ int error, saved_errno;
+
+ pi = sc->psc_pi;
+ command = pci_get_cfgdata16(pi, PCIR_COMMAND);
+
+ /*
+ * Stop trapped BAR accesses and drain any handler already touching the
+ * device. pci_cfgrw() holds pi_cfg_lock for this entire transaction,
+ * so another vCPU cannot re-enable or move a BAR around the reset.
+ */
+ atomic_store_explicit(&sc->psc_resetting, true, memory_order_release);
+ pthread_mutex_lock(&sc->psc_io_mtx);
+ pthread_mutex_unlock(&sc->psc_io_mtx);
+
+ if (pi->pi_lintr.pin != 0)
+ pci_lintr_deassert(pi);
+ pci_set_cfgdata16(pi, PCIR_COMMAND, 0);
+ pci_emul_cmd_changed(pi, command);
+
+ error = vm_reset_pptdev(pi->pi_vmctx, sc->psc_sel.pc_bus,
+ sc->psc_sel.pc_dev, sc->psc_sel.pc_func);
+ saved_errno = error == 0 ? 0 : errno;
+ /*
+ * EIO means PPT crossed the destructive preparation boundary. The FLR
+ * either ran or failed after host interrupt resources were torn down, so
+ * the corresponding guest state must be discarded in either case.
+ */
+ if (error != 0 && saved_errno != EIO) {
+ pci_set_cfgdata16(pi, PCIR_COMMAND, command);
+ pci_emul_cmd_changed(pi, 0);
+ } else {
+ passthru_reset_interrupt_state(sc);
+ passthru_reset_capability_state(sc);
+ }
+ atomic_store_explicit(&sc->psc_resetting, false, memory_order_release);
+ if (error != 0)
+ errno = saved_errno;
+ return (error);
+}
+
static int
passthru_cfgread_default(struct passthru_softc *sc,
struct pci_devinst *pi __unused, int coff, int bytes, uint32_t *rv)
@@ -1100,8 +1299,34 @@
return (0);
}
- /* Everything else just read from the device's config space */
+ /* Everything else just read from the device's config space. */
*rv = passthru_read_config(&sc->psc_sel, coff, bytes);
+ if (sc->psc_pcie.capoff != 0) {
+ uint32_t mask;
+ uint16_t physical_devctl;
+ int devctl;
+
+ devctl = sc->psc_pcie.capoff + PCIER_DEVICE_CTL;
+ mask = passthru_cfg_field_mask(coff, bytes, devctl,
+ PASSTHRU_DEVCTL_VIRT);
+ if (mask != 0) {
+ physical_devctl = passthru_read_config(&sc->psc_sel,
+ devctl, 2);
+ if (physical_devctl != 0xffff)
+ *rv = (*rv & ~mask) |
+ (passthru_cfg_field_value(coff, bytes,
+ devctl, sc->psc_pcie.devctl) & mask);
+ }
+ if (sc->psc_pcie.has_devctl2) {
+ devctl = sc->psc_pcie.capoff + PCIER_DEVICE_CTL2;
+ mask = passthru_cfg_field_mask(coff, bytes, devctl,
+ PASSTHRU_DEVCTL2_VIRT);
+ if (mask != 0)
+ *rv = (*rv & ~mask) |
+ (passthru_cfg_field_value(coff, bytes, devctl,
+ sc->psc_pcie.devctl2) & mask);
+ }
+ }
return (0);
}
@@ -1131,6 +1356,9 @@
passthru_cfgwrite_default(struct passthru_softc *sc, struct pci_devinst *pi,
int coff, int bytes, uint32_t val)
{
+ uint32_t flr_mask, host_mask, transport_mask;
+ uint16_t physical_devctl;
+ int devctl, devctl2, host_mps, guest_mrrs;
int error, msix_table_entries, i;
uint16_t cmd_old;
@@ -1175,6 +1403,84 @@
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
+ * host-owned state. MPS is shared-path policy, and Phantom Functions
+ * Enable changes IOMMU-visible requester IDs, so keep both host-owned.
+ * Floor physical MRRS at physical MPS because the guest lacks the
+ * hierarchy view. ppt also applies FLR quirks for VFs such as 82599.
+ */
+ devctl = sc->psc_pcie.capoff + PCIER_DEVICE_CTL;
+ if (sc->psc_pcie.capoff != 0 && coff < devctl + 2 &&
+ coff + bytes > devctl) {
+ transport_mask = passthru_cfg_field_mask(coff, bytes, devctl,
+ PASSTHRU_DEVCTL_VIRT);
+ host_mask = passthru_cfg_field_mask(coff, bytes, devctl,
+ PASSTHRU_DEVCTL_VIRT | PASSTHRU_DEVCTL_NO_WRITE);
+ physical_devctl = passthru_read_config(&sc->psc_sel, devctl, 2);
+ if (physical_devctl == 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, devctl,
+ PASSTHRU_DEVCTL_VIRT, &sc->psc_pcie.devctl);
+ if ((transport_mask & passthru_cfg_field_mask(coff, bytes,
+ devctl, PCIEM_CTL_MAX_READ_REQUEST)) != 0) {
+ host_mps = (physical_devctl & PCIEM_CTL_MAX_PAYLOAD) >> 5;
+ guest_mrrs = (sc->psc_pcie.devctl &
+ PCIEM_CTL_MAX_READ_REQUEST) >> 12;
+ if (guest_mrrs <= 5) {
+ if (guest_mrrs < host_mps)
+ guest_mrrs = host_mps;
+ physical_devctl &= ~PCIEM_CTL_MAX_READ_REQUEST;
+ physical_devctl |= guest_mrrs << 12;
+ }
+ }
+ val = (val & ~host_mask) |
+ (passthru_cfg_field_value(coff, bytes, devctl,
+ physical_devctl) & host_mask);
+
+ flr_mask = passthru_cfg_field_mask(coff, bytes, devctl,
+ PCIEM_CTL_INITIATE_FLR);
+ passthru_write_config(&sc->psc_sel, coff, bytes,
+ val & ~flr_mask);
+ if ((val & flr_mask) != 0 && passthru_reset(sc) != 0)
+ warnx("failed to reset passthru device %d/%d/%d: %s",
+ sc->psc_sel.pc_bus, sc->psc_sel.pc_dev,
+ sc->psc_sel.pc_func, strerror(errno));
+ return (0);
+ }
+
+ /*
+ * Completion Timeout controls how long the host must protect against an
+ * in-flight completion after a forced FLR. Keep the physical policy
+ * host-owned so a guest cannot extend the reset ioctl for tens of
+ * seconds, but retain a guest-visible value for normal PCI semantics.
+ */
+ devctl2 = sc->psc_pcie.capoff + PCIER_DEVICE_CTL2;
+ if (sc->psc_pcie.has_devctl2 && coff < devctl2 + 2 &&
+ coff + bytes > devctl2) {
+ physical_devctl = passthru_read_config(&sc->psc_sel, devctl2, 2);
+ if (physical_devctl == 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, devctl2,
+ PASSTHRU_DEVCTL2_VIRT, &sc->psc_pcie.devctl2);
+ host_mask = passthru_cfg_field_mask(coff, bytes, devctl2,
+ PASSTHRU_DEVCTL2_VIRT);
+ val = (val & ~host_mask) |
+ (passthru_cfg_field_value(coff, bytes, devctl2,
+ physical_devctl) & host_mask);
+ passthru_write_config(&sc->psc_sel, coff, bytes, val);
+ return (0);
+ }
+
/*
* The command register is emulated, but the status register
* is passed through.
@@ -1220,8 +1526,8 @@
}
static void
-passthru_write(struct pci_devinst *pi, int baridx, uint64_t offset, int size,
- uint64_t value)
+passthru_write_locked(struct pci_devinst *pi, int baridx, uint64_t offset,
+ int size, uint64_t value)
{
struct passthru_softc *sc;
struct passthru_bar_handler *handler;
@@ -1267,7 +1573,8 @@
}
static uint64_t
-passthru_read(struct pci_devinst *pi, int baridx, uint64_t offset, int size)
+passthru_read_locked(struct pci_devinst *pi, int baridx, uint64_t offset,
+ int size)
{
struct passthru_softc *sc;
struct passthru_bar_handler *handler;
@@ -1315,6 +1622,39 @@
return (val);
}
+static void
+passthru_write(struct pci_devinst *pi, int baridx, uint64_t offset, int size,
+ uint64_t value)
+{
+ struct passthru_softc *sc;
+
+ sc = pi->pi_arg;
+ if (atomic_load_explicit(&sc->psc_resetting, memory_order_acquire))
+ return;
+ pthread_mutex_lock(&sc->psc_io_mtx);
+ if (!atomic_load_explicit(&sc->psc_resetting, memory_order_relaxed))
+ passthru_write_locked(pi, baridx, offset, size, value);
+ pthread_mutex_unlock(&sc->psc_io_mtx);
+}
+
+static uint64_t
+passthru_read(struct pci_devinst *pi, int baridx, uint64_t offset, int size)
+{
+ struct passthru_softc *sc;
+ uint64_t value;
+
+ sc = pi->pi_arg;
+ if (atomic_load_explicit(&sc->psc_resetting, memory_order_acquire))
+ return (UINT64_MAX);
+ pthread_mutex_lock(&sc->psc_io_mtx);
+ if (atomic_load_explicit(&sc->psc_resetting, memory_order_relaxed))
+ value = UINT64_MAX;
+ else
+ value = passthru_read_locked(pi, baridx, offset, size);
+ pthread_mutex_unlock(&sc->psc_io_mtx);
+ return (value);
+}
+
static int
passthru_mmio_map(struct pci_devinst *pi, int baridx, int enabled,
uint64_t address, uint64_t off, uint64_t size)
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Aug 24, 9:55 AM (2 h, 30 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
37180762
Default Alt Text
D58864.id184218.diff (24 KB)
Attached To
Mode
D58864: bhyve: Manage passthrough devices across guest FLR
Attached
Detach File
Event Timeline
Log In to Comment