aboutsummaryrefslogtreecommitdiff
path: root/exec.c
diff options
context:
space:
mode:
authorAvi Kivity <avi.kivity@gmail.com>2012-10-30 13:47:46 +0200
committerPaolo Bonzini <pbonzini@redhat.com>2013-06-20 16:32:47 +0200
commit30951157441aed950ad8ca326500b4986d431c7a (patch)
treea5b3ba2434175eb5e87b533d4791038d33ac73a0 /exec.c
parent052e87b073cb70afcd767d32f45af2794a5a65de (diff)
memory: iommu support
Add a new memory region type that translates addresses it is given, then forwards them to a target address space. This is similar to an alias, except that the mapping is more flexible than a linear translation and trucation, and also less efficient since the translation happens at runtime. The implementation uses an AddressSpace mapping the target region to avoid hierarchical dispatch all the way to the resolved region; only iommu regions are looked up dynamically. Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Avi Kivity <avi.kivity@gmail.com> [Modified to put translation in address_space_translate; assume IOMMUs are not reachable from TCG. - Paolo] Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'exec.c')
-rw-r--r--exec.c35
1 files changed, 33 insertions, 2 deletions
diff --git a/exec.c b/exec.c
index 77eedd8725..dfddd853de 100644
--- a/exec.c
+++ b/exec.c
@@ -266,14 +266,45 @@ MemoryRegion *address_space_translate(AddressSpace *as, hwaddr addr,
hwaddr *xlat, hwaddr *plen,
bool is_write)
{
- return address_space_translate_internal(as, addr, xlat, plen, true)->mr;
+ IOMMUTLBEntry iotlb;
+ MemoryRegionSection *section;
+ MemoryRegion *mr;
+ hwaddr len = *plen;
+
+ for (;;) {
+ section = address_space_translate_internal(as, 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)
{
- return address_space_translate_internal(as, addr, xlat, plen, false);
+ MemoryRegionSection *section;
+ section = address_space_translate_internal(as, addr, xlat, plen, false);
+
+ assert(!section->mr->iommu_ops);
+ return section;
}
#endif