From 5628267505daf80967d5c7fd4ccbd2130ac583a5 Mon Sep 17 00:00:00 2001 From: John Baldwin Date: Fri, 7 Jun 2019 15:53:27 +0000 Subject: [PATCH] Keep the shadow PCIR_COMMAND synced with the real one for pass through. This ensures that bhyve properly recognizes when decoding is disabled for BARs on passthru devices. To properly handle writes to the register, export a pci_emul_cmd_changed function from pci_emul.c that the pass through device model invokes for config writes that change PCIR_COMMAND. Reviewed by: rgrimes MFC after: 2 weeks Differential Revision: https://reviews.freebsd.org/D20531 --- usr.sbin/bhyve/pci_emul.c | 99 ++++++++++++++++++++--------------- usr.sbin/bhyve/pci_emul.h | 1 + usr.sbin/bhyve/pci_passthru.c | 12 +++++ 3 files changed, 69 insertions(+), 43 deletions(-) diff --git a/usr.sbin/bhyve/pci_emul.c b/usr.sbin/bhyve/pci_emul.c index 0502cde9c053..a8fc53e0496e 100644 --- a/usr.sbin/bhyve/pci_emul.c +++ b/usr.sbin/bhyve/pci_emul.c @@ -1679,11 +1679,64 @@ pci_emul_hdrtype_fixup(int bus, int slot, int off, int bytes, uint32_t *rv) } } +/* + * Update device state in response to changes to the PCI command + * register. + */ +void +pci_emul_cmd_changed(struct pci_devinst *pi, uint16_t old) +{ + int i; + uint16_t changed, new; + + new = pci_get_cfgdata16(pi, PCIR_COMMAND); + changed = old ^ new; + + /* + * If the MMIO or I/O address space decoding has changed then + * register/unregister all BARs that decode that address space. + */ + for (i = 0; i <= PCI_BARMAX; i++) { + switch (pi->pi_bar[i].type) { + case PCIBAR_NONE: + case PCIBAR_MEMHI64: + break; + case PCIBAR_IO: + /* I/O address space decoding changed? */ + if (changed & PCIM_CMD_PORTEN) { + if (new & PCIM_CMD_PORTEN) + register_bar(pi, i); + else + unregister_bar(pi, i); + } + break; + case PCIBAR_MEM32: + case PCIBAR_MEM64: + /* MMIO address space decoding changed? */ + if (changed & PCIM_CMD_MEMEN) { + if (new & PCIM_CMD_MEMEN) + register_bar(pi, i); + else + unregister_bar(pi, i); + } + break; + default: + assert(0); + } + } + + /* + * If INTx has been unmasked and is pending, assert the + * interrupt. + */ + pci_lintr_update(pi); +} + static void pci_emul_cmdsts_write(struct pci_devinst *pi, int coff, uint32_t new, int bytes) { - int i, rshift; - uint32_t cmd, cmd2, changed, old, readonly; + int rshift; + uint32_t cmd, old, readonly; cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); /* stash old value */ @@ -1702,47 +1755,7 @@ pci_emul_cmdsts_write(struct pci_devinst *pi, int coff, uint32_t new, int bytes) new |= (old & readonly); CFGWRITE(pi, coff, new, bytes); /* update config */ - cmd2 = pci_get_cfgdata16(pi, PCIR_COMMAND); /* get updated value */ - changed = cmd ^ cmd2; - - /* - * If the MMIO or I/O address space decoding has changed then - * register/unregister all BARs that decode that address space. - */ - for (i = 0; i <= PCI_BARMAX; i++) { - switch (pi->pi_bar[i].type) { - case PCIBAR_NONE: - case PCIBAR_MEMHI64: - break; - case PCIBAR_IO: - /* I/O address space decoding changed? */ - if (changed & PCIM_CMD_PORTEN) { - if (porten(pi)) - register_bar(pi, i); - else - unregister_bar(pi, i); - } - break; - case PCIBAR_MEM32: - case PCIBAR_MEM64: - /* MMIO address space decoding changed? */ - if (changed & PCIM_CMD_MEMEN) { - if (memen(pi)) - register_bar(pi, i); - else - unregister_bar(pi, i); - } - break; - default: - assert(0); - } - } - - /* - * If INTx has been unmasked and is pending, assert the - * interrupt. - */ - pci_lintr_update(pi); + pci_emul_cmd_changed(pi, cmd); } static void diff --git a/usr.sbin/bhyve/pci_emul.h b/usr.sbin/bhyve/pci_emul.h index 853badaadbe6..fba2e8845af8 100644 --- a/usr.sbin/bhyve/pci_emul.h +++ b/usr.sbin/bhyve/pci_emul.h @@ -223,6 +223,7 @@ int pci_emul_alloc_pbar(struct pci_devinst *pdi, int idx, uint64_t hostbase, enum pcibar_type type, uint64_t size); int pci_emul_add_msicap(struct pci_devinst *pi, int msgnum); int pci_emul_add_pciecap(struct pci_devinst *pi, int pcie_device_type); +void pci_emul_cmd_changed(struct pci_devinst *pi, uint16_t old); void pci_generate_msi(struct pci_devinst *pi, int msgnum); void pci_generate_msix(struct pci_devinst *pi, int msgnum); void pci_lintr_assert(struct pci_devinst *pi); diff --git a/usr.sbin/bhyve/pci_passthru.c b/usr.sbin/bhyve/pci_passthru.c index 785da84244df..852643e9d779 100644 --- a/usr.sbin/bhyve/pci_passthru.c +++ b/usr.sbin/bhyve/pci_passthru.c @@ -639,6 +639,9 @@ cfginit(struct vmctx *ctx, struct pci_devinst *pi, int bus, int slot, int func) goto done; } + pci_set_cfgdata16(pi, PCIR_COMMAND, read_config(&sc->psc_sel, + PCIR_COMMAND, 2)); + error = 0; /* success */ done: return (error); @@ -815,6 +818,7 @@ passthru_cfgwrite(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, { int error, msix_table_entries, i; struct passthru_softc *sc; + uint16_t cmd_old; sc = pi->pi_arg; @@ -871,6 +875,14 @@ passthru_cfgwrite(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, #endif write_config(&sc->psc_sel, coff, bytes, val); + if (coff == PCIR_COMMAND) { + cmd_old = pci_get_cfgdata16(pi, PCIR_COMMAND); + if (bytes == 1) + pci_set_cfgdata8(pi, PCIR_COMMAND, val); + else if (bytes == 2) + pci_set_cfgdata16(pi, PCIR_COMMAND, val); + pci_emul_cmd_changed(pi, cmd_old); + } return (0); }