static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, uint8_t devfn, hwaddr addr, bool is_write, IOMMUTLBEntry *entry) { IntelIOMMUState *s = vtd_as->iommu_state; VTDContextEntry ce; uint8_t bus_num = pci_bus_num(bus); VTDContextCacheEntry *cc_entry = &vtd_as->context_cache_entry; uint64_t slpte, page_mask; uint32_t level; uint16_t source_id = vtd_make_source_id(bus_num, devfn); int ret_fr; bool is_fpd_set = false; bool reads = true; bool writes = true; uint8_t access_flags; VTDIOTLBEntry *iotlb_entry; /* * We have standalone memory region for interrupt addresses, we * should never receive translation requests in this region. */ assert(!vtd_is_interrupt_addr(addr)); /* Try to fetch slpte form IOTLB */ iotlb_entry = vtd_lookup_iotlb(s, source_id, addr); if (iotlb_entry) { trace_vtd_iotlb_page_hit(source_id, addr, iotlb_entry->slpte, iotlb_entry->domain_id); slpte = iotlb_entry->slpte; access_flags = iotlb_entry->access_flags; page_mask = iotlb_entry->mask; goto out; } /* Try to fetch context-entry from cache first */ if (cc_entry->context_cache_gen == s->context_cache_gen) { trace_vtd_iotlb_cc_hit(bus_num, devfn, cc_entry->context_entry.hi, cc_entry->context_entry.lo, cc_entry->context_cache_gen); ce = cc_entry->context_entry; is_fpd_set = ce.lo & VTD_CONTEXT_ENTRY_FPD; } else { ret_fr = vtd_dev_to_context_entry(s, bus_num, devfn, &ce); is_fpd_set = ce.lo & VTD_CONTEXT_ENTRY_FPD; if (ret_fr) { ret_fr = -ret_fr; if (is_fpd_set && vtd_is_qualified_fault(ret_fr)) { trace_vtd_fault_disabled(); } else { vtd_report_dmar_fault(s, source_id, addr, ret_fr, is_write); } goto error; } /* Update context-cache */ trace_vtd_iotlb_cc_update(bus_num, devfn, ce.hi, ce.lo, cc_entry->context_cache_gen, s->context_cache_gen); cc_entry->context_entry = ce; cc_entry->context_cache_gen = s->context_cache_gen; } /* * We don't need to translate for pass-through context entries. * Also, let's ignore IOTLB caching as well for PT devices. */ if (vtd_ce_get_type(&ce) == VTD_CONTEXT_TT_PASS_THROUGH) { entry->iova = addr & VTD_PAGE_MASK_4K; entry->translated_addr = entry->iova; entry->addr_mask = ~VTD_PAGE_MASK_4K; entry->perm = IOMMU_RW; trace_vtd_translate_pt(source_id, entry->iova); /* * When this happens, it means firstly caching-mode is not * enabled, and this is the first passthrough translation for * the device. Let's enable the fast path for passthrough. * * When passthrough is disabled again for the device, we can * capture it via the context entry invalidation, then the * IOMMU region can be swapped back. */ vtd_pt_enable_fast_path(s, source_id); return true; } ret_fr = vtd_iova_to_slpte(&ce, addr, is_write, &slpte, &level, &reads, &writes); if (ret_fr) { ret_fr = -ret_fr; if (is_fpd_set && vtd_is_qualified_fault(ret_fr)) { trace_vtd_fault_disabled(); } else { vtd_report_dmar_fault(s, source_id, addr, ret_fr, is_write); } goto error; } page_mask = vtd_slpt_level_page_mask(level); access_flags = IOMMU_ACCESS_FLAG(reads, writes); vtd_update_iotlb(s, source_id, VTD_CONTEXT_ENTRY_DID(ce.hi), addr, slpte, access_flags, level); out: entry->iova = addr & page_mask; entry->translated_addr = vtd_get_slpte_addr(slpte) & page_mask; entry->addr_mask = ~page_mask; entry->perm = access_flags; return true; error: entry->iova = 0; entry->translated_addr = 0; entry->addr_mask = 0; entry->perm = IOMMU_NONE; return false; }