From 5ecca6b256e589f34c3cfd84965e0f765639ebd6 Mon Sep 17 00:00:00 2001 From: Tao Yuhong Date: Wed, 14 Apr 2021 10:26:21 -0400 Subject: [PATCH] HV: vpci: check if address is in VM BAR MMIO space When guest doing BAR re-programming, we should check whether the base address of the BAR is valid.This patch does this check by: 1. whether the gpa is located in the responding MMIO window 2. whether the gpa is aligned with the BAR size Tracked-On: #6011 Signed-off-by: Tao Yuhong Reviewed-by: Eddie Dong Reviewed-by: Li Fei --- hypervisor/dm/vpci/vdev.c | 23 ++++++++++++++++++++++- hypervisor/dm/vpci/vpci.c | 15 +++++++++++++-- hypervisor/include/dm/vacpi.h | 4 ++++ hypervisor/include/dm/vpci.h | 8 ++++++++ 4 files changed, 47 insertions(+), 3 deletions(-) diff --git a/hypervisor/dm/vpci/vdev.c b/hypervisor/dm/vpci/vdev.c index 0764ee3da..ceca8ab29 100644 --- a/hypervisor/dm/vpci/vdev.c +++ b/hypervisor/dm/vpci/vdev.c @@ -106,11 +106,20 @@ uint32_t pci_vdev_read_vbar(const struct pci_vdev *vdev, uint32_t idx) return bar; } +static bool is_pci_mem_bar_base_valid(struct acrn_vm *vm, uint64_t base) +{ + struct acrn_vpci *vpci = &vm->vpci; + struct pci_mmio_res *res = (base < (1UL << 32UL)) ? &(vpci->res32): &(vpci->res64); + + return ((base >= res->start) && (base <= res->end)); +} + static void pci_vdev_update_vbar_base(struct pci_vdev *vdev, uint32_t idx) { struct pci_vbar *vbar; uint64_t base = 0UL; uint32_t lo, hi, offset; + struct pci_mmio_res *res; vbar = &vdev->vbars[idx]; offset = pci_bar_offset(idx); @@ -137,7 +146,19 @@ static void pci_vdev_update_vbar_base(struct pci_vdev *vdev, uint32_t idx) } } - /* TODO: 1. check whether the address locate in the MMIO windows 2. base must aligned with size */ + if ( (base != 0UL) && (!is_pci_io_bar(vbar))) { + if ((!is_pci_mem_bar_base_valid(vpci2vm(vdev->vpci), base)) || (!mem_aligned_check(base, vdev->vbars[idx].size))) { + res = (base < (1UL << 32UL)) ? &(vdev->vpci->res32): &(vdev->vpci->res64); + /* VM tries to reprogram vbar address out of pci mmio bar window, it can be caused by: + * 1. For SOS, .xml is misaligned with the actual native platform, and we get wrong mmio window. + * 2. Malicious operation from VM, it tries to reprogram vbar address out of pci mmio bar window + */ + pr_err("%s reprogram PCI:%02x:%02x.%x BAR%d to addr:0x%lx," + " which is out of mmio window[0x%lx - 0x%lx] or not aligned with size: 0x%lx", + __func__, vdev->bdf.bits.b, vdev->bdf.bits.d, vdev->bdf.bits.f, idx, base, res->start, + res->end, vdev->vbars[idx].size); + } + } vdev->vbars[idx].base_gpa = base; } diff --git a/hypervisor/dm/vpci/vpci.c b/hypervisor/dm/vpci/vpci.c index 14e5653d0..4f828d83c 100644 --- a/hypervisor/dm/vpci/vpci.c +++ b/hypervisor/dm/vpci/vpci.c @@ -38,6 +38,8 @@ #include "vpci_priv.h" #include #include +#include + static void 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); @@ -219,20 +221,29 @@ void init_vpci(struct acrn_vm *vm) struct pci_mmcfg_region *pci_mmcfg; vm->iommu = create_iommu_domain(vm->vm_id, hva2hpa(vm->arch_vm.nworld_eptp), 48U); - /* Build up vdev list for vm */ - vpci_init_vdevs(vm); vm_config = get_vm_config(vm->vm_id); /* virtual PCI MMCONFIG for SOS is same with the physical value */ if (vm_config->load_order == SOS_VM) { pci_mmcfg = get_mmcfg_region(); vm->vpci.pci_mmcfg = *pci_mmcfg; + vm->vpci.res32.start = MMIO32_START; + vm->vpci.res32.end = MMIO32_END; + vm->vpci.res64.start = MMIO64_START; + vm->vpci.res64.end = MMIO64_END; } else { vm->vpci.pci_mmcfg.address = UOS_VIRT_PCI_MMCFG_BASE; vm->vpci.pci_mmcfg.start_bus = UOS_VIRT_PCI_MMCFG_START_BUS; vm->vpci.pci_mmcfg.end_bus = UOS_VIRT_PCI_MMCFG_END_BUS; + vm->vpci.res32.start = UOS_VIRT_PCI_MEMBASE32; + vm->vpci.res32.end = UOS_VIRT_PCI_MEMLIMIT32; + vm->vpci.res64.start = UOS_VIRT_PCI_MEMBASE64; + vm->vpci.res64.end = UOS_VIRT_PCI_MEMLIMIT64; } + /* Build up vdev list for vm */ + 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); diff --git a/hypervisor/include/dm/vacpi.h b/hypervisor/include/dm/vacpi.h index a5d57af8c..5946f223c 100644 --- a/hypervisor/include/dm/vacpi.h +++ b/hypervisor/include/dm/vacpi.h @@ -26,6 +26,10 @@ #define UOS_VIRT_PCI_MMCFG_BASE 0xE0000000UL #define UOS_VIRT_PCI_MMCFG_START_BUS 0x0U #define UOS_VIRT_PCI_MMCFG_END_BUS 0xFFU +#define UOS_VIRT_PCI_MEMBASE32 0x80000000UL /* 2GB */ +#define UOS_VIRT_PCI_MEMLIMIT32 0xE0000000UL /* 3.5GB */ +#define UOS_VIRT_PCI_MEMBASE64 0x4000000000UL /* 256GB */ +#define UOS_VIRT_PCI_MEMLIMIT64 0x8000000000UL /* 512GB */ void build_vrsdp(struct acrn_vm *vm); diff --git a/hypervisor/include/dm/vpci.h b/hypervisor/include/dm/vpci.h index 4fba29fd3..b6217ed70 100644 --- a/hypervisor/include/dm/vpci.h +++ b/hypervisor/include/dm/vpci.h @@ -163,11 +163,19 @@ union pci_cfg_addr_reg { } bits; }; +/* start address & end address of MMIO BAR */ +struct pci_mmio_res { + uint64_t start; + uint64_t end; +}; + struct acrn_vpci { spinlock_t lock; union pci_cfg_addr_reg addr; struct pci_mmcfg_region pci_mmcfg; uint32_t pci_vdev_cnt; + struct pci_mmio_res res32; /* 32-bit mmio start/end address */ + struct pci_mmio_res res64; /* 64-bit mmio start/end address */ struct pci_vdev pci_vdevs[CONFIG_MAX_PCI_DEV_NUM]; struct hlist_head vdevs_hlist_heads [VDEV_LIST_HASHSIZE]; };