diff --git a/exec.c b/exec.c index eac6085760..f942eb2bd1 100644 --- a/exec.c +++ b/exec.c @@ -463,42 +463,12 @@ address_space_translate_internal(AddressSpaceDispatch *d, hwaddr addr, hwaddr *x } /* Called from RCU critical section */ -IOMMUTLBEntry address_space_get_iotlb_entry(AddressSpace *as, hwaddr addr, - bool is_write) -{ - IOMMUTLBEntry iotlb = {0}; - MemoryRegionSection *section; - MemoryRegion *mr; - - for (;;) { - AddressSpaceDispatch *d = atomic_rcu_read(&as->dispatch); - section = address_space_lookup_region(d, addr, false); - addr = addr - section->offset_within_address_space - + section->offset_within_region; - mr = section->mr; - - if (!mr->iommu_ops) { - break; - } - - iotlb = mr->iommu_ops->translate(mr, addr, is_write); - if (!(iotlb.perm & (1 << is_write))) { - iotlb.target_as = NULL; - break; - } - - addr = ((iotlb.translated_addr & ~iotlb.addr_mask) - | (addr & iotlb.addr_mask)); - as = iotlb.target_as; - } - - return iotlb; -} - -/* Called from RCU critical section */ -MemoryRegion *address_space_translate(AddressSpace *as, hwaddr addr, - hwaddr *xlat, hwaddr *plen, - bool is_write) +static MemoryRegionSection address_space_do_translate(AddressSpace *as, + hwaddr addr, + hwaddr *xlat, + hwaddr *plen, + bool is_write, + bool is_mmio) { IOMMUTLBEntry iotlb; MemoryRegionSection *section; @@ -506,7 +476,7 @@ MemoryRegion *address_space_translate(AddressSpace *as, hwaddr addr, for (;;) { AddressSpaceDispatch *d = atomic_rcu_read(&as->dispatch); - section = address_space_translate_internal(d, addr, &addr, plen, true); + section = address_space_translate_internal(d, addr, &addr, plen, is_mmio); mr = section->mr; if (!mr->iommu_ops) { @@ -518,19 +488,84 @@ MemoryRegion *address_space_translate(AddressSpace *as, hwaddr addr, | (addr & iotlb.addr_mask)); *plen = MIN(*plen, (addr | iotlb.addr_mask) - addr + 1); if (!(iotlb.perm & (1 << is_write))) { - mr = &io_mem_unassigned; - break; + goto translate_fail; } as = iotlb.target_as; } + *xlat = addr; + + return *section; + +translate_fail: + return (MemoryRegionSection) { .mr = &io_mem_unassigned }; +} + +/* Called from RCU critical section */ +IOMMUTLBEntry address_space_get_iotlb_entry(AddressSpace *as, hwaddr addr, + bool is_write) +{ + MemoryRegionSection section; + hwaddr xlat, plen; + + /* Try to get maximum page mask during translation. */ + plen = (hwaddr)-1; + + /* This can never be MMIO. */ + section = address_space_do_translate(as, addr, &xlat, &plen, + is_write, false); + + /* Illegal translation */ + if (section.mr == &io_mem_unassigned) { + goto iotlb_fail; + } + + /* Convert memory region offset into address space offset */ + xlat += section.offset_within_address_space - + section.offset_within_region; + + if (plen == (hwaddr)-1) { + /* + * We use default page size here. Logically it only happens + * for identity mappings. + */ + plen = TARGET_PAGE_SIZE; + } + + /* Convert to address mask */ + plen -= 1; + + return (IOMMUTLBEntry) { + .target_as = section.address_space, + .iova = addr & ~plen, + .translated_addr = xlat & ~plen, + .addr_mask = plen, + /* IOTLBs are for DMAs, and DMA only allows on RAMs. */ + .perm = IOMMU_RW, + }; + +iotlb_fail: + return (IOMMUTLBEntry) {0}; +} + +/* Called from RCU critical section */ +MemoryRegion *address_space_translate(AddressSpace *as, hwaddr addr, + hwaddr *xlat, hwaddr *plen, + bool is_write) +{ + MemoryRegion *mr; + MemoryRegionSection section; + + /* This can be MMIO, so setup MMIO bit. */ + section = address_space_do_translate(as, addr, xlat, plen, is_write, true); + mr = section.mr; + if (xen_enabled() && memory_access_is_direct(mr, is_write)) { hwaddr page = ((addr & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE) - addr; *plen = MIN(page, *plen); } - *xlat = addr; return mr; }