From 65ed6c3529de8b3f3d890e95a7d816afba7bf379 Mon Sep 17 00:00:00 2001 From: Li Fei1 Date: Thu, 5 Dec 2019 22:51:06 +0800 Subject: [PATCH] hv: vpci: trap PCIe ECAM access for SOS SOS will use PCIe ECAM access PCIe external configuration space. HV should trap this access for security(Now pre-launched VM doesn't want to support PCI ECAM; post-launched VM trap PCIe ECAM access in DM). Besides, update PCIe MMCONFIG region to be owned by hypervisor and expose and pass through platform hide PCI devices by BIOS to SOS. Tracked-On: #3475 Signed-off-by: Li Fei1 --- hypervisor/arch/x86/cpu.c | 1 + hypervisor/arch/x86/guest/vm.c | 3 ++ hypervisor/dm/vpci/vpci.c | 54 +++++++++++++++++++++++++++++++++- hypervisor/hw/pci.c | 20 ++++++++++--- hypervisor/include/dm/vpci.h | 7 +++-- hypervisor/include/hw/pci.h | 6 +++- 6 files changed, 82 insertions(+), 9 deletions(-) diff --git a/hypervisor/arch/x86/cpu.c b/hypervisor/arch/x86/cpu.c index 0397e04b8..621b9a7f0 100644 --- a/hypervisor/arch/x86/cpu.c +++ b/hypervisor/arch/x86/cpu.c @@ -237,6 +237,7 @@ void init_pcpu_post(uint16_t pcpu_id) panic("failed to initialize iommu!"); } + hv_access_memory_region_update(get_mmcfg_base(), PCI_MMCONFIG_SIZE); init_pci_pdev_list(); /* init_iommu must come before this */ ptdev_init(); diff --git a/hypervisor/arch/x86/guest/vm.c b/hypervisor/arch/x86/guest/vm.c index 19d2eac48..ff2bb2a3a 100644 --- a/hypervisor/arch/x86/guest/vm.c +++ b/hypervisor/arch/x86/guest/vm.c @@ -431,6 +431,9 @@ static void prepare_sos_vm_memmap(struct acrn_vm *vm) * code be page-aligned. */ ept_del_mr(vm, pml4_page, get_ap_trampoline_buf(), CONFIG_LOW_RAM_SIZE); + + /* unmap PCIe MMCONFIG region since it's owned by hypervisor */ + ept_del_mr(vm, (uint64_t *)vm->arch_vm.nworld_eptp, get_mmcfg_base(), PCI_MMCONFIG_SIZE); } /* Add EPT mapping of EPC reource for the VM */ diff --git a/hypervisor/dm/vpci/vpci.c b/hypervisor/dm/vpci/vpci.c index badb5951c..fffcdecbe 100644 --- a/hypervisor/dm/vpci/vpci.c +++ b/hypervisor/dm/vpci/vpci.c @@ -29,6 +29,7 @@ #include #include +#include #include #include #include "vpci_priv.h" @@ -172,6 +173,48 @@ static bool pci_cfgdata_io_write(struct acrn_vcpu *vcpu, uint16_t addr, size_t b return true; } +/** + * @pre io_req != NULL && private_data != NULL + */ +static int32_t vpci_handle_mmconfig_access(struct io_request *io_req, void *private_data) +{ + struct mmio_request *mmio = &io_req->reqs.mmio; + struct acrn_vpci *vpci = (struct acrn_vpci *)private_data; + uint64_t pci_mmcofg_base = vpci->pci_mmcfg_base; + uint64_t address = mmio->address; + uint32_t reg_num = (uint32_t)(address & 0xfffUL); + union pci_bdf bdf; + + /** + * Enhanced Configuration Address Mapping + * A[(20+n-1):20] Bus Number 1 ≤ n ≤ 8 + * A[19:15] Device Number + * A[14:12] Function Number + * A[11:8] Extended Register Number + * A[7:2] Register Number + * A[1:0] Along with size of the access, used to generate Byte Enables + */ + bdf.value = (uint16_t)((address - pci_mmcofg_base) >> 12U); + + if (mmio->direction == REQUEST_READ) { + if (!is_plat_hidden_pdev(bdf)) { + read_cfg(vpci, bdf, reg_num, (uint32_t)mmio->size, (uint32_t *)&mmio->value); + } else { + /* expose and pass through platform hidden devices to SOS */ + mmio->value = (uint64_t)pci_pdev_read_cfg(bdf, reg_num, (uint32_t)mmio->size); + } + } else { + if (!is_plat_hidden_pdev(bdf)) { + write_cfg(vpci, bdf, reg_num, (uint32_t)mmio->size, (uint32_t)mmio->value); + } else { + /* expose and pass through platform hidden devices to SOS */ + pci_pdev_write_cfg(bdf, reg_num, (uint32_t)mmio->size, (uint32_t)mmio->value); + } + } + + return 0; +} + /** * @pre vm != NULL * @pre vm->vm_id < CONFIG_MAX_VM_NUM @@ -189,6 +232,7 @@ void vpci_init(struct acrn_vm *vm) }; struct acrn_vm_config *vm_config; + uint64_t pci_mmcfg_base; vm->vpci.vm = vm; vm->iommu = create_iommu_domain(vm->vm_id, hva2hpa(vm->arch_vm.nworld_eptp), 48U); @@ -197,8 +241,13 @@ void vpci_init(struct acrn_vm *vm) vm_config = get_vm_config(vm->vm_id); switch (vm_config->load_order) { - case PRE_LAUNCHED_VM: case SOS_VM: + pci_mmcfg_base = get_mmcfg_base(); + vm->vpci.pci_mmcfg_base = pci_mmcfg_base; + register_mmio_emulation_handler(vm, vpci_handle_mmconfig_access, + pci_mmcfg_base, pci_mmcfg_base + PCI_MMCONFIG_SIZE, &vm->vpci); + /* falls through */ + case PRE_LAUNCHED_VM: /* * SOS: intercept port CF8 only. * UOS or pre-launched VM: register handler for CF8 only and I/O requests to CF9/CFA/CFB are @@ -404,6 +453,9 @@ static void write_cfg(struct acrn_vpci *vpci, union pci_bdf bdf, vdev = find_vdev(vpci, bdf); if (vdev != NULL) { vdev->vdev_ops->write_vdev_cfg(vdev, offset, bytes, val); + } else { + pr_acrnlog("%s %x:%x.%x not found! off: 0x%x, val: 0x%x\n", __func__, + bdf.bits.b, bdf.bits.d, bdf.bits.f, offset, val); } spinlock_release(&vpci->lock); } diff --git a/hypervisor/hw/pci.c b/hypervisor/hw/pci.c index c3020a274..3238814ae 100644 --- a/hypervisor/hw/pci.c +++ b/hypervisor/hw/pci.c @@ -78,7 +78,6 @@ static uint32_t pci_mmcfg_read_cfg(union pci_bdf bdf, uint32_t offset, uint32_t uint32_t val; spinlock_obtain(&pci_device_lock); - stac(); switch (bytes) { case 1U: val = (uint32_t)mmio_read8(hva); @@ -90,7 +89,6 @@ static uint32_t pci_mmcfg_read_cfg(union pci_bdf bdf, uint32_t offset, uint32_t val = mmio_read32(hva); break; } - clac(); spinlock_release(&pci_device_lock); return val; @@ -105,7 +103,6 @@ static void pci_mmcfg_write_cfg(union pci_bdf bdf, uint32_t offset, uint32_t byt void *hva = hpa2hva(addr); spinlock_obtain(&pci_device_lock); - stac(); switch (bytes) { case 1U: mmio_write8((uint8_t)val, hva); @@ -117,7 +114,6 @@ static void pci_mmcfg_write_cfg(union pci_bdf bdf, uint32_t offset, uint32_t byt mmio_write32(val, hva); break; } - clac(); spinlock_release(&pci_device_lock); } @@ -225,6 +221,22 @@ void enable_disable_pci_intx(union pci_bdf bdf, bool enable) } } +bool is_plat_hidden_pdev(union pci_bdf bdf) +{ + static uint32_t hidden_pdevs_num = MAX_HIDDEN_PDEVS_NUM; + uint32_t hidden_idx; + bool hidden = false; + + for (hidden_idx = 0U; hidden_idx < hidden_pdevs_num; hidden_idx++) { + if (bdf_is_equal(plat_hidden_pdevs[hidden_idx], bdf)) { + hidden = true; + break; + } + } + + return hidden; +} + static bool is_hv_owned_pdev(union pci_bdf pbdf) { bool hidden = false; diff --git a/hypervisor/include/dm/vpci.h b/hypervisor/include/dm/vpci.h index c86877a84..02f4f351b 100644 --- a/hypervisor/include/dm/vpci.h +++ b/hypervisor/include/dm/vpci.h @@ -70,9 +70,9 @@ struct pci_msix { }; union pci_cfgdata { - uint8_t data_8[PCI_REGMAX + 1U]; - uint16_t data_16[(PCI_REGMAX + 1U) >> 1U]; - uint32_t data_32[(PCI_REGMAX + 1U) >> 2U]; + uint8_t data_8[PCIE_CONFIG_SPACE_SIZE]; + uint16_t data_16[PCIE_CONFIG_SPACE_SIZE >> 1U]; + uint32_t data_32[PCIE_CONFIG_SPACE_SIZE >> 2U]; }; struct pci_vdev; @@ -123,6 +123,7 @@ struct acrn_vpci { spinlock_t lock; struct acrn_vm *vm; union pci_cfg_addr_reg addr; + uint64_t pci_mmcfg_base; uint32_t pci_vdev_cnt; struct pci_vdev pci_vdevs[CONFIG_MAX_PCI_DEV_NUM]; }; diff --git a/hypervisor/include/hw/pci.h b/hypervisor/include/hw/pci.h index d8fee0aed..3230fbfe2 100644 --- a/hypervisor/include/hw/pci.h +++ b/hypervisor/include/hw/pci.h @@ -48,9 +48,12 @@ #define PCI_SLOTMAX 0x1FU #define PCI_FUNCMAX 0x7U #define PCI_BAR_COUNT 0x6U -#define PCI_REGMAX 0xFFU #define PCI_REGMASK 0xFCU +#define PCI_CONFIG_SPACE_SIZE 0x100U +#define PCIE_CONFIG_SPACE_SIZE 0x1000U +#define PCI_MMCONFIG_SIZE 0x10000000U + /* I/O ports */ #define PCI_CONFIG_ADDR 0xCF8U #define PCI_CONFIG_DATA 0xCFCU @@ -323,6 +326,7 @@ static inline bool is_pci_cfg_bridge(uint8_t header_type) return ((header_type & PCIM_HDRTYPE) == PCIM_HDRTYPE_BRIDGE); } +bool is_plat_hidden_pdev(union pci_bdf bdf); bool pdev_need_bar_restore(const struct pci_pdev *pdev); void pdev_restore_bar(const struct pci_pdev *pdev); void pci_switch_to_mmio_cfg_ops(void);