From ec5b90f11eabd44f46f5baed054fcf0b6e768b15 Mon Sep 17 00:00:00 2001 From: Zide Chen Date: Wed, 24 Oct 2018 12:11:01 -0700 Subject: [PATCH] hv: implement PCI bus scan function - It starts from scaning bus 0 and scan other buses only if it is referred as a Secondary Bus by a PCI-to-PCI bridge. - Skip to next device if function 0 is not implemented. - Don't enumerate function 1-7 if a device is not a multi-function device. Tracked-On: #1568 Signed-off-by: dongshen Signed-off-by: Zide Chen Reviewed-by: Zhao Yakui Acked-by: Anthony Xu --- hypervisor/dm/hw/pci.c | 63 +++++++++++++++++++++++++++++++++++++ hypervisor/include/dm/pci.h | 4 +++ 2 files changed, 67 insertions(+) diff --git a/hypervisor/dm/hw/pci.c b/hypervisor/dm/hw/pci.c index f18f93398..ed41d77dc 100644 --- a/hypervisor/dm/hw/pci.c +++ b/hypervisor/dm/hw/pci.c @@ -98,3 +98,66 @@ void pci_pdev_write_cfg(union pci_bdf bdf, uint32_t offset, uint32_t bytes, uint } spinlock_release(&pci_device_lock); } + +#define BUS_SCAN_SKIP 0U +#define BUS_SCAN_PENDING 1U +#define BUS_SCAN_COMPLETE 2U +void pci_scan_bus(pci_enumeration_cb cb_func, void *cb_data) +{ + union pci_bdf pbdf; + uint8_t hdr_type, secondary_bus; + uint32_t bus, dev, func, val; + uint8_t bus_to_scan[PCI_BUSMAX + 1] = { BUS_SCAN_SKIP }; + + /* start from bus 0 */ + bus_to_scan[0U] = BUS_SCAN_PENDING; + + for (bus = 0U; bus <= PCI_BUSMAX; bus++) { + if (bus_to_scan[bus] != BUS_SCAN_PENDING) { + continue; + } + + bus_to_scan[bus] = BUS_SCAN_COMPLETE; + pbdf.bits.b = bus; + + for (dev = 0U; dev <= PCI_SLOTMAX; dev++) { + pbdf.bits.d = dev; + + for (func = 0U; func <= PCI_FUNCMAX; func++) { + pbdf.bits.f = func; + val = pci_pdev_read_cfg(pbdf, PCIR_VENDOR, 4U); + + if ((val == 0xFFFFFFFFU) || (val == 0x0U)) { + /* If function 0 is not implemented, skip to next device */ + if (func == 0U) { + break; + } + + /* continue scan next function */ + continue; + } + + if (cb_func != NULL) { + cb_func(pbdf.value, cb_data); + } + + hdr_type = (uint8_t)pci_pdev_read_cfg(pbdf, PCIR_HDRTYPE, 1U); + if ((hdr_type & PCIM_HDRTYPE) == PCIM_HDRTYPE_BRIDGE) { + + /* Secondary bus to be scanned */ + secondary_bus = (uint8_t)pci_pdev_read_cfg(pbdf, PCIR_SECBUS_1, 1U); + if (bus_to_scan[secondary_bus] != BUS_SCAN_SKIP) { + pr_err("%s, bus %d may be downstream of different PCI bridges", secondary_bus); + } else { + bus_to_scan[secondary_bus] = BUS_SCAN_PENDING; + } + } + + /* skip if it doesn't have multiple functions */ + if ((hdr_type & PCIM_MFDEV) == 0U) { + break; + } + } + } + } +} diff --git a/hypervisor/include/dm/pci.h b/hypervisor/include/dm/pci.h index 08b20382f..44b189e5b 100644 --- a/hypervisor/include/dm/pci.h +++ b/hypervisor/include/dm/pci.h @@ -142,6 +142,8 @@ enum pci_bar_type { PCIBAR_MEM64, }; +typedef void (*pci_enumeration_cb)(uint16_t pbdf, void *data); + static inline uint32_t pci_bar_offset(uint32_t idx) { return PCIR_BARS + (idx << 2U); @@ -160,4 +162,6 @@ static inline bool pci_bar_access(uint32_t offset) uint32_t pci_pdev_read_cfg(union pci_bdf bdf, uint32_t offset, uint32_t bytes); void pci_pdev_write_cfg(union pci_bdf bdf, uint32_t offset, uint32_t bytes, uint32_t val); +void pci_scan_bus(pci_enumeration_cb cb, void *data); + #endif /* PCI_H_ */