Fix pci-passthru MSI issues with OpenBSD guests

- Return 2 x 16-bit registers in the correct byte order
 for a 4-byte read that spans the CMD/STATUS register.
  This reversal was hiding the capabilities-list, which prevented
 the MSI capability from being found for XHCI passthru.

- Reorganize MSI/MSI-x config writes so that a 4-byte write at the
 capability offset would have the read-only portion skipped.
  This prevented MSI interrupts from being enabled.

 Reported and extensively tested by Anatoli (me at anatoli dot ws)

PR:	245392
Reported by:	Anatoli (me at anatoli dot ws)
Reviewed by:	jhb (bhyve)
Approved by:	jhb, bz (mentor)
MFC after:	1 week
Differential Revision:	https://reviews.freebsd.org/D24951
This commit is contained in:
Peter Grehan 2020-05-25 06:25:31 +00:00
parent e3d16bb6a8
commit 2136849868
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=361442
3 changed files with 30 additions and 28 deletions

View File

@ -875,7 +875,7 @@ pci_emul_add_msixcap(struct pci_devinst *pi, int msgnum, int barnum)
sizeof(msixcap)));
}
void
static void
msixcap_cfgwrite(struct pci_devinst *pi, int capoff, int offset,
int bytes, uint32_t val)
{
@ -899,7 +899,7 @@ msixcap_cfgwrite(struct pci_devinst *pi, int capoff, int offset,
CFGWRITE(pi, offset, val, bytes);
}
void
static void
msicap_cfgwrite(struct pci_devinst *pi, int capoff, int offset,
int bytes, uint32_t val)
{
@ -978,30 +978,34 @@ pci_emul_add_pciecap(struct pci_devinst *pi, int type)
/*
* This function assumes that 'coff' is in the capabilities region of the
* config space.
* config space. A capoff parameter of zero will force a search for the
* offset and type.
*/
static void
pci_emul_capwrite(struct pci_devinst *pi, int offset, int bytes, uint32_t val)
void
pci_emul_capwrite(struct pci_devinst *pi, int offset, int bytes, uint32_t val,
uint8_t capoff, int capid)
{
int capid;
uint8_t capoff, nextoff;
uint8_t nextoff;
/* Do not allow un-aligned writes */
if ((offset & (bytes - 1)) != 0)
return;
/* Find the capability that we want to update */
capoff = CAP_START_OFFSET;
while (1) {
nextoff = pci_get_cfgdata8(pi, capoff + 1);
if (nextoff == 0)
break;
if (offset >= capoff && offset < nextoff)
break;
if (capoff == 0) {
/* Find the capability that we want to update */
capoff = CAP_START_OFFSET;
while (1) {
nextoff = pci_get_cfgdata8(pi, capoff + 1);
if (nextoff == 0)
break;
if (offset >= capoff && offset < nextoff)
break;
capoff = nextoff;
capoff = nextoff;
}
assert(offset >= capoff);
capid = pci_get_cfgdata8(pi, capoff);
}
assert(offset >= capoff);
/*
* Capability ID and Next Capability Pointer are readonly.
@ -1018,7 +1022,6 @@ pci_emul_capwrite(struct pci_devinst *pi, int offset, int bytes, uint32_t val)
return;
}
capid = pci_get_cfgdata8(pi, capoff);
switch (capid) {
case PCIY_MSI:
msicap_cfgwrite(pi, capoff, offset, bytes, val);
@ -1897,7 +1900,7 @@ pci_cfgrw(struct vmctx *ctx, int vcpu, int in, int bus, int slot, int func,
pci_set_cfgdata32(pi, coff, bar);
} else if (pci_emul_iscap(pi, coff)) {
pci_emul_capwrite(pi, coff, bytes, *eax);
pci_emul_capwrite(pi, coff, bytes, *eax, 0, 0);
} else if (coff >= PCIR_COMMAND && coff < PCIR_REVID) {
pci_emul_cmdsts_write(pi, coff, *eax, bytes);
} else {

View File

@ -218,10 +218,6 @@ typedef void (*pci_lintr_cb)(int b, int s, int pin, int pirq_pin,
int ioapic_irq, void *arg);
int init_pci(struct vmctx *ctx);
void msicap_cfgwrite(struct pci_devinst *pi, int capoff, int offset,
int bytes, uint32_t val);
void msixcap_cfgwrite(struct pci_devinst *pi, int capoff, int offset,
int bytes, uint32_t val);
void pci_callback(void);
int pci_emul_alloc_bar(struct pci_devinst *pdi, int idx,
enum pcibar_type type, uint64_t size);
@ -229,6 +225,8 @@ 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_capwrite(struct pci_devinst *pi, int offset, int bytes,
uint32_t val, uint8_t capoff, int capid);
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);

View File

@ -811,8 +811,8 @@ passthru_cfgread(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
if (coff == PCIR_COMMAND) {
if (bytes <= 2)
return (-1);
*rv = pci_get_cfgdata16(pi, PCIR_COMMAND) << 16 |
read_config(&sc->psc_sel, PCIR_STATUS, 2);
*rv = read_config(&sc->psc_sel, PCIR_STATUS, 2) << 16 |
pci_get_cfgdata16(pi, PCIR_COMMAND);
return (0);
}
@ -842,8 +842,8 @@ passthru_cfgwrite(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
* MSI capability is emulated
*/
if (msicap_access(sc, coff)) {
msicap_cfgwrite(pi, sc->psc_msi.capoff, coff, bytes, val);
pci_emul_capwrite(pi, coff, bytes, val, sc->psc_msi.capoff,
PCIY_MSI);
error = vm_setup_pptdev_msi(ctx, vcpu, sc->psc_sel.pc_bus,
sc->psc_sel.pc_dev, sc->psc_sel.pc_func,
pi->pi_msi.addr, pi->pi_msi.msg_data,
@ -854,7 +854,8 @@ passthru_cfgwrite(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
}
if (msixcap_access(sc, coff)) {
msixcap_cfgwrite(pi, sc->psc_msix.capoff, coff, bytes, val);
pci_emul_capwrite(pi, coff, bytes, val, sc->psc_msix.capoff,
PCIY_MSIX);
if (pi->pi_msix.enabled) {
msix_table_entries = pi->pi_msix.table_count;
for (i = 0; i < msix_table_entries; i++) {