From: CLEMENT MATHIEU--DRIF Date: Wed, 29 Oct 2025 10:51:42 +0000 (+0000) Subject: intel_iommu: Support memory operations with pre-translated addresses X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=05f858cb67e4965db77f3fe223e6110ff3ca0dde;p=thirdparty%2Fqemu.git intel_iommu: Support memory operations with pre-translated addresses Signed-off-by: Clement Mathieu--Drif Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Message-Id: <20251029105137.1097933-3-clement.mathieu--drif@eviden.com> --- diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 80b21a6468..7e7c31cb55 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -3985,6 +3985,25 @@ static void vtd_mem_write(void *opaque, hwaddr addr, } } +static void vtd_prepare_identity_entry(hwaddr addr, IOMMUAccessFlags perm, + uint32_t pasid, IOMMUTLBEntry *iotlb) +{ + iotlb->iova = addr & VTD_PAGE_MASK_4K; + iotlb->translated_addr = addr & VTD_PAGE_MASK_4K; + iotlb->addr_mask = ~VTD_PAGE_MASK_4K; + iotlb->perm = perm; + iotlb->pasid = pasid; +} + +static inline void vtd_prepare_error_entry(IOMMUTLBEntry *entry) +{ + entry->iova = 0; + entry->translated_addr = 0; + entry->addr_mask = ~VTD_PAGE_MASK_4K; + entry->perm = IOMMU_NONE; + entry->pasid = PCI_NO_PASID; +} + static IOMMUTLBEntry vtd_iommu_translate(IOMMUMemoryRegion *iommu, hwaddr addr, IOMMUAccessFlags flag, int iommu_idx) { @@ -3996,16 +4015,29 @@ static IOMMUTLBEntry vtd_iommu_translate(IOMMUMemoryRegion *iommu, hwaddr addr, .pasid = vtd_as->pasid, }; bool success; + bool is_write = flag & IOMMU_WO; if (likely(s->dmar_enabled)) { - success = vtd_do_iommu_translate(vtd_as, vtd_as->bus, vtd_as->devfn, - addr, flag & IOMMU_WO, &iotlb); + /* Only support translated requests in scalable mode */ + if (iommu_idx == VTD_IDX_TRANSLATED && s->root_scalable) { + if (vtd_as->pasid == PCI_NO_PASID) { + vtd_prepare_identity_entry(addr, IOMMU_RW, PCI_NO_PASID, + &iotlb); + success = true; + } else { + vtd_prepare_error_entry(&iotlb); + error_report_once("%s: translated request with PASID not " + "allowed (pasid=0x%" PRIx32 ")", __func__, + vtd_as->pasid); + success = false; + } + } else { + success = vtd_do_iommu_translate(vtd_as, vtd_as->bus, vtd_as->devfn, + addr, is_write, &iotlb); + } } else { /* DMAR disabled, passthrough, use 4k-page*/ - iotlb.iova = addr & VTD_PAGE_MASK_4K; - iotlb.translated_addr = addr & VTD_PAGE_MASK_4K; - iotlb.addr_mask = ~VTD_PAGE_MASK_4K; - iotlb.perm = IOMMU_RW; + vtd_prepare_identity_entry(addr, IOMMU_RW, vtd_as->pasid, &iotlb); success = true; } @@ -4414,6 +4446,37 @@ static int vtd_int_remap(X86IOMMUState *iommu, MSIMessage *src, src, dst, sid, false); } +static void vtd_report_sid_ir_illegal_access(IntelIOMMUState *s, uint16_t sid, + uint32_t pasid, hwaddr addr, + bool is_write) +{ + uint8_t bus_n = VTD_SID_TO_BUS(sid); + uint8_t devfn = VTD_SID_TO_DEVFN(sid); + bool is_fpd_set = false; + VTDContextEntry ce; + + /* Try out best to fetch FPD, we can't do anything more */ + if (vtd_dev_to_context_entry(s, bus_n, devfn, &ce) == 0) { + is_fpd_set = ce.lo & VTD_CONTEXT_ENTRY_FPD; + if (!is_fpd_set && s->root_scalable) { + vtd_ce_get_pasid_fpd(s, &ce, &is_fpd_set, pasid); + } + } + + vtd_report_fault(s, VTD_FR_SM_INTERRUPT_ADDR, is_fpd_set, sid, addr, + is_write, pasid != PCI_NO_PASID, pasid); +} + +static void vtd_report_ir_illegal_access(VTDAddressSpace *vtd_as, + hwaddr addr, bool is_write) +{ + uint8_t bus_n = pci_bus_num(vtd_as->bus); + uint16_t sid = PCI_BUILD_BDF(bus_n, vtd_as->devfn); + + vtd_report_sid_ir_illegal_access(vtd_as->iommu_state, sid, vtd_as->pasid, + addr, is_write); +} + static MemTxResult vtd_mem_ir_read(void *opaque, hwaddr addr, uint64_t *data, unsigned size, MemTxAttrs attrs) @@ -4425,9 +4488,11 @@ static MemTxResult vtd_mem_ir_write(void *opaque, hwaddr addr, uint64_t value, unsigned size, MemTxAttrs attrs) { + IntelIOMMUState *s = opaque; int ret = 0; MSIMessage from = {}, to = {}; uint16_t sid = X86_IOMMU_SID_INVALID; + uint32_t pasid; from.address = (uint64_t) addr + VTD_INTERRUPT_ADDR_FIRST; from.data = (uint32_t) value; @@ -4435,9 +4500,16 @@ static MemTxResult vtd_mem_ir_write(void *opaque, hwaddr addr, if (!attrs.unspecified) { /* We have explicit Source ID */ sid = attrs.requester_id; + pasid = attrs.pid != 0 ? attrs.pid : PCI_NO_PASID; + + if (attrs.address_type == PCI_AT_TRANSLATED && + sid != X86_IOMMU_SID_INVALID) { + vtd_report_sid_ir_illegal_access(s, sid, pasid, from.address, true); + return MEMTX_ERROR; + } } - ret = vtd_interrupt_remap_msi(opaque, &from, &to, sid, true); + ret = vtd_interrupt_remap_msi(s, &from, &to, sid, true); if (ret) { /* Drop this interrupt */ return MEMTX_ERROR; @@ -4462,30 +4534,6 @@ static const MemoryRegionOps vtd_mem_ir_ops = { }, }; -static void vtd_report_ir_illegal_access(VTDAddressSpace *vtd_as, - hwaddr addr, bool is_write) -{ - IntelIOMMUState *s = vtd_as->iommu_state; - uint8_t bus_n = pci_bus_num(vtd_as->bus); - uint16_t sid = PCI_BUILD_BDF(bus_n, vtd_as->devfn); - bool is_fpd_set = false; - VTDContextEntry ce; - - assert(vtd_as->pasid != PCI_NO_PASID); - - /* Try out best to fetch FPD, we can't do anything more */ - if (vtd_dev_to_context_entry(s, bus_n, vtd_as->devfn, &ce) == 0) { - is_fpd_set = ce.lo & VTD_CONTEXT_ENTRY_FPD; - if (!is_fpd_set && s->root_scalable) { - vtd_ce_get_pasid_fpd(s, &ce, &is_fpd_set, vtd_as->pasid); - } - } - - vtd_report_fault(s, VTD_FR_SM_INTERRUPT_ADDR, - is_fpd_set, sid, addr, is_write, - true, vtd_as->pasid); -} - static MemTxResult vtd_mem_ir_fault_read(void *opaque, hwaddr addr, uint64_t *data, unsigned size, MemTxAttrs attrs) @@ -5145,14 +5193,10 @@ static IOMMUTLBEntry vtd_iommu_ats_do_translate(IOMMUMemoryRegion *iommu, if (vtd_is_interrupt_addr(addr)) { vtd_report_ir_illegal_access(vtd_as, addr, flags & IOMMU_WO); + vtd_prepare_error_entry(&entry); entry.target_as = &address_space_memory; - entry.iova = 0; - entry.translated_addr = 0; - entry.addr_mask = ~VTD_PAGE_MASK_4K; - entry.perm = IOMMU_NONE; - entry.pasid = PCI_NO_PASID; } else { - entry = vtd_iommu_translate(iommu, addr, flags, 0); + entry = vtd_iommu_translate(iommu, addr, flags, VTD_IDX_UNTRANSLATED); } return entry;