aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--exec.c35
-rw-r--r--include/exec/memory.h66
-rw-r--r--memory.c16
3 files changed, 111 insertions, 6 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
diff --git a/include/exec/memory.h b/include/exec/memory.h
index fddc6ada27..a5e7721d0e 100644
--- a/include/exec/memory.h
+++ b/include/exec/memory.h
@@ -53,6 +53,24 @@ struct MemoryRegionIORange {
hwaddr offset;
};
+typedef struct IOMMUTLBEntry IOMMUTLBEntry;
+
+/* See address_space_translate: bit 0 is read, bit 1 is write. */
+typedef enum {
+ IOMMU_NONE = 0,
+ IOMMU_RO = 1,
+ IOMMU_WO = 2,
+ IOMMU_RW = 3,
+} IOMMUAccessFlags;
+
+struct IOMMUTLBEntry {
+ AddressSpace *target_as;
+ hwaddr iova;
+ hwaddr translated_addr;
+ hwaddr addr_mask; /* 0xfff = 4k translation */
+ IOMMUAccessFlags perm;
+};
+
/*
* Memory region callbacks
*/
@@ -115,12 +133,20 @@ struct MemoryRegionOps {
const MemoryRegionMmio old_mmio;
};
+typedef struct MemoryRegionIOMMUOps MemoryRegionIOMMUOps;
+
+struct MemoryRegionIOMMUOps {
+ /* Return a TLB entry that contains a given address. */
+ IOMMUTLBEntry (*translate)(MemoryRegion *iommu, hwaddr addr);
+};
+
typedef struct CoalescedMemoryRange CoalescedMemoryRange;
typedef struct MemoryRegionIoeventfd MemoryRegionIoeventfd;
struct MemoryRegion {
/* All fields are private - violators will be prosecuted */
const MemoryRegionOps *ops;
+ const MemoryRegionIOMMUOps *iommu_ops;
void *opaque;
MemoryRegion *parent;
Int128 size;
@@ -332,6 +358,24 @@ void memory_region_init_rom_device(MemoryRegion *mr,
void memory_region_init_reservation(MemoryRegion *mr,
const char *name,
uint64_t size);
+
+/**
+ * memory_region_init_iommu: Initialize a memory region that translates
+ * addresses
+ *
+ * An IOMMU region translates addresses and forwards accesses to a target
+ * memory region.
+ *
+ * @mr: the #MemoryRegion to be initialized
+ * @ops: a function that translates addresses into the @target region
+ * @name: used for debugging; not visible to the user or ABI
+ * @size: size of the region.
+ */
+void memory_region_init_iommu(MemoryRegion *mr,
+ const MemoryRegionIOMMUOps *ops,
+ const char *name,
+ uint64_t size);
+
/**
* memory_region_destroy: Destroy a memory region and reclaim all resources.
*
@@ -371,6 +415,15 @@ static inline bool memory_region_is_romd(MemoryRegion *mr)
}
/**
+ * memory_region_is_iommu: check whether a memory region is an iommu
+ *
+ * Returns %true is a memory region is an iommu.
+ *
+ * @mr: the memory region being queried
+ */
+bool memory_region_is_iommu(MemoryRegion *mr);
+
+/**
* memory_region_name: get a memory region's name
*
* Returns the string that was used to initialize the memory region.
@@ -825,7 +878,8 @@ void address_space_destroy(AddressSpace *as);
/**
* address_space_rw: read from or write to an address space.
*
- * Return true if the operation hit any unassigned memory.
+ * Return true if the operation hit any unassigned memory or encountered an
+ * IOMMU fault.
*
* @as: #AddressSpace to be accessed
* @addr: address within that address space
@@ -838,7 +892,8 @@ bool address_space_rw(AddressSpace *as, hwaddr addr, uint8_t *buf,
/**
* address_space_write: write to address space.
*
- * Return true if the operation hit any unassigned memory.
+ * Return true if the operation hit any unassigned memory or encountered an
+ * IOMMU fault.
*
* @as: #AddressSpace to be accessed
* @addr: address within that address space
@@ -850,7 +905,8 @@ bool address_space_write(AddressSpace *as, hwaddr addr,
/**
* address_space_read: read from an address space.
*
- * Return true if the operation hit any unassigned memory.
+ * Return true if the operation hit any unassigned memory or encountered an
+ * IOMMU fault.
*
* @as: #AddressSpace to be accessed
* @addr: address within that address space
@@ -875,7 +931,9 @@ MemoryRegion *address_space_translate(AddressSpace *as, hwaddr addr,
/* address_space_access_valid: check for validity of accessing an address
* space range
*
- * Check whether memory is assigned to the given address space range.
+ * Check whether memory is assigned to the given address space range, and
+ * access is permitted by any IOMMU regions that are active for the address
+ * space.
*
* For now, addr and len should be aligned to a page size. This limitation
* will be lifted in the future.
diff --git a/memory.c b/memory.c
index 2b197455b4..7eb8d461b9 100644
--- a/memory.c
+++ b/memory.c
@@ -824,6 +824,7 @@ void memory_region_init(MemoryRegion *mr,
{
mr->ops = &unassigned_mem_ops;
mr->opaque = NULL;
+ mr->iommu_ops = NULL;
mr->parent = NULL;
mr->size = int128_make64(size);
if (size == UINT64_MAX) {
@@ -1063,6 +1064,16 @@ void memory_region_init_rom_device(MemoryRegion *mr,
mr->ram_addr = qemu_ram_alloc(size, mr);
}
+void memory_region_init_iommu(MemoryRegion *mr,
+ const MemoryRegionIOMMUOps *ops,
+ const char *name,
+ uint64_t size)
+{
+ memory_region_init(mr, name, size);
+ mr->iommu_ops = ops,
+ mr->terminates = true; /* then re-forwards */
+}
+
void memory_region_init_reservation(MemoryRegion *mr,
const char *name,
uint64_t size)
@@ -1108,6 +1119,11 @@ bool memory_region_is_rom(MemoryRegion *mr)
return mr->ram && mr->readonly;
}
+bool memory_region_is_iommu(MemoryRegion *mr)
+{
+ return mr->iommu_ops;
+}
+
void memory_region_set_log(MemoryRegion *mr, bool log, unsigned client)
{
uint8_t mask = 1 << client;