mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-08-01 07:49:39 +00:00
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:
parent
275a274673
commit
4105ca2cb4
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user