+
+static MemoryRegionSection *address_space_lookup_region(AddressSpaceDispatch *d,
+ hwaddr addr,
+ bool resolve_subpage)
+{
+ MemoryRegionSection *section;
+ subpage_t *subpage;
+
+ section = phys_page_find(d->phys_map, addr >> TARGET_PAGE_BITS,
+ d->nodes, d->sections);
+ if (resolve_subpage && section->mr->subpage) {
+ subpage = container_of(section->mr, subpage_t, iomem);
+ section = &d->sections[subpage->sub_section[SUBPAGE_IDX(addr)]];
+ }
+ return section;
+}
+
+static MemoryRegionSection *
+address_space_translate_internal(AddressSpaceDispatch *d, hwaddr addr, hwaddr *xlat,
+ hwaddr *plen, bool resolve_subpage)
+{
+ MemoryRegionSection *section;
+ Int128 diff;
+
+ section = address_space_lookup_region(d, addr, resolve_subpage);
+ /* Compute offset within MemoryRegionSection */
+ addr -= section->offset_within_address_space;
+
+ /* Compute offset within MemoryRegion */
+ *xlat = addr + section->offset_within_region;
+
+ diff = int128_sub(section->mr->size, int128_make64(addr));
+ *plen = int128_get64(int128_min(diff, int128_make64(*plen)));
+ return section;
+}
+
+MemoryRegion *address_space_translate(AddressSpace *as, hwaddr addr,
+ hwaddr *xlat, hwaddr *plen,
+ bool is_write)
+{
+ IOMMUTLBEntry iotlb;
+ MemoryRegionSection *section;
+ MemoryRegion *mr;
+ hwaddr len = *plen;
+
+ for (;;) {
+ section = address_space_translate_internal(as->dispatch, addr, &addr, plen, true);
+ mr = section->mr;
+
+ if (!mr->iommu_ops) {
+ break;
+ }
+
+ iotlb = mr->iommu_ops->translate(mr, addr);
+ addr = ((iotlb.translated_addr & ~iotlb.addr_mask)
+ | (addr & iotlb.addr_mask));
+ len = MIN(len, (addr | iotlb.addr_mask) - addr + 1);
+ if (!(iotlb.perm & (1 << is_write))) {
+ mr = &io_mem_unassigned;
+ break;
+ }
+
+ as = iotlb.target_as;
+ }
+
+ *plen = len;
+ *xlat = addr;
+ return mr;
+}
+
+MemoryRegionSection *
+address_space_translate_for_iotlb(AddressSpace *as, hwaddr addr, hwaddr *xlat,
+ hwaddr *plen)
+{
+ MemoryRegionSection *section;
+ section = address_space_translate_internal(as->dispatch, addr, xlat, plen, false);
+
+ assert(!section->mr->iommu_ops);
+ return section;
+}