@@ -208,15 +208,38 @@ MemoryRegionSection *address_space_translate(AddressSpace *as, hwaddr addr,
hwaddr *xlat, hwaddr *plen,
bool is_write)
{
+ IOMMUTLBEntry iotlb;
MemoryRegionSection *section;
+ hwaddr len = *plen;
- section = address_space_lookup_region(as, addr);
- /* Compute offset within MemoryRegionSection */
- addr -= section->offset_within_address_space;
- *plen = MIN(section->size - addr, *plen);
+ for (;;) {
+ section = address_space_lookup_region(as, addr);
+
+ /* Compute offset within MemoryRegionSection */
+ addr -= section->offset_within_address_space;
+ len = MIN(section->size - addr, len);
+
+ /* Compute offset within MemoryRegion */
+ addr += section->offset_within_region;
+
+ if (!section->mr->iommu_ops) {
+ break;
+ }
+
+ iotlb = section->mr->iommu_ops->translate(section->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))) {
+ section = &phys_sections[phys_section_unassigned];
+ break;
+ }
+
+ as = section->mr->iommu_target_as;
+ }
- /* Compute offset within MemoryRegion */
- *xlat = addr + section->offset_within_region;
+ *plen = len;
+ *xlat = addr;
return section;
}
@@ -115,12 +115,36 @@ struct MemoryRegionOps {
const MemoryRegionMmio old_mmio;
};
+typedef struct IOMMUTLBEntry IOMMUTLBEntry;
+typedef struct MemoryRegionIOMMUOps MemoryRegionIOMMUOps;
+
+/* 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 {
+ hwaddr iova;
+ hwaddr translated_addr;
+ hwaddr addr_mask; /* 0xfff = 4k translation */
+ IOMMUAccessFlags perm;
+};
+
+struct MemoryRegionIOMMUOps {
+ /* Returns 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;
@@ -147,6 +171,7 @@ struct MemoryRegion {
uint8_t dirty_log_mask;
unsigned ioeventfd_nb;
MemoryRegionIoeventfd *ioeventfds;
+ struct AddressSpace *iommu_target_as;
};
struct MemoryRegionPortio {
@@ -332,6 +357,25 @@ 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
+ * @target_as: the #AddressSpace that will be used to satisfy accesses to translated
+ * addresses
+ * @name: used for debugging; not visible to the user or ABI
+ * @size: size of the region.
+ */
+void memory_region_init_iommu(MemoryRegion *mr,
+ MemoryRegionIOMMUOps *ops,
+ AddressSpace *target_as,
+ 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.
@@ -816,7 +869,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
@@ -829,7 +883,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
@@ -841,7 +896,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
@@ -865,7 +921,9 @@ MemoryRegionSection *address_space_translate(AddressSpace *as, hwaddr addr,
/* address_space_valid: check for validity of 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.
@@ -787,6 +787,7 @@ void memory_region_init(MemoryRegion *mr,
uint64_t size)
{
mr->ops = NULL;
+ mr->iommu_ops = NULL;
mr->parent = NULL;
mr->size = int128_make64(size);
if (size == UINT64_MAX) {
@@ -977,6 +978,21 @@ void memory_region_init_rom_device(MemoryRegion *mr,
mr->ram_addr = qemu_ram_alloc(size, mr);
}
+void memory_region_init_iommu(MemoryRegion *mr,
+ MemoryRegionIOMMUOps *ops,
+ AddressSpace *target_as,
+ const char *name,
+ uint64_t size)
+{
+ memory_region_init(mr, name, size);
+ mr->ops = NULL;
+ mr->iommu_ops = ops,
+ mr->opaque = mr;
+ mr->terminates = true; /* then re-forwards */
+ mr->destructor = memory_region_destructor_none;
+ mr->iommu_target_as = target_as;
+}
+
static uint64_t invalid_read(void *opaque, hwaddr addr,
unsigned size)
{
@@ -1051,6 +1067,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;