@@ -39,31 +39,39 @@ unsigned int pci_find_cap_offset(pci_sbdf_t sbdf, unsigned int cap)
return 0;
}
-unsigned int pci_find_next_cap(pci_sbdf_t sbdf, unsigned int pos,
- unsigned int cap)
+unsigned int pci_find_next_cap_ttl(pci_sbdf_t sbdf, unsigned int pos,
+ bool (*is_match)(unsigned int),
+ unsigned int cap, unsigned int *ttl)
{
- u8 id;
- int ttl = 48;
+ unsigned int id;
- while ( ttl-- )
+ while ( (*ttl)-- )
{
pos = pci_conf_read8(sbdf, pos);
if ( pos < 0x40 )
break;
- pos &= ~3;
- id = pci_conf_read8(sbdf, pos + PCI_CAP_LIST_ID);
+ id = pci_conf_read8(sbdf, (pos & ~3) + PCI_CAP_LIST_ID);
if ( id == 0xff )
break;
- if ( id == cap )
+ if ( (is_match && is_match(id)) || (!is_match && id == cap) )
return pos;
- pos += PCI_CAP_LIST_NEXT;
+ pos = (pos & ~3) + PCI_CAP_LIST_NEXT;
}
+
return 0;
}
+unsigned int pci_find_next_cap(pci_sbdf_t sbdf, unsigned int pos,
+ unsigned int cap)
+{
+ unsigned int ttl = 48;
+
+ return pci_find_next_cap_ttl(sbdf, pos, NULL, cap, &ttl) & ~3;
+}
+
/**
* pci_find_ext_capability - Find an extended capability
* @sbdf: PCI device to query
@@ -513,6 +513,18 @@ static void cf_check rom_write(
rom->addr = val & PCI_ROM_ADDRESS_MASK;
}
+static bool cf_check vpci_cap_supported(unsigned int id)
+{
+ switch ( id )
+ {
+ case PCI_CAP_ID_MSI:
+ case PCI_CAP_ID_MSIX:
+ return true;
+ default:
+ return false;
+ }
+}
+
static int cf_check init_bars(struct pci_dev *pdev)
{
uint16_t cmd;
@@ -545,6 +557,70 @@ static int cf_check init_bars(struct pci_dev *pdev)
if ( rc )
return rc;
+ if ( !is_hardware_domain(pdev->domain) )
+ {
+ if ( !(pci_conf_read16(pdev->sbdf, PCI_STATUS) & PCI_STATUS_CAP_LIST) )
+ {
+ /* RAZ/WI */
+ rc = vpci_add_register(pdev->vpci, vpci_read_val, NULL,
+ PCI_CAPABILITY_LIST, 1, (void *)0);
+ if ( rc )
+ return rc;
+ }
+ else
+ {
+ /* Only expose capabilities to the guest that vPCI can handle. */
+ unsigned int next, ttl = 48;
+
+ next = pci_find_next_cap_ttl(pdev->sbdf, PCI_CAPABILITY_LIST,
+ vpci_cap_supported, 0, &ttl);
+
+ rc = vpci_add_register(pdev->vpci, vpci_read_val, NULL,
+ PCI_CAPABILITY_LIST, 1,
+ (void *)(uintptr_t)next);
+ if ( rc )
+ return rc;
+
+ next &= ~3;
+
+ if ( !next )
+ /*
+ * If we don't have any supported capabilities to expose to the
+ * guest, mask the PCI_STATUS_CAP_LIST bit in the status
+ * register.
+ */
+ mask_cap_list = true;
+
+ while ( next && ttl )
+ {
+ unsigned int pos = next;
+
+ next = pci_find_next_cap_ttl(pdev->sbdf,
+ pos + PCI_CAP_LIST_NEXT,
+ vpci_cap_supported, 0, &ttl);
+
+ rc = vpci_add_register(pdev->vpci, vpci_hw_read8, NULL,
+ pos + PCI_CAP_LIST_ID, 1, NULL);
+ if ( rc )
+ return rc;
+
+ rc = vpci_add_register(pdev->vpci, vpci_read_val, NULL,
+ pos + PCI_CAP_LIST_NEXT, 1,
+ (void *)(uintptr_t)next);
+ if ( rc )
+ return rc;
+
+ next &= ~3;
+ }
+ }
+
+ /* Extended capabilities RAZ/WI */
+ rc = vpci_add_register(pdev->vpci, vpci_read_val, NULL, 0x100, 4,
+ (void *)0);
+ if ( rc )
+ return rc;
+ }
+
/*
* If mask_cap_list is true, PCI_STATUS_CAP_LIST will be set in both
* rsvdz_mask and ro_mask, and thus will effectively behave as rsvdp
@@ -136,6 +136,18 @@ static void cf_check vpci_ignored_write(
{
}
+uint32_t cf_check vpci_read_val(
+ const struct pci_dev *pdev, unsigned int reg, void *data)
+{
+ return (uintptr_t)data;
+}
+
+uint32_t cf_check vpci_hw_read8(
+ const struct pci_dev *pdev, unsigned int reg, void *data)
+{
+ return pci_conf_read8(pdev->sbdf, reg);
+}
+
uint32_t cf_check vpci_hw_read16(
const struct pci_dev *pdev, unsigned int reg, void *data)
{
@@ -194,6 +194,9 @@ int pci_mmcfg_read(unsigned int seg, unsigned int bus,
int pci_mmcfg_write(unsigned int seg, unsigned int bus,
unsigned int devfn, int reg, int len, u32 value);
unsigned int pci_find_cap_offset(pci_sbdf_t sbdf, unsigned int cap);
+unsigned int pci_find_next_cap_ttl(pci_sbdf_t sbdf, unsigned int pos,
+ bool (*is_match)(unsigned int),
+ unsigned int cap, unsigned int *ttl);
unsigned int pci_find_next_cap(pci_sbdf_t sbdf, unsigned int pos,
unsigned int cap);
unsigned int pci_find_ext_capability(pci_sbdf_t sbdf, unsigned int cap);
@@ -51,7 +51,12 @@ uint32_t vpci_read(pci_sbdf_t sbdf, unsigned int reg, unsigned int size);
void vpci_write(pci_sbdf_t sbdf, unsigned int reg, unsigned int size,
uint32_t data);
+uint32_t cf_check vpci_read_val(
+ const struct pci_dev *pdev, unsigned int reg, void *data);
+
/* Passthrough handlers. */
+uint32_t cf_check vpci_hw_read8(
+ const struct pci_dev *pdev, unsigned int reg, void *data);
uint32_t cf_check vpci_hw_read16(
const struct pci_dev *pdev, unsigned int reg, void *data);
uint32_t cf_check vpci_hw_read32(