Signed-off-by: Clément Mathieu--Drif <clement.mathieu--d...@eviden.com> --- hw/i386/intel_iommu.c | 75 ++++++++++++++++++++++++++++++++-- hw/i386/intel_iommu_internal.h | 1 + 2 files changed, 73 insertions(+), 3 deletions(-)
diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index aac7677063..400b27fc95 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -5395,12 +5395,10 @@ static void vtd_report_ir_illegal_access(VTDAddressSpace *vtd_as, 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) { + if (!is_fpd_set && s->root_scalable && vtd_as->pasid != PCI_NO_PASID) { vtd_ce_get_pasid_fpd(s, &ce, &is_fpd_set, vtd_as->pasid); } } @@ -6025,6 +6023,75 @@ static IOMMUMemoryRegion *vtd_get_memory_region_pasid(PCIBus *bus, return &vtd_as->iommu; } +static IOMMUTLBEntry vtd_iommu_ats_do_translate(IOMMUMemoryRegion *iommu, + hwaddr addr, + IOMMUAccessFlags flags, + int iommu_idx) +{ + IOMMUTLBEntry entry; + VTDAddressSpace *vtd_as = container_of(iommu, VTDAddressSpace, iommu); + + if (vtd_is_interrupt_addr(addr)) { + vtd_report_ir_illegal_access(vtd_as, addr, flags & IOMMU_WO); + 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, iommu_idx); + } + return entry; +} + +static ssize_t vtd_iommu_ats_request_translation(IOMMUMemoryRegion *iommu, + bool priv_req, bool exec_req, + hwaddr addr, size_t length, + bool no_write, + IOMMUTLBEntry *result, + size_t result_length, + uint32_t *err_count) +{ + IOMMUAccessFlags flags = IOMMU_ACCESS_FLAG_FULL(true, !no_write, exec_req, + priv_req, false, false); + ssize_t res_index = 0; + hwaddr target_address = addr + length; + IOMMUTLBEntry entry; + + *err_count = 0; + + while ((addr < target_address) && (res_index < result_length)) { + entry = vtd_iommu_ats_do_translate(iommu, addr, flags, 0); + if (!IOMMU_TLB_ENTRY_TRANSLATION_ERROR(&entry)) { /* Translation done */ + if (no_write) { + /* The device should not use this entry for a write access */ + entry.perm &= ~IOMMU_WO; + } + /* + * 4.1.2 : Global Mapping (G) : Remapping hardware provides a value + * of 0 in this field + */ + entry.perm &= ~IOMMU_GLOBAL; + } else { + *err_count += 1; + } + result[res_index] = entry; + res_index += 1; + addr = (addr & (~entry.addr_mask)) + (entry.addr_mask + 1); + } + + /* Buffer too small */ + if (addr < target_address) { + return -ENOMEM; + } + return res_index; +} + +static uint64_t vtd_get_min_page_size(IOMMUMemoryRegion *iommu) +{ + return VTD_PAGE_SIZE; +} + static PCIIOMMUOps vtd_iommu_ops = { .get_address_space = vtd_host_dma_iommu, .get_address_space_pasid = vtd_host_dma_iommu_pasid, @@ -6231,6 +6298,8 @@ static void vtd_iommu_memory_region_class_init(ObjectClass *klass, imrc->translate = vtd_iommu_translate; imrc->notify_flag_changed = vtd_iommu_notify_flag_changed; imrc->replay = vtd_iommu_replay; + imrc->iommu_ats_request_translation = vtd_iommu_ats_request_translation; + imrc->get_min_page_size = vtd_get_min_page_size; } static const TypeInfo vtd_iommu_memory_region_info = { diff --git a/hw/i386/intel_iommu_internal.h b/hw/i386/intel_iommu_internal.h index 3d59e10488..aa4d0d5f16 100644 --- a/hw/i386/intel_iommu_internal.h +++ b/hw/i386/intel_iommu_internal.h @@ -193,6 +193,7 @@ #define VTD_ECAP_MHMV (15ULL << 20) #define VTD_ECAP_NEST (1ULL << 26) #define VTD_ECAP_SRS (1ULL << 31) +#define VTD_ECAP_NWFS (1ULL << 33) #define VTD_ECAP_PSS (19ULL << 35) #define VTD_ECAP_PASID (1ULL << 40) #define VTD_ECAP_SMTS (1ULL << 43) -- 2.44.0