diff --git a/hypervisor/hw/pci.c b/hypervisor/hw/pci.c index 8dcfb4760..6747890dd 100644 --- a/hypervisor/hw/pci.c +++ b/hypervisor/hw/pci.c @@ -212,57 +212,49 @@ static uint8_t pci_pdev_get_num_bars(uint8_t hdr_type) return num_bars; } -static enum pci_bar_type pci_pdev_read_bar_type(union pci_bdf bdf, uint8_t idx) +/* Get the base address of the raw bar value (val) */ +static inline uint32_t pci_pdev_get_bar_base(uint32_t bar_val) { - uint32_t bar; - enum pci_bar_type type = PCIBAR_NONE; + uint32_t base; + union pci_bar_reg reg; - bar = pci_pdev_read_cfg(bdf, pci_bar_offset(idx), 4U); - if ((bar & PCIM_BAR_SPACE) == PCIM_BAR_IO_SPACE) { - type = PCIBAR_IO_SPACE; + /* set raw bar value */ + reg.value = bar_val; + + /* Extract base address portion from the raw bar value */ + if (reg.bits.io.is_io != 0U) { + /* IO bar, BITS 31-2 = base address, 4-byte aligned */ + base = (uint32_t)(reg.bits.io.base); + base <<= 2U; } 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; - } + /* MMIO bar, BITS 31-4 = base address, 16-byte aligned */ + base = (uint32_t)(reg.bits.mem.base); + base <<= 4U; } - return type; + return base; } -static uint8_t pci_pdev_read_bar(union pci_bdf bdf, uint8_t idx, struct pci_bar *bar) +/* + * @pre bar != NULL + */ +static uint32_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; + uint32_t bar_lo, bar_hi, size_lo; + + bar->reg.value = pci_pdev_read_cfg(bdf, pci_bar_offset(idx), 4U); base = 0UL; size = 0UL; - type = pci_pdev_read_bar_type(bdf, idx); + type = pci_get_bar_type(bar->reg.value); bar_hi = 0U; 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; + base = (uint64_t)pci_pdev_get_bar_base(bar_lo); if (type == PCIBAR_MEM64) { bar_hi = pci_pdev_read_cfg(bdf, pci_bar_offset(idx + 1U), 4U); @@ -278,8 +270,8 @@ static uint8_t pci_pdev_read_bar(union pci_bdf bdf, uint8_t idx, struct pci_bar } 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); + size_lo = pci_pdev_read_cfg(bdf, pci_bar_offset(idx), 4U); + size |= (uint64_t)pci_pdev_get_bar_base(size_lo); if (size != 0UL) { size = size & ~(size - 1U); @@ -306,10 +298,18 @@ static uint8_t pci_pdev_read_bar(union pci_bdf bdf, uint8_t idx, struct pci_bar */ static void pci_pdev_read_bars(union pci_bdf bdf, uint8_t nr_bars, struct pci_bar *bar) { - uint8_t idx = 0U; + uint8_t idx = 0U; + uint32_t bar_step; while (idx < nr_bars) { - idx += pci_pdev_read_bar(bdf, idx, &bar[idx]); + bar_step = pci_pdev_read_bar(bdf, idx, &bar[idx]); + if ((bar_step == 2U) && ((idx + 1U) < nr_bars)) { + /* Upper 32-bit of a 64-bit bar: */ + bar[idx + 1U].is_64bit_high = true; + bar[idx + 1U].reg.value = pci_pdev_read_cfg(bdf, pci_bar_offset(idx + 1U), 4U); + } + + idx += bar_step; } } @@ -398,6 +398,9 @@ static void pci_read_cap(struct pci_pdev *pdev, uint8_t hdr_type) } } +/* + * @pre pdev != NULL + */ static void fill_pdev(uint16_t pbdf, struct pci_pdev *pdev) { uint8_t hdr_type; diff --git a/hypervisor/include/hw/pci.h b/hypervisor/include/hw/pci.h index 5fb48e600..e88cc323f 100644 --- a/hypervisor/include/hw/pci.h +++ b/hypervisor/include/hw/pci.h @@ -146,10 +146,46 @@ enum pci_bar_type { PCIBAR_MEM64, }; +/* + * Base Address Register for MMIO, pf=prefetchable, type=0 (32-bit), 1 (<=1MB), 2 (64-bit): + * 31 4 3 2 1 0 + * +----------+--------------+-------------+ + * | Base address |pf| type | 0 | + * +---------------------------------------+ + * + * Base Address Register for IO (R=reserved): + * 31 2 1 0 + * +----------+----------------------------+ + * | Base address | R | 1 | + * +---------------------------------------+ + */ +union pci_bar_reg { + uint32_t value; + + /* Base address + flags portion */ + union { + struct { + uint32_t is_io:1; /* 0 for memory */ + uint32_t type:2; + uint32_t prefetchable:1; + uint32_t base:28; /* BITS 31-4 = base address, 16-byte aligned */ + } mem; + + struct { + uint32_t is_io:1; /* 1 for I/O */ + uint32_t:1; + uint32_t base:30; /* BITS 31-2 = base address, 4-byte aligned */ + } io; + } bits; +}; + struct pci_bar { uint64_t base; + /* Base Address Register */ + union pci_bar_reg reg; uint64_t size; enum pci_bar_type type; + bool is_64bit_high; /* true if this is the upper 32-bit of a 64-bit bar */ }; /* Basic MSI capability info */ @@ -204,6 +240,32 @@ static inline bool pci_bar_access(uint32_t offset) return ret; } +static inline enum pci_bar_type pci_get_bar_type(uint32_t val) +{ + enum pci_bar_type type = PCIBAR_NONE; + + if ((val & PCIM_BAR_SPACE) == PCIM_BAR_IO_SPACE) { + type = PCIBAR_IO_SPACE; + } else { + switch (val & 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 inline uint8_t pci_bus(uint16_t bdf) { return (uint8_t)((bdf >> 8U) & 0xFFU);