diff --git a/hypervisor/arch/x86/cpu.c b/hypervisor/arch/x86/cpu.c index fc95f1c25..5b5b372a8 100644 --- a/hypervisor/arch/x86/cpu.c +++ b/hypervisor/arch/x86/cpu.c @@ -195,6 +195,7 @@ void init_cpu_post(uint16_t pcpu_id) timer_init(); setup_notification(); setup_posted_intr_notification(); + init_pci_pdev_list(); /* Start all secondary cores */ startup_paddr = prepare_trampoline(); diff --git a/hypervisor/hw/pci.c b/hypervisor/hw/pci.c index 896541c10..8a1e77011 100644 --- a/hypervisor/hw/pci.c +++ b/hypervisor/hw/pci.c @@ -1,4 +1,6 @@ /* + * Copyright (c) 1997, Stefan Esser + * Copyright (c) 2000, Michael Smith * Copyright (c) 2011 NetApp, Inc. * Copyright (c) 2018 Intel Corporation * All rights reserved. @@ -30,10 +32,10 @@ #include #include -static spinlock_t pci_device_lock = { - .head = 0U, - .tail = 0U -}; +static spinlock_t pci_device_lock; +static uint32_t num_pci_pdev; +static struct pci_pdev pci_pdev_array[CONFIG_MAX_PCI_DEV_NUM]; + static uint32_t pci_pdev_calc_address(union pci_bdf bdf, uint32_t offset) { @@ -145,7 +147,7 @@ void pci_scan_bus(pci_enumeration_cb cb_func, const void *cb_data) pbdf.bits.f = func; val = pci_pdev_read_cfg(pbdf, PCIR_VENDOR, 4U); - if ((val == 0xFFFFFFFFU) || (val == 0x0U)) { + if ((val == 0xFFFFFFFFU) || (val == 0U) || (val == 0xFFFF0000U) || (val == 0xFFFFU)) { /* If function 0 is not implemented, skip to next device */ if (func == 0U) { break; @@ -181,3 +183,219 @@ void pci_scan_bus(pci_enumeration_cb cb_func, const void *cb_data) } } } + +static uint8_t pci_pdev_get_num_bars(uint8_t hdr_type) +{ + uint8_t num_bars = (uint8_t)0U; + + switch (hdr_type & PCIM_HDRTYPE) { + case PCIM_HDRTYPE_NORMAL: + num_bars = (uint8_t)6U; + break; + + case PCIM_HDRTYPE_BRIDGE: + num_bars = (uint8_t)2U; + break; + + case PCIM_HDRTYPE_CARDBUS: + num_bars = (uint8_t)1U; + break; + + default: + /*no actions are required for other cases.*/ + break; + } + + return num_bars; +} + +static enum pci_bar_type pci_pdev_read_bar_type(union pci_bdf bdf, uint8_t idx) +{ + uint32_t bar; + enum pci_bar_type type = PCIBAR_NONE; + + bar = pci_pdev_read_cfg(bdf, pci_bar_offset(idx), 4U); + if ((bar & PCIM_BAR_SPACE) == PCIM_BAR_IO_SPACE) { + type = PCIBAR_IO_SPACE; + } else { + switch (bar & PCIM_BAR_MEM_TYPE) { + case PCIM_BAR_MEM_32: + case PCIM_BAR_MEM_1MB: + type = PCIBAR_MEM32; + break; + + case PCIM_BAR_MEM_64: + type = PCIBAR_MEM64; + break; + + default: + /*no actions are required for other cases.*/ + break; + } + } + + return type; +} + +static uint8_t pci_pdev_read_bar(union pci_bdf bdf, uint8_t idx, struct pci_bar *bar) +{ + uint64_t base, size; + enum pci_bar_type type; + uint32_t bar_lo, bar_hi, val32; + uint32_t bar_base_mask; + + base = 0UL; + size = 0UL; + type = pci_pdev_read_bar_type(bdf, idx); + + if (type != PCIBAR_NONE) { + if (type == PCIBAR_IO_SPACE) { + bar_base_mask = ~0x03U; + } else { + bar_base_mask = ~0x0fU; + } + + bar_lo = pci_pdev_read_cfg(bdf, pci_bar_offset(idx), 4U); + + /* Get the base address */ + base = (uint64_t)bar_lo & bar_base_mask; + + if (base != 0UL) { + if (type == PCIBAR_MEM64) { + bar_hi = pci_pdev_read_cfg(bdf, pci_bar_offset(idx + 1U), 4U); + base |= ((uint64_t)bar_hi << 32U); + } + + /* Sizing the BAR */ + size = 0UL; + if ((type == PCIBAR_MEM64) && (idx < (PCI_BAR_COUNT - 1U))) { + pci_pdev_write_cfg(bdf, pci_bar_offset(idx + 1U), 4U, ~0U); + size = (uint64_t)pci_pdev_read_cfg(bdf, pci_bar_offset(idx + 1U), 4U); + size <<= 32U; + } + + pci_pdev_write_cfg(bdf, pci_bar_offset(idx), 4U, ~0U); + val32 = pci_pdev_read_cfg(bdf, pci_bar_offset(idx), 4U); + size |= ((uint64_t)val32 & bar_base_mask); + + if (size != 0UL) { + size = size & ~(size - 1U); + } + + /* Restore the BAR */ + pci_pdev_write_cfg(bdf, pci_bar_offset(idx), 4U, bar_lo); + + if (type == PCIBAR_MEM64) { + pci_pdev_write_cfg(bdf, pci_bar_offset(idx + 1U), 4U, bar_hi); + } + } + } + + bar->base = base; + bar->size = size; + bar->type = type; + + return (type == PCIBAR_MEM64)?2U:1U; +} + + +/* + * @pre nr_bars <= PCI_BAR_COUNT + */ +static void pci_pdev_read_bars(union pci_bdf bdf, uint8_t nr_bars, struct pci_bar *bar) +{ + uint8_t idx = 0U; + + while (idx < nr_bars) { + idx += pci_pdev_read_bar(bdf, idx, &bar[idx]); + } +} + +static void pci_read_cap(struct pci_pdev *pdev) +{ + uint8_t ptr, cap; + uint32_t msgctrl; + uint32_t len, offset, idx; + uint32_t table_info; + + ptr = (uint8_t)pci_pdev_read_cfg(pdev->bdf, PCIR_CAP_PTR, 1U); + + while ((ptr != 0U) && (ptr != 0xFFU)) { + cap = (uint8_t)pci_pdev_read_cfg(pdev->bdf, ptr + PCICAP_ID, 1U); + + /* Ignore all other Capability IDs for now */ + if ((cap == PCIY_MSI) || (cap == PCIY_MSIX)) { + offset = ptr; + if (cap == PCIY_MSI) { + pdev->msi.capoff = offset; + msgctrl = pci_pdev_read_cfg(pdev->bdf, offset + PCIR_MSI_CTRL, 2U); + len = ((msgctrl & PCIM_MSICTRL_64BIT) != 0U) ? 14U : 10U; + pdev->msi.caplen = len; + + /* Copy MSI capability struct into buffer */ + for (idx = 0U; idx < len; idx++) { + pdev->msi.cap[idx] = (uint8_t)pci_pdev_read_cfg(pdev->bdf, offset + idx, 1U); + } + } else { + pdev->msix.capoff = offset; + pdev->msix.caplen = MSIX_CAPLEN; + len = pdev->msix.caplen; + + msgctrl = pci_pdev_read_cfg(pdev->bdf, pdev->msix.capoff + PCIR_MSIX_CTRL, 2U); + + /* Read Table Offset and Table BIR */ + table_info = pci_pdev_read_cfg(pdev->bdf, pdev->msix.capoff + PCIR_MSIX_TABLE, 4U); + + pdev->msix.table_bar = (uint8_t)(table_info & PCIM_MSIX_BIR_MASK); + + pdev->msix.table_offset = table_info & ~PCIM_MSIX_BIR_MASK; + pdev->msix.table_count = (msgctrl & PCIM_MSIXCTRL_TABLE_SIZE) + 1U; + + /* Copy MSIX capability struct into buffer */ + for (idx = 0U; idx < len; idx++) { + pdev->msix.cap[idx] = (uint8_t)pci_pdev_read_cfg(pdev->bdf, offset + idx, 1U); + } + } + } + + ptr = (uint8_t)pci_pdev_read_cfg(pdev->bdf, ptr + PCICAP_NEXTPTR, 1U); + } +} + +static void fill_pdev(uint16_t pbdf, struct pci_pdev *pdev) +{ + uint8_t hdr_type; + uint8_t nr_bars; + + pdev->bdf.value = pbdf; + + hdr_type = (uint8_t)pci_pdev_read_cfg(pdev->bdf, PCIR_HDRTYPE, 1U); + + nr_bars = pci_pdev_get_num_bars(hdr_type); + + pci_pdev_read_bars(pdev->bdf, nr_bars, &pdev->bar[0]); + + if ((pci_pdev_read_cfg(pdev->bdf, PCIR_STATUS, 2U) & PCIM_STATUS_CAPPRESENT) != 0U) { + pci_read_cap(pdev); + } +} + +static void init_pdev(uint16_t pbdf, __unused const void *cb_data) +{ + static struct pci_pdev *curpdev = NULL; + + if (num_pci_pdev < CONFIG_MAX_PCI_DEV_NUM) { + curpdev = &pci_pdev_array[num_pci_pdev]; + num_pci_pdev++; + + fill_pdev(pbdf, curpdev); + } else { + pr_err("%s, failed to alloc pci_pdev!\n", __func__); + } +} + +void init_pci_pdev_list(void) +{ + /* Build up pdev array */ + pci_scan_bus(init_pdev, NULL); +} diff --git a/hypervisor/include/dm/pci.h b/hypervisor/include/dm/pci.h index 65b60ecd5..3ccb83a18 100644 --- a/hypervisor/include/dm/pci.h +++ b/hypervisor/include/dm/pci.h @@ -1,4 +1,5 @@ /*- +* Copyright (c) 1997, Stefan Esser * Copyright (c) 2011 NetApp, Inc. * Copyright (c) 2018 Intel Corporation * All rights reserved. @@ -59,6 +60,8 @@ #define PCIR_VENDOR 0x00U #define PCIR_DEVICE 0x02U #define PCIR_COMMAND 0x04U +#define PCIM_CMD_PORTEN 0x01U +#define PCIM_CMD_MEMEN 0x02U #define PCIM_CMD_INTxDIS 0x400U #define PCIR_STATUS 0x06U #define PCIM_STATUS_CAPPRESENT 0x0010U @@ -69,6 +72,7 @@ #define PCIM_HDRTYPE 0x7FU #define PCIM_HDRTYPE_NORMAL 0x00U #define PCIM_HDRTYPE_BRIDGE 0x01U +#define PCIM_HDRTYPE_CARDBUS 0x02U #define PCIM_MFDEV 0x80U #define PCIR_BARS 0x10U #define PCIM_BAR_SPACE 0x01U @@ -120,6 +124,7 @@ #define PCIM_MSIX_BIR_MASK 0x7U #define PCIM_MSIX_VCTRL_MASK 0x1U +#define MSI_MAX_CAPLEN 14U #define MSIX_CAPLEN 12U #define MSIX_TABLE_ENTRY_SIZE 16U @@ -134,6 +139,7 @@ union pci_bdf { enum pci_bar_type { PCIBAR_NONE = 0, + PCIBAR_IO_SPACE, PCIBAR_MEM32, PCIBAR_MEM64, }; @@ -144,12 +150,33 @@ struct pci_bar { enum pci_bar_type type; }; +/* Basic MSI capability info */ +struct pci_msi_cap { + uint32_t capoff; + uint32_t caplen; + uint8_t cap[MSI_MAX_CAPLEN]; +}; + +/* Basic MSIX capability info */ +struct pci_msix_cap { + uint32_t capoff; + uint32_t caplen; + uint8_t table_bar; + uint32_t table_offset; + uint32_t table_count; + uint8_t cap[MSIX_CAPLEN]; +}; + struct pci_pdev { /* The bar info of the physical PCI device. */ struct pci_bar bar[PCI_BAR_COUNT]; /* The bus/device/function triple of the physical PCI device. */ union pci_bdf bdf; + + struct pci_msi_cap msi; + + struct pci_msix_cap msix; }; @@ -199,5 +226,7 @@ void pci_pdev_write_cfg(union pci_bdf bdf, uint32_t offset, uint32_t bytes, uint void enable_disable_pci_intx(union pci_bdf bdf, bool enable); void pci_scan_bus(pci_enumeration_cb cb, const void *cb_data); +void init_pci_pdev_list(void); + #endif /* PCI_H_ */