@@ -39,31 +39,42 @@ 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,
+ const unsigned int caps[], unsigned int n,
+ unsigned int *ttl)
{
- u8 id;
- int ttl = 48;
-
- while ( ttl-- )
+ while ( (*ttl)-- )
{
+ unsigned int id, i;
+
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 )
- return pos;
+ for ( i = 0; i < n; i++ )
+ {
+ if ( id == caps[i] )
+ 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, &cap, 1, &ttl) & ~3;
+}
+
/**
* pci_find_ext_capability - Find an extended capability
* @sbdf: PCI device to query
@@ -18,6 +18,7 @@
*/
#include <xen/iocap.h>
+#include <xen/lib.h>
#include <xen/sched.h>
#include <xen/softirq.h>
#include <xen/vpci.h>
@@ -513,7 +514,7 @@ static void cf_check rom_write(
rom->addr = val & PCI_ROM_ADDRESS_MASK;
}
-static int cf_check init_bars(struct pci_dev *pdev)
+static int cf_check init_header(struct pci_dev *pdev)
{
uint16_t cmd;
uint64_t addr, size;
@@ -545,6 +546,68 @@ 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 )
+ {
+ /* Only expose capabilities to the guest that vPCI can handle. */
+ unsigned int next, ttl = 48;
+ static const unsigned int supported_caps[] = {
+ PCI_CAP_ID_MSI,
+ PCI_CAP_ID_MSIX,
+ };
+
+ next = pci_find_next_cap_ttl(pdev->sbdf, PCI_CAPABILITY_LIST,
+ supported_caps,
+ ARRAY_SIZE(supported_caps), &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,
+ supported_caps,
+ ARRAY_SIZE(supported_caps), &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 read as zero, write ignore */
+ rc = vpci_add_register(pdev->vpci, vpci_read_val, NULL, 0x100, 4,
+ (void *)0);
+ if ( rc )
+ return rc;
+ }
+
/* Utilize rsvdp_mask to hide PCI_STATUS_CAP_LIST from the guest. */
rc = vpci_add_register_mask(pdev->vpci, vpci_hw_read16, vpci_hw_write16,
PCI_STATUS, 2, NULL,
@@ -642,7 +705,7 @@ static int cf_check init_bars(struct pci_dev *pdev)
return (cmd & PCI_COMMAND_MEMORY) ? modify_bars(pdev, cmd, false) : 0;
}
-REGISTER_VPCI_INIT(init_bars, VPCI_PRIORITY_MIDDLE);
+REGISTER_VPCI_INIT(init_header, VPCI_PRIORITY_MIDDLE);
/*
* Local variables:
@@ -137,6 +137,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)
{
@@ -205,6 +205,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,
+ const unsigned int caps[], unsigned int n,
+ 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);
@@ -57,7 +57,13 @@ 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);
+/* Helper to return the value passed in 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(