mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-08-02 16:30:12 +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());
|
passthrough_smbios(vm, get_acrn_boot_info());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
init_vpci(vm);
|
status = 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);
|
|
||||||
if (status == 0) {
|
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;
|
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)
|
void pci_vdev_write_vbar(struct pci_vdev *vdev, uint32_t idx, uint32_t val)
|
||||||
{
|
{
|
||||||
struct pci_vbar *vbar;
|
struct pci_vbar *vbar;
|
||||||
|
@ -41,7 +41,7 @@
|
|||||||
#include <board_info.h>
|
#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_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 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);
|
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 != NULL
|
||||||
* @pre vm->vm_id < CONFIG_MAX_VM_NUM
|
* @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 = {
|
struct vm_io_range pci_cfgaddr_range = {
|
||||||
.base = PCI_CONFIG_ADDR,
|
.base = PCI_CONFIG_ADDR,
|
||||||
@ -219,6 +219,7 @@ void init_vpci(struct acrn_vm *vm)
|
|||||||
|
|
||||||
struct acrn_vm_config *vm_config;
|
struct acrn_vm_config *vm_config;
|
||||||
struct pci_mmcfg_region *pci_mmcfg;
|
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);
|
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 */
|
/* 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,
|
if (ret == 0) {
|
||||||
vm->vpci.pci_mmcfg.address + get_pci_mmcfg_size(&vm->vpci.pci_mmcfg), &vm->vpci, false);
|
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 */
|
/* Intercept and handle I/O ports CF8h */
|
||||||
register_pio_emulation_handler(vm, PCI_CFGADDR_PIO_IDX, &pci_cfgaddr_range,
|
register_pio_emulation_handler(vm, PCI_CFGADDR_PIO_IDX, &pci_cfgaddr_range,
|
||||||
vpci_pio_cfgaddr_read, vpci_pio_cfgaddr_write);
|
vpci_pio_cfgaddr_read, vpci_pio_cfgaddr_write);
|
||||||
|
|
||||||
/* Intercept and handle I/O ports CFCh -- CFFh */
|
/* Intercept and handle I/O ports CFCh -- CFFh */
|
||||||
register_pio_emulation_handler(vm, PCI_CFGDATA_PIO_IDX, &pci_cfgdata_range,
|
register_pio_emulation_handler(vm, PCI_CFGDATA_PIO_IDX, &pci_cfgdata_range,
|
||||||
vpci_pio_cfgdata_read, vpci_pio_cfgdata_write);
|
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
|
* @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;
|
uint32_t idx;
|
||||||
struct acrn_vpci *vpci = &(vm->vpci);
|
struct acrn_vpci *vpci = &(vm->vpci);
|
||||||
const struct acrn_vm_config *vm_config = get_vm_config(vpci2vm(vpci)->vm_id);
|
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++) {
|
for (idx = 0U; idx < vm_config->pci_dev_num; idx++) {
|
||||||
/* the vdev whose vBDF is unassigned will be created by hypercall */
|
/* 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)) {
|
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);
|
(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]);
|
pci_vdev_write_vbar(vdev, idx, pcidev->bar[idx]);
|
||||||
}
|
}
|
||||||
|
|
||||||
vdev->flags |= pcidev->type;
|
ret = check_pt_dev_pio_bars(vdev);
|
||||||
vdev->bdf.value = pcidev->virt_bdf;
|
|
||||||
/*We should re-add the vdev to hashlist since its vbdf has changed */
|
if (ret == 0) {
|
||||||
hlist_del(&vdev->link);
|
vdev->flags |= pcidev->type;
|
||||||
hlist_add_head(&vdev->link, &vpci->vdevs_hlist_heads[hash64(vdev->bdf.value, VDEV_LIST_HASHBITS)]);
|
vdev->bdf.value = pcidev->virt_bdf;
|
||||||
vdev->parent_user = vdev_in_sos;
|
/*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);
|
spinlock_release(&tgt_vm->vpci.lock);
|
||||||
vdev_in_sos->user = vdev;
|
|
||||||
} else {
|
} else {
|
||||||
pr_fatal("%s, can't find PCI device %x:%x.%x for vm[%d] %x:%x.%x\n", __func__,
|
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,
|
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);
|
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 (*map_pcibar)(struct pci_vdev *vdev, uint32_t bar_idx);
|
||||||
typedef void (*unmap_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);
|
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 vhostbridge_ops;
|
||||||
extern const struct pci_vdev_ops vpci_bridge_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);
|
void deinit_vpci(struct acrn_vm *vm);
|
||||||
struct pci_vdev *pci_find_vdev(struct acrn_vpci *vpci, union pci_bdf vbdf);
|
struct pci_vdev *pci_find_vdev(struct acrn_vpci *vpci, union pci_bdf vbdf);
|
||||||
struct acrn_pcidev;
|
struct acrn_pcidev;
|
||||||
|
Loading…
Reference in New Issue
Block a user