]> git.proxmox.com Git - mirror_qemu.git/blobdiff - hw/dma/rc4030.c
Use OBJECT_DECLARE_SIMPLE_TYPE when possible
[mirror_qemu.git] / hw / dma / rc4030.c
index 17c8518feafb0c3e281ac691e05a5223fc72a095..e4d2f1725bcc7fd7c0266d62ca9008c44ec9a5b3 100644 (file)
  */
 
 #include "qemu/osdep.h"
-#include "hw/hw.h"
+#include "qemu/units.h"
+#include "hw/irq.h"
 #include "hw/mips/mips.h"
 #include "hw/sysbus.h"
+#include "migration/vmstate.h"
+#include "qapi/error.h"
 #include "qemu/timer.h"
 #include "qemu/log.h"
+#include "qemu/module.h"
 #include "exec/address-spaces.h"
 #include "trace.h"
+#include "qom/object.h"
 
 /********************************************************/
 /* rc4030 emulation                                     */
 
-#define MAX_TL_ENTRIES 512
-
 typedef struct dma_pagetable_entry {
     int32_t frame;
     int32_t owner;
@@ -53,11 +56,12 @@ typedef struct dma_pagetable_entry {
 #define DMA_FLAG_ADDR_INTR  0x0400
 
 #define TYPE_RC4030 "rc4030"
-#define RC4030(obj) \
-    OBJECT_CHECK(rc4030State, (obj), TYPE_RC4030)
+OBJECT_DECLARE_SIMPLE_TYPE(rc4030State, RC4030)
+
+#define TYPE_RC4030_IOMMU_MEMORY_REGION "rc4030-iommu-memory-region"
+
+struct rc4030State {
 
-typedef struct rc4030State
-{
     SysBusDevice parent;
 
     uint32_t config; /* 0x0000: RC4030 config register */
@@ -91,24 +95,18 @@ typedef struct rc4030State
     qemu_irq timer_irq;
     qemu_irq jazz_bus_irq;
 
-    /* biggest translation table */
-    MemoryRegion dma_tt;
-    /* translation table memory region alias, added to system RAM */
-    MemoryRegion dma_tt_alias;
     /* whole DMA memory region, root of DMA address space */
-    MemoryRegion dma_mr;
-    /* translation table entry aliases, added to DMA memory region */
-    MemoryRegion dma_mrs[MAX_TL_ENTRIES];
+    IOMMUMemoryRegion dma_mr;
     AddressSpace dma_as;
 
     MemoryRegion iomem_chipset;
     MemoryRegion iomem_jazzio;
-} rc4030State;
+};
 
 static void set_next_tick(rc4030State *s)
 {
-    qemu_irq_lower(s->timer_irq);
     uint32_t tm_hz;
+    qemu_irq_lower(s->timer_irq);
 
     tm_hz = 1000 / (s->itr + 1);
 
@@ -156,8 +154,9 @@ static uint64_t rc4030_read(void *opaque, hwaddr addr, unsigned int size)
     case 0x0058:
         val = s->cache_bmask;
         /* HACK */
-        if (s->cache_bmask == (uint32_t)-1)
+        if (s->cache_bmask == (uint32_t)-1) {
             s->cache_bmask = 0;
+        }
         break;
     /* Remote Speed Registers */
     case 0x0070:
@@ -256,96 +255,6 @@ static uint64_t rc4030_read(void *opaque, hwaddr addr, unsigned int size)
     return val;
 }
 
-static void rc4030_dma_as_update_one(rc4030State *s, int index, uint32_t frame)
-{
-    if (index < MAX_TL_ENTRIES) {
-        memory_region_set_enabled(&s->dma_mrs[index], false);
-    }
-
-    if (!frame) {
-        return;
-    }
-
-    if (index >= MAX_TL_ENTRIES) {
-        qemu_log_mask(LOG_UNIMP,
-                      "rc4030: trying to use too high "
-                      "translation table entry %d (max allowed=%d)",
-                      index, MAX_TL_ENTRIES);
-        return;
-    }
-    memory_region_set_alias_offset(&s->dma_mrs[index], frame);
-    memory_region_set_enabled(&s->dma_mrs[index], true);
-}
-
-static void rc4030_dma_tt_write(void *opaque, hwaddr addr, uint64_t data,
-                                unsigned int size)
-{
-    rc4030State *s = opaque;
-
-    /* write memory */
-    memcpy(memory_region_get_ram_ptr(&s->dma_tt) + addr, &data, size);
-
-    /* update dma address space (only if frame field has been written) */
-    if (addr % sizeof(dma_pagetable_entry) == 0) {
-        int index = addr / sizeof(dma_pagetable_entry);
-        memory_region_transaction_begin();
-        rc4030_dma_as_update_one(s, index, (uint32_t)data);
-        memory_region_transaction_commit();
-    }
-}
-
-static const MemoryRegionOps rc4030_dma_tt_ops = {
-    .write = rc4030_dma_tt_write,
-    .impl.min_access_size = 4,
-    .impl.max_access_size = 4,
-};
-
-static void rc4030_dma_tt_update(rc4030State *s, uint32_t new_tl_base,
-                                 uint32_t new_tl_limit)
-{
-    int entries, i;
-    dma_pagetable_entry *dma_tl_contents;
-
-    if (s->dma_tl_limit) {
-        /* write old dma tl table to physical memory */
-        memory_region_del_subregion(get_system_memory(), &s->dma_tt_alias);
-        cpu_physical_memory_write(s->dma_tl_limit & 0x7fffffff,
-                                  memory_region_get_ram_ptr(&s->dma_tt),
-                                  memory_region_size(&s->dma_tt_alias));
-    }
-    object_unparent(OBJECT(&s->dma_tt_alias));
-
-    s->dma_tl_base = new_tl_base;
-    s->dma_tl_limit = new_tl_limit;
-    new_tl_base &= 0x7fffffff;
-
-    if (s->dma_tl_limit) {
-        uint64_t dma_tt_size;
-        if (s->dma_tl_limit <= memory_region_size(&s->dma_tt)) {
-            dma_tt_size = s->dma_tl_limit;
-        } else {
-            dma_tt_size = memory_region_size(&s->dma_tt);
-        }
-        memory_region_init_alias(&s->dma_tt_alias, OBJECT(s),
-                                 "dma-table-alias",
-                                 &s->dma_tt, 0, dma_tt_size);
-        dma_tl_contents = memory_region_get_ram_ptr(&s->dma_tt);
-        cpu_physical_memory_read(new_tl_base, dma_tl_contents, dma_tt_size);
-
-        memory_region_transaction_begin();
-        entries = dma_tt_size / sizeof(dma_pagetable_entry);
-        for (i = 0; i < entries; i++) {
-            rc4030_dma_as_update_one(s, i, dma_tl_contents[i].frame);
-        }
-        memory_region_add_subregion(get_system_memory(), new_tl_base,
-                                    &s->dma_tt_alias);
-        memory_region_transaction_commit();
-    } else {
-        memory_region_init(&s->dma_tt_alias, OBJECT(s),
-                           "dma-table-alias", 0);
-    }
-}
-
 static void rc4030_write(void *opaque, hwaddr addr, uint64_t data,
                          unsigned int size)
 {
@@ -362,11 +271,11 @@ static void rc4030_write(void *opaque, hwaddr addr, uint64_t data,
         break;
     /* DMA transl. table base */
     case 0x0018:
-        rc4030_dma_tt_update(s, val, s->dma_tl_limit);
+        s->dma_tl_base = val;
         break;
     /* DMA transl. table limit */
     case 0x0020:
-        rc4030_dma_tt_update(s, s->dma_tl_base, val);
+        s->dma_tl_limit = val;
         break;
     /* DMA transl. table invalidated */
     case 0x0028:
@@ -460,7 +369,7 @@ static void rc4030_write(void *opaque, hwaddr addr, uint64_t data,
         break;
     /* Interval timer reload */
     case 0x0228:
-        s->itr = val;
+        s->itr = val & 0x01FF;
         qemu_irq_lower(s->timer_irq);
         set_next_tick(s);
         break;
@@ -489,10 +398,11 @@ static void update_jazz_irq(rc4030State *s)
 
     pending = s->isr_jazz & s->imr_jazz;
 
-    if (pending != 0)
+    if (pending != 0) {
         qemu_irq_raise(s->jazz_bus_irq);
-    else
+    } else {
         qemu_irq_lower(s->jazz_bus_irq);
+    }
 }
 
 static void rc4030_irq_jazz_request(void *opaque, int irq, int level)
@@ -586,6 +496,34 @@ static const MemoryRegionOps jazzio_ops = {
     .endianness = DEVICE_NATIVE_ENDIAN,
 };
 
+static IOMMUTLBEntry rc4030_dma_translate(IOMMUMemoryRegion *iommu, hwaddr addr,
+                                          IOMMUAccessFlags flag, int iommu_idx)
+{
+    rc4030State *s = container_of(iommu, rc4030State, dma_mr);
+    IOMMUTLBEntry ret = {
+        .target_as = &address_space_memory,
+        .iova = addr & ~(DMA_PAGESIZE - 1),
+        .translated_addr = 0,
+        .addr_mask = DMA_PAGESIZE - 1,
+        .perm = IOMMU_NONE,
+    };
+    uint64_t i, entry_address;
+    dma_pagetable_entry entry;
+
+    i = addr / DMA_PAGESIZE;
+    if (i < s->dma_tl_limit / sizeof(entry)) {
+        entry_address = (s->dma_tl_base & 0x7fffffff) + i * sizeof(entry);
+        if (address_space_read(ret.target_as, entry_address,
+                               MEMTXATTRS_UNSPECIFIED, &entry, sizeof(entry))
+                == MEMTX_OK) {
+            ret.translated_addr = entry.frame & ~(DMA_PAGESIZE - 1);
+            ret.perm = IOMMU_RW;
+        }
+    }
+
+    return ret;
+}
+
 static void rc4030_reset(DeviceState *dev)
 {
     rc4030State *s = RC4030(dev);
@@ -596,7 +534,6 @@ static void rc4030_reset(DeviceState *dev)
     s->invalid_address_register = 0;
 
     memset(s->dma_regs, 0, sizeof(s->dma_regs));
-    rc4030_dma_tt_update(s, 0, 0);
 
     s->remote_failed_address = s->memory_failed_address = 0;
     s->cache_maint = 0;
@@ -605,8 +542,9 @@ static void rc4030_reset(DeviceState *dev)
 
     s->memory_refresh_rate = 0x18186;
     s->nvram_protect = 7;
-    for (i = 0; i < 15; i++)
+    for (i = 0; i < 15; i++) {
         s->rem_speed[i] = 7;
+    }
     s->imr_jazz = 0x10; /* XXX: required by firmware, but why? */
     s->isr_jazz = 0;
 
@@ -618,7 +556,7 @@ static void rc4030_reset(DeviceState *dev)
 
 static int rc4030_post_load(void *opaque, int version_id)
 {
-    rc4030States = opaque;
+    rc4030State *s = opaque;
 
     set_next_tick(s);
     update_jazz_irq(s);
@@ -652,13 +590,15 @@ static const VMStateDescription vmstate_rc4030 = {
     }
 };
 
-static void rc4030_do_dma(void *opaque, int n, uint8_t *buf, int len, int is_write)
+static void rc4030_do_dma(void *opaque, int n, uint8_t *buf,
+                          int len, bool is_write)
 {
     rc4030State *s = opaque;
     hwaddr dma_addr;
     int dev_to_mem;
 
-    s->dma_regs[n][DMA_REG_ENABLE] &= ~(DMA_FLAG_TC_INTR | DMA_FLAG_MEM_INTR | DMA_FLAG_ADDR_INTR);
+    s->dma_regs[n][DMA_REG_ENABLE] &=
+           ~(DMA_FLAG_TC_INTR | DMA_FLAG_MEM_INTR | DMA_FLAG_ADDR_INTR);
 
     /* Check DMA channel consistency */
     dev_to_mem = (s->dma_regs[n][DMA_REG_ENABLE] & DMA_FLAG_MEM_TO_DEV) ? 0 : 1;
@@ -670,8 +610,9 @@ static void rc4030_do_dma(void *opaque, int n, uint8_t *buf, int len, int is_wri
     }
 
     /* Get start address and len */
-    if (len > s->dma_regs[n][DMA_REG_COUNT])
+    if (len > s->dma_regs[n][DMA_REG_COUNT]) {
         len = s->dma_regs[n][DMA_REG_COUNT];
+    }
     dma_addr = s->dma_regs[n][DMA_REG_ADDRESS];
 
     /* Read/write data at right place */
@@ -690,13 +631,13 @@ struct rc4030DMAState {
 void rc4030_dma_read(void *dma, uint8_t *buf, int len)
 {
     rc4030_dma s = dma;
-    rc4030_do_dma(s->opaque, s->n, buf, len, 0);
+    rc4030_do_dma(s->opaque, s->n, buf, len, false);
 }
 
 void rc4030_dma_write(void *dma, uint8_t *buf, int len)
 {
     rc4030_dma s = dma;
-    rc4030_do_dma(s->opaque, s->n, buf, len, 1);
+    rc4030_do_dma(s->opaque, s->n, buf, len, true);
 }
 
 static rc4030_dma *rc4030_allocate_dmas(void *opaque, int n)
@@ -705,8 +646,8 @@ static rc4030_dma *rc4030_allocate_dmas(void *opaque, int n)
     struct rc4030DMAState *p;
     int i;
 
-    s = (rc4030_dma *)g_malloc0(sizeof(rc4030_dma) * n);
-    p = (struct rc4030DMAState *)g_malloc0(sizeof(struct rc4030DMAState) * n);
+    s = (rc4030_dma *)g_new0(rc4030_dma, n);
+    p = (struct rc4030DMAState *)g_new0(struct rc4030DMAState, n);
     for (i = 0; i < n; i++) {
         p->opaque = opaque;
         p->n = i;
@@ -735,47 +676,29 @@ static void rc4030_realize(DeviceState *dev, Error **errp)
 {
     rc4030State *s = RC4030(dev);
     Object *o = OBJECT(dev);
-    int i;
 
     s->periodic_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
                                      rc4030_periodic_timer, s);
 
-    memory_region_init_io(&s->iomem_chipset, NULL, &rc4030_ops, s,
+    memory_region_init_io(&s->iomem_chipset, o, &rc4030_ops, s,
                           "rc4030.chipset", 0x300);
-    memory_region_init_io(&s->iomem_jazzio, NULL, &jazzio_ops, s,
+    memory_region_init_io(&s->iomem_jazzio, o, &jazzio_ops, s,
                           "rc4030.jazzio", 0x00001000);
 
-    memory_region_init_rom_device(&s->dma_tt, o,
-                                  &rc4030_dma_tt_ops, s, "dma-table",
-                                  MAX_TL_ENTRIES * sizeof(dma_pagetable_entry),
-                                  NULL);
-    memory_region_init(&s->dma_tt_alias, o, "dma-table-alias", 0);
-    memory_region_init(&s->dma_mr, o, "dma", INT32_MAX);
-    for (i = 0; i < MAX_TL_ENTRIES; ++i) {
-        memory_region_init_alias(&s->dma_mrs[i], o, "dma-alias",
-                                 get_system_memory(), 0, DMA_PAGESIZE);
-        memory_region_set_enabled(&s->dma_mrs[i], false);
-        memory_region_add_subregion(&s->dma_mr, i * DMA_PAGESIZE,
-                                    &s->dma_mrs[i]);
-    }
-    address_space_init(&s->dma_as, &s->dma_mr, "rc4030-dma");
+    memory_region_init_iommu(&s->dma_mr, sizeof(s->dma_mr),
+                             TYPE_RC4030_IOMMU_MEMORY_REGION,
+                             o, "rc4030.dma", 4 * GiB);
+    address_space_init(&s->dma_as, MEMORY_REGION(&s->dma_mr), "rc4030-dma");
 }
 
-static void rc4030_unrealize(DeviceState *dev, Error **errp)
+static void rc4030_unrealize(DeviceState *dev)
 {
     rc4030State *s = RC4030(dev);
-    int i;
 
     timer_free(s->periodic_timer);
 
     address_space_destroy(&s->dma_as);
-    object_unparent(OBJECT(&s->dma_tt));
-    object_unparent(OBJECT(&s->dma_tt_alias));
     object_unparent(OBJECT(&s->dma_mr));
-    for (i = 0; i < MAX_TL_ENTRIES; ++i) {
-        memory_region_del_subregion(&s->dma_mr, &s->dma_mrs[i]);
-        object_unparent(OBJECT(&s->dma_mrs[i]));
-    }
 }
 
 static void rc4030_class_init(ObjectClass *klass, void *class_data)
@@ -796,19 +719,34 @@ static const TypeInfo rc4030_info = {
     .class_init = rc4030_class_init,
 };
 
+static void rc4030_iommu_memory_region_class_init(ObjectClass *klass,
+                                                  void *data)
+{
+    IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass);
+
+    imrc->translate = rc4030_dma_translate;
+}
+
+static const TypeInfo rc4030_iommu_memory_region_info = {
+    .parent = TYPE_IOMMU_MEMORY_REGION,
+    .name = TYPE_RC4030_IOMMU_MEMORY_REGION,
+    .class_init = rc4030_iommu_memory_region_class_init,
+};
+
 static void rc4030_register_types(void)
 {
     type_register_static(&rc4030_info);
+    type_register_static(&rc4030_iommu_memory_region_info);
 }
 
 type_init(rc4030_register_types)
 
-DeviceState *rc4030_init(rc4030_dma **dmas, MemoryRegion **dma_mr)
+DeviceState *rc4030_init(rc4030_dma **dmas, IOMMUMemoryRegion **dma_mr)
 {
     DeviceState *dev;
 
-    dev = qdev_create(NULL, TYPE_RC4030);
-    qdev_init_nofail(dev);
+    dev = qdev_new(TYPE_RC4030);
+    sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
 
     *dmas = rc4030_allocate_dmas(dev, 4);
     *dma_mr = &RC4030(dev)->dma_mr;