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 <fei1.li@intel.com>
This commit is contained in:
Li Fei1 2019-11-06 21:39:18 +08:00 committed by wenlingz
parent f53baadd5a
commit c049c5c965
6 changed files with 121 additions and 236 deletions

View File

@ -32,68 +32,6 @@
#include <logmsg.h>
#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);
}
}
}

View File

@ -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;
}

View File

@ -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)) {

View File

@ -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_ */

View File

@ -34,6 +34,15 @@
#include <pci.h>
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;

View File

@ -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);