hv: deny the launch of VM if pass-thru PIO bar isn't identical mapping

In current design, when pass-thru dev,
for the PIO bar, need to ensure the guest PIO start address
equals to host PIO start address.
Then set the VMCS io bitmap to pass-thru the corresponding
port io to guest for performance.

ACRN-DM and acrn-config should ensure the identical mapping of PIO bar.
If ACRN-DM or acrn-config failed to achieve this,
we should deny the launch of VM

Tracked-On: #6508

Signed-off-by: Liu,Junming <junming.liu@intel.com>
Reviewed-by: Zhao Yakui <yakui.zhao@intel.com>
Reviewed-by: Fei Li <fei1.li@intel.com>
This commit is contained in:
Liu,Junming 2021-09-23 15:18:38 +00:00 committed by wenlingz
parent 275a274673
commit 4105ca2cb4
5 changed files with 93 additions and 48 deletions

View File

@ -599,34 +599,37 @@ int32_t create_vm(uint16_t vm_id, uint64_t pcpu_bitmap, struct acrn_vm_config *v
passthrough_smbios(vm, get_acrn_boot_info());
#endif
init_vpci(vm);
enable_iommu();
/* Create virtual uart;*/
init_legacy_vuarts(vm, vm_config->vuart);
register_reset_port_handler(vm);
/* vpic wire_mode default is INTR */
vm->wire_mode = VPIC_WIRE_INTR;
/* Init full emulated vIOAPIC instance:
* Present a virtual IOAPIC to guest, as a placeholder interrupt controller,
* even if the guest uses PT LAPIC. This is to satisfy the guest OSes,
* in some cases, though the functionality of vIOAPIC doesn't work.
*/
vioapic_init(vm);
/* Populate return VM handle */
*rtn_vm = vm;
vm->sw.io_shared_page = NULL;
if ((vm_config->load_order == POST_LAUNCHED_VM) && ((vm_config->guest_flags & GUEST_FLAG_IO_COMPLETION_POLLING) != 0U)) {
/* enable IO completion polling mode per its guest flags in vm_config. */
vm->sw.is_polling_ioreq = true;
}
status = set_vcpuid_entries(vm);
status = init_vpci(vm);
if (status == 0) {
vm->state = VM_CREATED;
enable_iommu();
/* Create virtual uart;*/
init_legacy_vuarts(vm, vm_config->vuart);
register_reset_port_handler(vm);
/* vpic wire_mode default is INTR */
vm->wire_mode = VPIC_WIRE_INTR;
/* Init full emulated vIOAPIC instance:
* Present a virtual IOAPIC to guest, as a placeholder interrupt controller,
* even if the guest uses PT LAPIC. This is to satisfy the guest OSes,
* in some cases, though the functionality of vIOAPIC doesn't work.
*/
vioapic_init(vm);
/* Populate return VM handle */
*rtn_vm = vm;
vm->sw.io_shared_page = NULL;
if ((vm_config->load_order == POST_LAUNCHED_VM)
&& ((vm_config->guest_flags & GUEST_FLAG_IO_COMPLETION_POLLING) != 0U)) {
/* enable IO completion polling mode per its guest flags in vm_config. */
vm->sw.is_polling_ioreq = true;
}
status = set_vcpuid_entries(vm);
if (status == 0) {
vm->state = VM_CREATED;
}
}
}

View File

@ -149,6 +149,27 @@ static void pci_vdev_update_vbar_base(struct pci_vdev *vdev, uint32_t idx)
vdev->vbars[idx].base_gpa = base;
}
int32_t check_pt_dev_pio_bars(struct pci_vdev *vdev)
{
int32_t ret = 0;
uint32_t idx;
if (vdev->pdev != NULL) {
for (idx = 0U; idx < vdev->nr_bars; idx++) {
if ((is_pci_io_bar(&vdev->vbars[idx])) && (vdev->vbars[idx].base_gpa != vdev->vbars[idx].base_hpa)) {
ret = -EIO;
pr_err("%s, PCI:%02x:%02x.%x PIO BAR%d isn't identical mapping, "
"host start addr is 0x%lx, while guest start addr is 0x%lx",
__func__, vdev->bdf.bits.b, vdev->bdf.bits.d, vdev->bdf.bits.f, idx,
vdev->vbars[idx].base_hpa, vdev->vbars[idx].base_gpa);
break;
}
}
}
return ret;
}
void pci_vdev_write_vbar(struct pci_vdev *vdev, uint32_t idx, uint32_t val)
{
struct pci_vbar *vbar;

View File

@ -41,7 +41,7 @@
#include <board_info.h>
static void vpci_init_vdevs(struct acrn_vm *vm);
static int32_t vpci_init_vdevs(struct acrn_vm *vm);
static int32_t vpci_read_cfg(struct acrn_vpci *vpci, union pci_bdf bdf, uint32_t offset, uint32_t bytes, uint32_t *val);
static int32_t vpci_write_cfg(struct acrn_vpci *vpci, union pci_bdf bdf, uint32_t offset, uint32_t bytes, uint32_t val);
static struct pci_vdev *find_available_vdev(struct acrn_vpci *vpci, union pci_bdf bdf);
@ -205,7 +205,7 @@ static int32_t vpci_mmio_cfg_access(struct io_request *io_req, void *private_dat
* @pre vm != NULL
* @pre vm->vm_id < CONFIG_MAX_VM_NUM
*/
void init_vpci(struct acrn_vm *vm)
int32_t init_vpci(struct acrn_vm *vm)
{
struct vm_io_range pci_cfgaddr_range = {
.base = PCI_CONFIG_ADDR,
@ -219,6 +219,7 @@ void init_vpci(struct acrn_vm *vm)
struct acrn_vm_config *vm_config;
struct pci_mmcfg_region *pci_mmcfg;
int32_t ret = 0;
vm->iommu = create_iommu_domain(vm->vm_id, hva2hpa(vm->arch_vm.nworld_eptp), 48U);
@ -242,20 +243,24 @@ void init_vpci(struct acrn_vm *vm)
}
/* Build up vdev list for vm */
vpci_init_vdevs(vm);
ret = vpci_init_vdevs(vm);
register_mmio_emulation_handler(vm, vpci_mmio_cfg_access, vm->vpci.pci_mmcfg.address,
vm->vpci.pci_mmcfg.address + get_pci_mmcfg_size(&vm->vpci.pci_mmcfg), &vm->vpci, false);
if (ret == 0) {
register_mmio_emulation_handler(vm, vpci_mmio_cfg_access, vm->vpci.pci_mmcfg.address,
vm->vpci.pci_mmcfg.address + get_pci_mmcfg_size(&vm->vpci.pci_mmcfg), &vm->vpci, false);
/* Intercept and handle I/O ports CF8h */
register_pio_emulation_handler(vm, PCI_CFGADDR_PIO_IDX, &pci_cfgaddr_range,
vpci_pio_cfgaddr_read, vpci_pio_cfgaddr_write);
/* Intercept and handle I/O ports CF8h */
register_pio_emulation_handler(vm, PCI_CFGADDR_PIO_IDX, &pci_cfgaddr_range,
vpci_pio_cfgaddr_read, vpci_pio_cfgaddr_write);
/* Intercept and handle I/O ports CFCh -- CFFh */
register_pio_emulation_handler(vm, PCI_CFGDATA_PIO_IDX, &pci_cfgdata_range,
vpci_pio_cfgdata_read, vpci_pio_cfgdata_write);
/* Intercept and handle I/O ports CFCh -- CFFh */
register_pio_emulation_handler(vm, PCI_CFGDATA_PIO_IDX, &pci_cfgdata_range,
vpci_pio_cfgdata_read, vpci_pio_cfgdata_write);
spinlock_init(&vm->vpci.lock);
spinlock_init(&vm->vpci.lock);
}
return ret;
}
/**
@ -666,18 +671,25 @@ struct pci_vdev *vpci_init_vdev(struct acrn_vpci *vpci, struct acrn_vm_pci_dev_c
/**
* @pre vm != NULL
*/
static void vpci_init_vdevs(struct acrn_vm *vm)
static int32_t vpci_init_vdevs(struct acrn_vm *vm)
{
uint32_t idx;
struct acrn_vpci *vpci = &(vm->vpci);
const struct acrn_vm_config *vm_config = get_vm_config(vpci2vm(vpci)->vm_id);
int32_t ret = 0;
for (idx = 0U; idx < vm_config->pci_dev_num; idx++) {
/* the vdev whose vBDF is unassigned will be created by hypercall */
if ((!is_postlaunched_vm(vm)) || (vm_config->pci_devs[idx].vbdf.value != UNASSIGNED_VBDF)) {
(void)vpci_init_vdev(vpci, &vm_config->pci_devs[idx], NULL);
ret = check_pt_dev_pio_bars(&vpci->pci_vdevs[idx]);
if (ret != 0) {
break;
}
}
}
return ret;
}
/**
@ -733,14 +745,21 @@ int32_t vpci_assign_pcidev(struct acrn_vm *tgt_vm, struct acrn_pcidev *pcidev)
pci_vdev_write_vbar(vdev, idx, pcidev->bar[idx]);
}
vdev->flags |= pcidev->type;
vdev->bdf.value = pcidev->virt_bdf;
/*We should re-add the vdev to hashlist since its vbdf has changed */
hlist_del(&vdev->link);
hlist_add_head(&vdev->link, &vpci->vdevs_hlist_heads[hash64(vdev->bdf.value, VDEV_LIST_HASHBITS)]);
vdev->parent_user = vdev_in_sos;
ret = check_pt_dev_pio_bars(vdev);
if (ret == 0) {
vdev->flags |= pcidev->type;
vdev->bdf.value = pcidev->virt_bdf;
/*We should re-add the vdev to hashlist since its vbdf has changed */
hlist_del(&vdev->link);
hlist_add_head(&vdev->link, &vpci->vdevs_hlist_heads[hash64(vdev->bdf.value, VDEV_LIST_HASHBITS)]);
vdev->parent_user = vdev_in_sos;
vdev_in_sos->user = vdev;
} else {
vdev->vdev_ops->deinit_vdev(vdev);
vdev_in_sos->vdev_ops->init_vdev(vdev_in_sos);
}
spinlock_release(&tgt_vm->vpci.lock);
vdev_in_sos->user = vdev;
} else {
pr_fatal("%s, can't find PCI device %x:%x.%x for vm[%d] %x:%x.%x\n", __func__,
pcidev->phys_bdf >> 8U, (pcidev->phys_bdf >> 3U) & 0x1fU, pcidev->phys_bdf & 0x7U,

View File

@ -178,6 +178,8 @@ void pci_vdev_write_vbar(struct pci_vdev *vdev, uint32_t idx, uint32_t val);
void vdev_pt_hide_sriov_cap(struct pci_vdev *vdev);
int32_t check_pt_dev_pio_bars(struct pci_vdev *vdev);
typedef void (*map_pcibar)(struct pci_vdev *vdev, uint32_t bar_idx);
typedef void (*unmap_pcibar)(struct pci_vdev *vdev, uint32_t bar_idx);
void vpci_update_one_vbar(struct pci_vdev *vdev, uint32_t bar_idx, uint32_t val, map_pcibar map_cb, unmap_pcibar unmap_cb);

View File

@ -184,7 +184,7 @@ struct acrn_vm;
extern const struct pci_vdev_ops vhostbridge_ops;
extern const struct pci_vdev_ops vpci_bridge_ops;
void init_vpci(struct acrn_vm *vm);
int32_t init_vpci(struct acrn_vm *vm);
void deinit_vpci(struct acrn_vm *vm);
struct pci_vdev *pci_find_vdev(struct acrn_vpci *vpci, union pci_bdf vbdf);
struct acrn_pcidev;