aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Cave-Ayland <mark.cave-ayland@ilande.co.uk>2017-10-27 13:09:03 +0100
committerMark Cave-Ayland <mark.cave-ayland@ilande.co.uk>2017-10-31 17:25:37 +0000
commit8413846631113f42f0cd18165f465a6193af7f30 (patch)
tree838803e40d337d32e8aab2114911f5bf3c5729d1
parent331b7fc1569d65eb9e083e6d3e3c9352fcccd4f9 (diff)
sun4m: implement IOMMU translation using IOMMU memory region
Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
-rw-r--r--hw/dma/sun4m_iommu.c62
-rw-r--r--include/hw/sparc/sun4m.h5
2 files changed, 67 insertions, 0 deletions
diff --git a/hw/dma/sun4m_iommu.c b/hw/dma/sun4m_iommu.c
index 840064b5e2..ce21a22d72 100644
--- a/hw/dma/sun4m_iommu.c
+++ b/hw/dma/sun4m_iommu.c
@@ -278,6 +278,49 @@ static void iommu_bad_addr(IOMMUState *s, hwaddr addr,
qemu_irq_raise(s->irq);
}
+/* Called from RCU critical section */
+static IOMMUTLBEntry sun4m_translate_iommu(IOMMUMemoryRegion *iommu,
+ hwaddr addr,
+ IOMMUAccessFlags flags)
+{
+ IOMMUState *is = container_of(iommu, IOMMUState, iommu);
+ hwaddr page, pa;
+ int is_write = (flags & IOMMU_WO) ? 1 : 0;
+ uint32_t pte;
+ IOMMUTLBEntry ret = {
+ .target_as = &address_space_memory,
+ .iova = 0,
+ .translated_addr = 0,
+ .addr_mask = ~(hwaddr)0,
+ .perm = IOMMU_NONE,
+ };
+
+ page = addr & IOMMU_PAGE_MASK;
+ pte = iommu_page_get_flags(is, page);
+ if (!(pte & IOPTE_VALID)) {
+ iommu_bad_addr(is, page, is_write);
+ return ret;
+ }
+
+ pa = iommu_translate_pa(addr, pte);
+ if (is_write && !(pte & IOPTE_WRITE)) {
+ iommu_bad_addr(is, page, is_write);
+ return ret;
+ }
+
+ if (pte & IOPTE_WRITE) {
+ ret.perm = IOMMU_RW;
+ } else {
+ ret.perm = IOMMU_RO;
+ }
+
+ ret.iova = page;
+ ret.translated_addr = pa;
+ ret.addr_mask = ~IOMMU_PAGE_MASK;
+
+ return ret;
+}
+
void sparc_iommu_memory_rw(void *opaque, hwaddr addr,
uint8_t *buf, int len, int is_write)
{
@@ -340,6 +383,11 @@ static void iommu_init(Object *obj)
IOMMUState *s = SUN4M_IOMMU(obj);
SysBusDevice *dev = SYS_BUS_DEVICE(obj);
+ memory_region_init_iommu(&s->iommu, sizeof(s->iommu),
+ TYPE_SUN4M_IOMMU_MEMORY_REGION, OBJECT(dev),
+ "iommu-sun4m", UINT64_MAX);
+ address_space_init(&s->iommu_as, MEMORY_REGION(&s->iommu), "iommu-as");
+
sysbus_init_irq(dev, &s->irq);
memory_region_init_io(&s->iomem, obj, &iommu_mem_ops, s, "iommu",
@@ -369,9 +417,23 @@ static const TypeInfo iommu_info = {
.class_init = iommu_class_init,
};
+static void sun4m_iommu_memory_region_class_init(ObjectClass *klass, void *data)
+{
+ IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass);
+
+ imrc->translate = sun4m_translate_iommu;
+}
+
+static const TypeInfo sun4m_iommu_memory_region_info = {
+ .parent = TYPE_IOMMU_MEMORY_REGION,
+ .name = TYPE_SUN4M_IOMMU_MEMORY_REGION,
+ .class_init = sun4m_iommu_memory_region_class_init,
+};
+
static void iommu_register_types(void)
{
type_register_static(&iommu_info);
+ type_register_static(&sun4m_iommu_memory_region_info);
}
type_init(iommu_register_types)
diff --git a/include/hw/sparc/sun4m.h b/include/hw/sparc/sun4m.h
index 1f1cf91ed9..6e21e1046f 100644
--- a/include/hw/sparc/sun4m.h
+++ b/include/hw/sparc/sun4m.h
@@ -12,11 +12,16 @@
#define TYPE_SUN4M_IOMMU "iommu"
#define SUN4M_IOMMU(obj) OBJECT_CHECK(IOMMUState, (obj), TYPE_SUN4M_IOMMU)
+#define TYPE_SUN4M_IOMMU_MEMORY_REGION "sun4m-iommu-memory-region"
+
#define IOMMU_NREGS (4 * 4096 / 4)
typedef struct IOMMUState {
SysBusDevice parent_obj;
+ AddressSpace iommu_as;
+ IOMMUMemoryRegion iommu;
+
MemoryRegion iomem;
uint32_t regs[IOMMU_NREGS];
hwaddr iostart;