From c049c5c965cd9529d44eb163fb331c14a0d9c45c Mon Sep 17 00:00:00 2001 From: Li Fei1 Date: Wed, 6 Nov 2019 21:39:18 +0800 Subject: [PATCH] hv: vpci: reshuffle pci_bar structure The current code declare pci_bar structure following the PCI bar spec. However, we could not tell whether the value in virtual BAR configuration space is valid base address base on current pci_bar structure. We need to add more fields which are duplicated instances of the vBAR information. Basides these fields which will added, bar_base_mapped is another duplicated instance of the vBAR information. This patch try to reshuffle the pci_bar structure to declare pci_bar structure following the software implement benefit not the PCI bar spec. Tracked-On: #3475 Signed-off-by: Li Fei1 --- hypervisor/dm/vpci/pci_pt.c | 227 ++++++++------------------------- hypervisor/dm/vpci/vdev.c | 44 +++++++ hypervisor/dm/vpci/vpci.c | 5 +- hypervisor/dm/vpci/vpci_priv.h | 4 +- hypervisor/include/dm/vpci.h | 16 ++- hypervisor/include/hw/pci.h | 61 +-------- 6 files changed, 121 insertions(+), 236 deletions(-) diff --git a/hypervisor/dm/vpci/pci_pt.c b/hypervisor/dm/vpci/pci_pt.c index c26accca8..f09f5f913 100644 --- a/hypervisor/dm/vpci/pci_pt.c +++ b/hypervisor/dm/vpci/pci_pt.c @@ -32,68 +32,6 @@ #include #include "vpci_priv.h" -/** - * @brief get bar's full base address in 64-bit - * @pre (pci_get_bar_type(bars[idx].reg.value) == PCIBAR_MEM64) ? ((idx + 1U) < nr_bars) : (idx < nr_bars) - * For 64-bit MMIO bar, its lower 32-bits base address and upper 32-bits base are combined - * into one 64-bit base address - */ -static uint64_t pci_bar_2_bar_base(const struct pci_bar *bars, uint32_t nr_bars, uint32_t idx) -{ - uint64_t base = 0UL; - uint64_t tmp; - const struct pci_bar *bar; - enum pci_bar_type type; - - bar = &bars[idx]; - type = pci_get_bar_type(bar->reg.value); - switch (type) { - case PCIBAR_IO_SPACE: - /* IO bar, BITS 31-2 = base address, 4-byte aligned */ - base = (uint64_t)(bar->reg.bits.io.base); - base <<= 2U; - break; - - case PCIBAR_MEM32: - base = (uint64_t)(bar->reg.bits.mem.base); - base <<= 4U; - break; - - case PCIBAR_MEM64: - if ((idx + 1U) < nr_bars) { - const struct pci_bar *next_bar = &bars[idx + 1U]; - - /* Upper 32-bit of 64-bit bar */ - base = (uint64_t)(next_bar->reg.value); - base <<= 32U; - - /* Lower 32-bit of a 64-bit bar (BITS 31-4 = base address, 16-byte aligned) */ - tmp = (uint64_t)(bar->reg.bits.mem.base); - tmp <<= 4U; - - base |= tmp; - } - break; - - default: - /* Nothing to do */ - break; - } - - return base; -} - -/** - * @brief get vbar's full base address in 64-bit - * For 64-bit MMIO bar, its lower 32-bits base address and upper 32-bits base are combined - * into one 64-bit base address - * @pre vdev != NULL - */ -static uint64_t get_vbar_base(const struct pci_vdev *vdev, uint32_t idx) -{ - return pci_bar_2_bar_base(&vdev->bar[0], vdev->nr_bars, idx); -} - /** * @pre vdev != NULL */ @@ -120,11 +58,11 @@ static void vdev_pt_unmap_mem_vbar(struct pci_vdev *vdev, uint32_t idx) vbar = &vdev->bar[idx]; - if (vdev->bar_base_mapped[idx] != 0UL) { + if (vbar->base != 0UL) { ept_del_mr(vm, (uint64_t *)(vm->arch_vm.nworld_eptp), - vdev->bar_base_mapped[idx], /* GPA (old vbar) */ + vbar->base, /* GPA (old vbar) */ vbar->size); - vdev->bar_base_mapped[idx] = 0UL; + vbar->base = 0UL; } is_msix_table_bar = (has_msix_cap(vdev) && (idx == vdev->msix.table_bar)); @@ -169,7 +107,7 @@ static void vdev_pt_map_mem_vbar(struct pci_vdev *vdev, uint32_t idx) vbar = &vdev->bar[idx]; - vbar_base = get_vbar_base(vdev, idx); + vbar_base = pci_vdev_get_bar_base(vdev, idx); if (vbar_base != 0UL) { if (ept_is_mr_valid(vm, vbar_base, vbar->size)) { uint64_t hpa = gpa2hpa(vdev->vpci->vm, vbar_base); @@ -183,7 +121,7 @@ static void vdev_pt_map_mem_vbar(struct pci_vdev *vdev, uint32_t idx) EPT_WR | EPT_RD | EPT_UNCACHED); } /* Remember the previously mapped MMIO vbar */ - vdev->bar_base_mapped[idx] = vbar_base; + vbar->base = vbar_base; } else { pr_fatal("%s, %x:%x.%x set invalid bar[%d] address: 0x%lx\n", __func__, vdev->bdf.bits.b, vdev->bdf.bits.d, vdev->bdf.bits.f, idx, vbar_base); @@ -195,8 +133,8 @@ static void vdev_pt_map_mem_vbar(struct pci_vdev *vdev, uint32_t idx) uint64_t addr_hi, addr_lo; struct pci_msix *msix = &vdev->msix; - if (vdev->bar_base_mapped[idx] != 0UL) { - addr_lo = vdev->bar_base_mapped[idx] + msix->table_offset; + if (vbar->base != 0UL) { + addr_lo = vbar->base + msix->table_offset; addr_hi = addr_lo + (msix->table_count * MSIX_TABLE_ENTRY_SIZE); addr_lo = round_page_down(addr_lo); @@ -204,7 +142,7 @@ static void vdev_pt_map_mem_vbar(struct pci_vdev *vdev, uint32_t idx) register_mmio_emulation_handler(vm, vmsix_table_mmio_access_handler, addr_lo, addr_hi, vdev); ept_del_mr(vm, (uint64_t *)vm->arch_vm.nworld_eptp, addr_lo, addr_hi - addr_lo); - msix->mmio_gpa = vdev->bar_base_mapped[idx]; + msix->mmio_gpa = vbar->base; } } } @@ -220,11 +158,11 @@ static void vdev_pt_allow_io_vbar(struct pci_vdev *vdev, uint32_t idx) /* For SOS, all port IO access is allowed by default, so skip SOS here */ if (!is_sos_vm(vdev->vpci->vm)) { struct pci_bar *vbar = &vdev->bar[idx]; - uint64_t vbar_base = get_vbar_base(vdev, idx); /* vbar (gpa) */ + uint64_t vbar_base = pci_vdev_get_bar_base(vdev, idx); /* vbar (gpa) */ if (vbar_base != 0UL) { allow_guest_pio_access(vdev->vpci->vm, (uint16_t)vbar_base, (uint32_t)(vbar->size)); /* Remember the previously allowed IO vbar base */ - vdev->bar_base_mapped[idx] = vbar_base; + vbar->base = vbar_base; } } } @@ -240,117 +178,56 @@ static void vdev_pt_deny_io_vbar(struct pci_vdev *vdev, uint32_t idx) /* For SOS, all port IO access is allowed by default, so skip SOS here */ if (!is_sos_vm(vdev->vpci->vm)) { struct pci_bar *vbar = &vdev->bar[idx]; - if (vdev->bar_base_mapped[idx] != 0UL) { - deny_guest_pio_access(vdev->vpci->vm, (uint16_t)(vdev->bar_base_mapped[idx]), - (uint32_t)(vbar->size)); - vdev->bar_base_mapped[idx] = 0UL; + if (vbar->base != 0UL) { + deny_guest_pio_access(vdev->vpci->vm, (uint16_t)(vbar->base), (uint32_t)(vbar->size)); + vbar->base = 0UL; } } } -/** - * @brief Set the base address portion of the vbar base address register (32-bit) - * base: bar value with flags portion masked off - * @pre vbar != NULL - */ -static void set_vbar_base(struct pci_bar *vbar, uint32_t base) -{ - union pci_bar_reg bar_reg; - - bar_reg.value = base; - - if (vbar->is_64bit_high) { - /* Upper 32-bit of a 64-bit bar does not have the flags portion */ - vbar->reg.value = bar_reg.value; - } else if (vbar->reg.bits.io.is_io == 1U) { - /* IO bar, BITS 31-2 = base address, 4-byte aligned */ - vbar->reg.bits.io.base = bar_reg.bits.io.base; - } else { - /* MMIO bar, BITS 31-4 = base address, 16-byte aligned */ - vbar->reg.bits.mem.base = bar_reg.bits.mem.base; - } -} - /** * @pre vdev != NULL */ -static void vdev_pt_write_vbar(struct pci_vdev *vdev, uint32_t offset, uint32_t val) +void vdev_pt_write_vbar(struct pci_vdev *vdev, uint32_t idx, uint32_t val) { - uint32_t idx; - uint64_t base; - bool bar_update_normal; - struct pci_bar *vbar; + bool update_bar = false; + uint32_t update_idx = idx; + uint32_t offset = pci_bar_offset(idx); + struct pci_bar *vbar = &vdev->bar[idx]; - base = 0UL; - idx = (offset - pci_bar_offset(0U)) >> 2U; - bar_update_normal = (val != (uint32_t)~0U); + switch (vbar->type) { + case PCIBAR_IO_SPACE: + vdev_pt_deny_io_vbar(vdev, update_idx); + if (val != ~0U) { + pci_vdev_write_bar(vdev, idx, val); + vdev_pt_allow_io_vbar(vdev, update_idx); + } else { + pci_vdev_write_cfg_u32(vdev, offset, vbar->mask); + } + break; - vbar = &vdev->bar[idx]; - - if (vbar->is_64bit_high) { - if (idx > 0U) { - uint32_t prev_idx = idx - 1U; - - vdev_pt_unmap_mem_vbar(vdev, prev_idx); - base = git_size_masked_bar_base(vdev->bar[prev_idx].size, ((uint64_t)val) << 32U) >> 32U; - set_vbar_base(vbar, (uint32_t)base); - - if (bar_update_normal) { - vdev_pt_map_mem_vbar(vdev, prev_idx); + case PCIBAR_MEM64HI: + update_idx = idx - 1U; + /* falls through */ + case PCIBAR_MEM32: + update_bar = true; + /* falls through */ + case PCIBAR_MEM64: + vdev_pt_unmap_mem_vbar(vdev, update_idx); + if (val != ~0U) { + pci_vdev_write_bar(vdev, idx, val); + if (update_bar) { + vdev_pt_map_mem_vbar(vdev, update_idx); } } else { - ASSERT(false, "idx for upper 32-bit of the 64-bit bar should be greater than 0!"); + pci_vdev_write_cfg_u32(vdev, offset, vbar->mask); } - } else { - enum pci_bar_type type = pci_get_bar_type(vbar->reg.value); + break; - switch (type) { - case PCIBAR_IO_SPACE: - vdev_pt_deny_io_vbar(vdev, idx); - base = git_size_masked_bar_base(vbar->size, (uint64_t)val) & 0xffffUL; - set_vbar_base(vbar, (uint32_t)base); - - if (bar_update_normal) { - vdev_pt_allow_io_vbar(vdev, idx); - } - break; - - case PCIBAR_MEM32: - vdev_pt_unmap_mem_vbar(vdev, idx); - base = git_size_masked_bar_base(vbar->size, (uint64_t)val); - set_vbar_base(vbar, (uint32_t)base); - - if (bar_update_normal) { - vdev_pt_map_mem_vbar(vdev, idx); - } - break; - - case PCIBAR_MEM64: - vdev_pt_unmap_mem_vbar(vdev, idx); - base = git_size_masked_bar_base(vbar->size, (uint64_t)val); - set_vbar_base(vbar, (uint32_t)base); - break; - - default: - /* Nothing to do */ - break; - } - } - - /* Write the vbar value to corresponding virtualized vbar reg */ - pci_vdev_write_cfg_u32(vdev, offset, vbar->reg.value); -} - -/** - * @pre vdev != NULL - * bar write access must be 4 bytes and offset must also be 4 bytes aligned, it will be dropped otherwise - */ -void vdev_pt_write_cfg(struct pci_vdev *vdev, uint32_t offset, uint32_t bytes, uint32_t val) -{ - /* bar write access must be 4 bytes and offset must also be 4 bytes aligned */ - if ((bytes == 4U) && ((offset & 0x3U) == 0U)) { - vdev_pt_write_vbar(vdev, offset, val); + default: + /* Nothing to do */ + break; } } @@ -414,8 +291,10 @@ void init_vdev_pt(struct pci_vdev *vdev) size32 = pci_pdev_read_cfg(pbdf, offset, 4U); pci_pdev_write_cfg(pbdf, offset, 4U, lo); + vbar->type = type; + vbar->mask = size32 & mask; + vbar->fixed = lo & (~mask); vbar->size = (uint64_t)size32 & mask; - vbar->reg.value = lo; if (is_prelaunched_vm(vdev->vpci->vm)) { lo = (uint32_t)vdev->pci_dev_config->vbar_base[idx]; @@ -433,20 +312,20 @@ void init_vdev_pt(struct pci_vdev *vdev) vbar->size = round_page_up(vbar->size); vbar = &vdev->bar[idx]; - vbar->is_64bit_high = true; - vbar->reg.value = hi; + vbar->mask = size32; + vbar->type = PCIBAR_MEM64HI; if (is_prelaunched_vm(vdev->vpci->vm)) { hi = (uint32_t)(vdev->pci_dev_config->vbar_base[idx - 1U] >> 32U); } - vdev_pt_write_vbar(vdev, pci_bar_offset(idx - 1U), lo); - vdev_pt_write_vbar(vdev, pci_bar_offset(idx), hi); + vdev_pt_write_vbar(vdev, idx - 1U, lo); + vdev_pt_write_vbar(vdev, idx, hi); } else { vbar->size = vbar->size & ~(vbar->size - 1UL); if (type == PCIBAR_MEM32) { vbar->size = round_page_up(vbar->size); } - vdev_pt_write_vbar(vdev, pci_bar_offset(idx), lo); + vdev_pt_write_vbar(vdev, idx, lo); } } } diff --git a/hypervisor/dm/vpci/vdev.c b/hypervisor/dm/vpci/vdev.c index 383997106..bc6fa875b 100644 --- a/hypervisor/dm/vpci/vdev.c +++ b/hypervisor/dm/vpci/vdev.c @@ -91,3 +91,47 @@ struct pci_vdev *pci_find_vdev(struct acrn_vpci *vpci, union pci_bdf vbdf) return vdev; } + +void pci_vdev_write_bar(struct pci_vdev *vdev, uint32_t idx, uint32_t val) +{ + struct pci_bar *vbar; + uint32_t bar, offset; + + vbar = &vdev->bar[idx]; + bar = val & vbar->mask; + bar |= vbar->fixed; + offset = pci_bar_offset(idx); + pci_vdev_write_cfg_u32(vdev, offset, bar); +} + +uint64_t pci_vdev_get_bar_base(const struct pci_vdev *vdev, uint32_t idx) +{ + const struct pci_bar *vbar; + enum pci_bar_type type; + uint64_t base = 0UL; + uint32_t lo, hi, offset; + + vbar = &vdev->bar[idx]; + offset = pci_bar_offset(idx); + lo = pci_vdev_read_cfg_u32(vdev, offset); + if ((vbar->type != PCIBAR_NONE) && (lo != ~0U)) { + type = vbar->type; + base = lo & vbar->mask; + + if (vbar->type == PCIBAR_MEM64) { + vbar = &vdev->bar[idx + 1U]; + hi = pci_vdev_read_cfg_u32(vdev, offset + 4U); + if (hi != ~0U) { + hi &= vbar->mask; + base |= ((uint64_t)hi << 32U); + } else { + base = 0UL; + } + } + if (type == PCIBAR_IO_SPACE) { + base &= 0xffffUL; + } + } + + return base; +} diff --git a/hypervisor/dm/vpci/vpci.c b/hypervisor/dm/vpci/vpci.c index d4b34115f..147b46b24 100644 --- a/hypervisor/dm/vpci/vpci.c +++ b/hypervisor/dm/vpci/vpci.c @@ -329,7 +329,10 @@ static int32_t vpci_write_pt_dev_cfg(struct pci_vdev *vdev, uint32_t offset, uint32_t bytes, uint32_t val) { if (vbar_access(vdev, offset)) { - vdev_pt_write_cfg(vdev, offset, bytes, val); + /* bar write access must be 4 bytes and offset must also be 4 bytes aligned */ + if ((bytes == 4U) && ((offset & 0x3U) == 0U)) { + vdev_pt_write_vbar(vdev, pci_bar_index(offset), val); + } } else if (msicap_access(vdev, offset)) { vmsi_write_cfg(vdev, offset, bytes, val); } else if (msixcap_access(vdev, offset)) { diff --git a/hypervisor/dm/vpci/vpci_priv.h b/hypervisor/dm/vpci/vpci_priv.h index 073b0879c..4b0eb858d 100644 --- a/hypervisor/dm/vpci/vpci_priv.h +++ b/hypervisor/dm/vpci/vpci_priv.h @@ -127,7 +127,7 @@ static inline bool msicap_access(const struct pci_vdev *vdev, uint32_t offset) void init_vdev_pt(struct pci_vdev *vdev); void vdev_pt_read_cfg(const struct pci_vdev *vdev, uint32_t offset, uint32_t bytes, uint32_t *val); -void vdev_pt_write_cfg(struct pci_vdev *vdev, uint32_t offset, uint32_t bytes, uint32_t val); +void vdev_pt_write_vbar(struct pci_vdev *vdev, uint32_t idx, uint32_t val); void init_vmsi(struct pci_vdev *vdev); void vmsi_read_cfg(const struct pci_vdev *vdev, uint32_t offset, uint32_t bytes, uint32_t *val); @@ -145,4 +145,6 @@ void pci_vdev_write_cfg(struct pci_vdev *vdev, uint32_t offset, uint32_t bytes, struct pci_vdev *pci_find_vdev(struct acrn_vpci *vpci, union pci_bdf vbdf); +void pci_vdev_write_bar(struct pci_vdev *vdev, uint32_t idx, uint32_t val); +uint64_t pci_vdev_get_bar_base(const struct pci_vdev *vdev, uint32_t idx); #endif /* VPCI_PRIV_H_ */ diff --git a/hypervisor/include/dm/vpci.h b/hypervisor/include/dm/vpci.h index 0e439bfba..4f0cafb38 100644 --- a/hypervisor/include/dm/vpci.h +++ b/hypervisor/include/dm/vpci.h @@ -34,6 +34,15 @@ #include +struct pci_bar { + enum pci_bar_type type; + uint64_t size; /* BAR size */ + uint64_t base; /* BAR guest physical address */ + uint64_t base_hpa; /* BAR host physical address */ + uint32_t fixed; /* BAR fix memory type encoding */ + uint32_t mask; /* BAR size mask */ +}; + struct msix_table_entry { uint64_t addr; uint32_t data; @@ -61,8 +70,8 @@ struct pci_msix { union pci_cfgdata { uint8_t data_8[PCI_REGMAX + 1U]; - uint16_t data_16[(PCI_REGMAX + 1U) >> 2U]; - uint32_t data_32[(PCI_REGMAX + 1U) >> 4U]; + uint16_t data_16[(PCI_REGMAX + 1U) >> 1U]; + uint32_t data_32[(PCI_REGMAX + 1U) >> 2U]; }; struct pci_vdev; @@ -86,9 +95,6 @@ struct pci_vdev { uint32_t nr_bars; /* 6 for normal device, 2 for bridge, 1 for cardbus */ struct pci_bar bar[PCI_BAR_COUNT]; - /* Remember the previously mapped/registered vbar base for undo purpose */ - uint64_t bar_base_mapped[PCI_BAR_COUNT]; - struct pci_msi msi; struct pci_msix msix; diff --git a/hypervisor/include/hw/pci.h b/hypervisor/include/hw/pci.h index ff02a2fa7..1a6ca6d7b 100644 --- a/hypervisor/include/hw/pci.h +++ b/hypervisor/include/hw/pci.h @@ -151,47 +151,7 @@ enum pci_bar_type { PCIBAR_IO_SPACE, PCIBAR_MEM32, 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 { - /* Base Address Register */ - union pci_bar_reg reg; - uint64_t size; - uint64_t base_hpa; - bool is_64bit_high; /* true if this is the upper 32-bit of a 64-bit bar */ + PCIBAR_MEM64HI, }; /* Basic MSIX capability info */ @@ -221,6 +181,11 @@ static inline uint32_t pci_bar_offset(uint32_t idx) return PCIR_BARS + (idx << 2U); } +static inline uint32_t pci_bar_index(uint32_t offset) +{ + return (offset - PCIR_BARS) >> 2U; +} + static inline bool is_bar_offset(uint32_t nr_bars, uint32_t offset) { bool ret; @@ -244,7 +209,6 @@ static inline enum pci_bar_type pci_get_bar_type(uint32_t val) } else { switch (val & PCIM_BAR_MEM_TYPE) { case PCIM_BAR_MEM_32: - case PCIM_BAR_MEM_1MB: type = PCIBAR_MEM32; break; @@ -261,19 +225,6 @@ static inline enum pci_bar_type pci_get_bar_type(uint32_t val) return type; } -/** - * Given bar size and raw bar value, return bar base address by masking off its lower flag bits - * size/val: all in 64-bit values to accommodate 64-bit MMIO bar size masking - */ -static inline uint64_t git_size_masked_bar_base(uint64_t size, uint64_t val) -{ - uint64_t mask; - - mask = ~(size - 1UL); - - return (mask & val); -} - static inline bool bdf_is_equal(union pci_bdf a, union pci_bdf b) { return (a.value == b.value);