diff --git a/devicemodel/hw/pci/passthrough.c b/devicemodel/hw/pci/passthrough.c index 2c04a051e..bea9d469f 100644 --- a/devicemodel/hw/pci/passthrough.c +++ b/devicemodel/hw/pci/passthrough.c @@ -57,6 +57,8 @@ */ #define AUDIO_NHLT_HACK 1 +#define PCI_BDF_GPU 0x00000010 /* 00:02.0 */ + #define GPU_GSM_SIZE 0x4000000 /* set gsm gpa=0xDB000000, which is reserved in e820 table */ #define GPU_GSM_GPA 0xDB000000 @@ -75,6 +77,9 @@ extern uint64_t audio_nhlt_len; static int pciaccess_ref_cnt; static pthread_mutex_t ref_cnt_mtx = PTHREAD_MUTEX_INITIALIZER; +uint32_t gsm_start_hpa = 0; +uint32_t opregion_start_hpa = 0; + struct passthru_dev { struct pci_vdev *dev; struct pcibar bar[PCI_BARMAX + 1]; @@ -412,6 +417,11 @@ passthru_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts) return -EINVAL; } + if (is_rtvm && (PCI_BDF(bus, slot, func) == PCI_BDF_GPU)) { + warnx("%s RTVM doesn't support GVT-D.", __func__); + return -EINVAL; + } + while ((opt = strsep(&opts, ",")) != NULL) { if (!strncmp(opt, "keep_gsi", 8)) keep_gsi = true; @@ -491,6 +501,24 @@ passthru_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts) if (error < 0) goto done; + if (ptdev->phys_bdf == PCI_BDF_GPU) { + uint32_t gsm_phys, opregion_phys; + /* get gsm hpa */ + gsm_phys = read_config(ptdev->phys_dev, PCIR_BDSM, 4); + gsm_start_hpa = gsm_phys & PCIM_BDSM_GSM_MASK; + /* initialize the EPT mapping for passthrough GPU gsm region */ + vm_map_ptdev_mmio(ctx, 0, 2, 0, GPU_GSM_GPA, GPU_GSM_SIZE, gsm_start_hpa); + + /* get opregion hpa */ + opregion_phys = read_config(ptdev->phys_dev, PCIR_ASLS_CTL, 4); + opregion_start_hpa = opregion_phys & PCIM_ASLS_OPREGION_MASK; + /* initialize the EPT mapping for passthrough GPU opregion */ + vm_map_ptdev_mmio(ctx, 0, 2, 0, GPU_OPREGION_GPA, GPU_OPREGION_SIZE, opregion_start_hpa); + + pcidev.rsvd2[0] = GPU_GSM_GPA | (gsm_phys & ~PCIM_BDSM_GSM_MASK) ; + pcidev.rsvd2[1] = GPU_OPREGION_GPA | (opregion_phys & ~PCIM_ASLS_OPREGION_MASK); + } + pcidev.virt_bdf = PCI_BDF(dev->bus, dev->slot, dev->func); pcidev.phys_bdf = ptdev->phys_bdf; for (idx = 0; idx <= PCI_BARMAX; idx++) { @@ -555,6 +583,11 @@ passthru_deinit(struct vmctx *ctx, struct pci_vdev *dev, char *opts) vm_reset_ptdev_intx_info(ctx, virt_bdf, ptdev->phys_bdf, dev->lintr.ioapic_irq, false); } + if (ptdev->phys_bdf == PCI_BDF_GPU) { + vm_unmap_ptdev_mmio(ctx, 0, 2, 0, GPU_GSM_GPA, GPU_GSM_SIZE, gsm_start_hpa); + vm_unmap_ptdev_mmio(ctx, 0, 2, 0, GPU_OPREGION_GPA, GPU_OPREGION_SIZE, opregion_start_hpa); + } + pcidev.virt_bdf = PCI_BDF(dev->bus, dev->slot, dev->func); pcidev.phys_bdf = ptdev->phys_bdf; pciaccess_cleanup(); diff --git a/hypervisor/dm/vpci/vpci.c b/hypervisor/dm/vpci/vpci.c index dfc6b9494..f27e92225 100644 --- a/hypervisor/dm/vpci/vpci.c +++ b/hypervisor/dm/vpci/vpci.c @@ -482,8 +482,12 @@ static int32_t write_pt_dev_cfg(struct pci_vdev *vdev, uint32_t offset, } else if (sriovcap_access(vdev, offset)) { write_sriov_cap_reg(vdev, offset, bytes, val); } else { - /* passthru to physical device */ - pci_pdev_write_cfg(vdev->pdev->bdf, offset, bytes, val); + /* For GVT-D, prevent stolen memory and opregion memory write */ + if (!(is_postlaunched_vm(vdev->vpci->vm) && is_gvtd(vdev->pdev->bdf) && + ((offset == PCIR_BDSM) || (offset == PCIR_ASLS_CTL)))) { + /* passthru to physical device */ + pci_pdev_write_cfg(vdev->pdev->bdf, offset, bytes, val); + } } return 0; @@ -501,8 +505,14 @@ static int32_t read_pt_dev_cfg(const struct pci_vdev *vdev, uint32_t offset, } else if (sriovcap_access(vdev, offset)) { read_sriov_cap_reg(vdev, offset, bytes, val); } else { - /* passthru to physical device */ - *val = pci_pdev_read_cfg(vdev->pdev->bdf, offset, bytes); + /* For GVT-D, just return GPA for stolen memory and opregion memory read. */ + if (is_postlaunched_vm(vdev->vpci->vm) && is_gvtd(vdev->pdev->bdf) && + ((offset == PCIR_BDSM) || (offset == PCIR_ASLS_CTL))) { + *val = pci_vdev_read_vcfg(vdev, offset, bytes); + } else { + /* passthru to physical device */ + *val = pci_pdev_read_cfg(vdev->pdev->bdf, offset, bytes); + } } return 0; @@ -752,6 +762,12 @@ int32_t vpci_assign_pcidev(struct acrn_vm *tgt_vm, struct acrn_assign_pcidev *pc pci_vdev_write_vbar(vdev, idx, pcidev->bar[idx]); } + if (is_gvtd(bdf)) { + /* rsvd2[0U] for stolen memory GPA; rsvd2[1U] for opregion memory GPA */ + pci_vdev_write_vcfg(vdev, PCIR_BDSM, 4U, pcidev->rsvd2[0U]); + pci_vdev_write_vcfg(vdev, PCIR_ASLS_CTL, 4U, pcidev->rsvd2[1U]); + } + vdev->bdf.value = pcidev->virt_bdf; spinlock_release(&tgt_vm->vpci.lock); vdev_in_sos->new_owner = vdev; @@ -800,7 +816,6 @@ int32_t vpci_deassign_pcidev(struct acrn_vm *tgt_vm, struct acrn_assign_pcidev * deinit_vmsi(vdev); deinit_vmsix(vdev); - } spinlock_release(&tgt_vm->vpci.lock); diff --git a/hypervisor/include/hw/pci.h b/hypervisor/include/hw/pci.h index c594e77f8..a200f6fa3 100644 --- a/hypervisor/include/hw/pci.h +++ b/hypervisor/include/hw/pci.h @@ -186,6 +186,10 @@ #define HOST_BRIDGE_BDF 0U #define PCI_STD_NUM_BARS 6U +/* Graphics definitions */ +#define PCIR_BDSM 0x5CU /* BDSM graphics base data of stolen memory register */ +#define PCIR_ASLS_CTL 0xFCU /* Opregion start addr register */ + union pci_bdf { uint16_t value; struct { @@ -251,6 +255,11 @@ struct pci_cfg_ops { void (*pci_write_cfg)(union pci_bdf bdf, uint32_t offset, uint32_t bytes, uint32_t val); }; +static inline bool is_gvtd(union pci_bdf bdf) +{ + return (bdf.value == CONFIG_GPU_SBDF); +} + static inline uint32_t pci_bar_offset(uint32_t idx) { return PCIR_BARS + (idx << 2U);