diff --git a/exec-all.h b/exec-all.h
index 3d72952..51d01f2 100644
--- a/exec-all.h
+++ b/exec-all.h
@@ -302,9 +302,7 @@ extern void *tci_tb_ptr;
 uint64_t io_mem_read(int index, target_phys_addr_t addr, unsigned size);
 void io_mem_write(int index, target_phys_addr_t addr, uint64_t value,
                   unsigned size);
-extern CPUWriteMemoryFunc *_io_mem_write[IO_MEM_NB_ENTRIES][4];
-extern CPUReadMemoryFunc *_io_mem_read[IO_MEM_NB_ENTRIES][4];
-extern void *io_mem_opaque[IO_MEM_NB_ENTRIES];
+extern struct MemoryRegion *io_mem_region[IO_MEM_NB_ENTRIES];
 
 void tlb_fill(CPUState *env1, target_ulong addr, int is_write, int mmu_idx,
               void *retaddr);
diff --git a/exec-obsolete.h b/exec-obsolete.h
index e08e750..f8af27e 100644
--- a/exec-obsolete.h
+++ b/exec-obsolete.h
@@ -31,9 +31,8 @@ ram_addr_t qemu_ram_alloc(ram_addr_t size, MemoryRegion *mr);
 void qemu_ram_free(ram_addr_t addr);
 void qemu_ram_free_from_ptr(ram_addr_t addr);
 
-int cpu_register_io_memory(CPUReadMemoryFunc * const *mem_read,
-                           CPUWriteMemoryFunc * const *mem_write,
-                           void *opaque);
+struct MemoryRegion;
+int cpu_register_io_memory(MemoryRegion *mr);
 void cpu_unregister_io_memory(int table_address);
 
 struct MemoryRegionSection;
diff --git a/exec.c b/exec.c
index fc1dcb7..b443dbb 100644
--- a/exec.c
+++ b/exec.c
@@ -208,9 +208,7 @@
 static void memory_map_init(void);
 
 /* io memory support */
-CPUWriteMemoryFunc *_io_mem_write[IO_MEM_NB_ENTRIES][4];
-CPUReadMemoryFunc *_io_mem_read[IO_MEM_NB_ENTRIES][4];
-void *io_mem_opaque[IO_MEM_NB_ENTRIES];
+MemoryRegion *io_mem_region[IO_MEM_NB_ENTRIES];
 static char io_mem_used[IO_MEM_NB_ENTRIES];
 static MemoryRegion io_mem_watch;
 #endif
@@ -2563,8 +2561,10 @@ void cpu_register_physical_memory_log(MemoryRegionSection *section,
                                            &p->phys_offset, orig_memory,
                                            p->region_offset);
                 } else {
-                    subpage = io_mem_opaque[(orig_memory & ~TARGET_PAGE_MASK)
-                                            >> IO_MEM_SHIFT];
+                    MemoryRegion *mr
+                        = io_mem_region[(orig_memory & ~TARGET_PAGE_MASK)
+                                        >> IO_MEM_SHIFT];
+                    subpage = container_of(mr, subpage_t, iomem);
                 }
                 subpage_register(subpage, start_addr2, end_addr2, phys_offset,
                                  region_offset);
@@ -3427,13 +3427,8 @@ static int get_free_io_mem_idx(void)
    modified. If it is zero, a new io zone is allocated. The return
    value can be used with cpu_register_physical_memory(). (-1) is
    returned if error. */
-static int cpu_register_io_memory_fixed(int io_index,
-                                        CPUReadMemoryFunc * const *mem_read,
-                                        CPUWriteMemoryFunc * const *mem_write,
-                                        void *opaque)
+static int cpu_register_io_memory_fixed(int io_index, MemoryRegion *mr)
 {
-    int i;
-
     if (io_index <= 0) {
         io_index = get_free_io_mem_idx();
         if (io_index == -1)
@@ -3444,36 +3439,21 @@ static int cpu_register_io_memory_fixed(int io_index,
             return -1;
     }
 
-    for (i = 0; i < 3; ++i) {
-        assert(mem_read[i]);
-        _io_mem_read[io_index][i] = mem_read[i];
-    }
-    for (i = 0; i < 3; ++i) {
-        assert(mem_write[i]);
-        _io_mem_write[io_index][i] = mem_write[i];
-    }
-    io_mem_opaque[io_index] = opaque;
+    io_mem_region[io_index] = mr;
 
     return (io_index << IO_MEM_SHIFT);
 }
 
-int cpu_register_io_memory(CPUReadMemoryFunc * const *mem_read,
-                           CPUWriteMemoryFunc * const *mem_write,
-                           void *opaque)
+int cpu_register_io_memory(MemoryRegion *mr)
 {
-    return cpu_register_io_memory_fixed(0, mem_read, mem_write, opaque);
+    return cpu_register_io_memory_fixed(0, mr);
 }
 
 void cpu_unregister_io_memory(int io_table_address)
 {
-    int i;
     int io_index = io_table_address >> IO_MEM_SHIFT;
 
-    for (i=0;i < 3; i++) {
-        _io_mem_read[io_index][i] = NULL;
-        _io_mem_write[io_index][i] = NULL;
-    }
-    io_mem_opaque[io_index] = NULL;
+    io_mem_region[io_index] = NULL;
     io_mem_used[io_index] = 0;
 }
 
diff --git a/memory.c b/memory.c
index e34bc65..25b36ff 100644
--- a/memory.c
+++ b/memory.c
@@ -906,11 +906,10 @@ static bool memory_region_access_valid(MemoryRegion *mr,
     return true;
 }
 
-static uint32_t memory_region_read_thunk_n(void *_mr,
-                                           target_phys_addr_t addr,
-                                           unsigned size)
+static uint64_t memory_region_dispatch_read1(MemoryRegion *mr,
+                                             target_phys_addr_t addr,
+                                             unsigned size)
 {
-    MemoryRegion *mr = _mr;
     uint64_t data = 0;
 
     if (!memory_region_access_valid(mr, addr, size, false)) {
@@ -930,17 +929,45 @@ static uint32_t memory_region_read_thunk_n(void *_mr,
     return data;
 }
 
-static void memory_region_write_thunk_n(void *_mr,
-                                        target_phys_addr_t addr,
-                                        unsigned size,
-                                        uint64_t data)
+static void adjust_endianness(MemoryRegion *mr, uint64_t *data, unsigned size)
 {
-    MemoryRegion *mr = _mr;
+    if (memory_region_wrong_endianness(mr)) {
+        switch (size) {
+        case 1:
+            break;
+        case 2:
+            *data = bswap16(*data);
+            break;
+        case 4:
+            *data = bswap32(*data);
+        default:
+            abort();
+        }
+    }
+}
+
+static uint64_t memory_region_dispatch_read(MemoryRegion *mr,
+                                            target_phys_addr_t addr,
+                                            unsigned size)
+{
+    uint64_t ret;
+
+    ret = memory_region_dispatch_read1(mr, addr, size);
+    adjust_endianness(mr, &ret, size);
+    return ret;
+}
 
+static void memory_region_dispatch_write(MemoryRegion *mr,
+                                         target_phys_addr_t addr,
+                                         uint64_t data,
+                                         unsigned size)
+{
     if (!memory_region_access_valid(mr, addr, size, true)) {
         return; /* FIXME: better signalling */
     }
 
+    adjust_endianness(mr, &data, size);
+
     if (!mr->ops->write) {
         mr->ops->old_mmio.write[bitops_ffsl(size)](mr->opaque, addr, data);
         return;
@@ -953,69 +980,6 @@ static void memory_region_write_thunk_n(void *_mr,
                               memory_region_write_accessor, mr);
 }
 
-static uint32_t memory_region_read_thunk_b(void *mr, target_phys_addr_t addr)
-{
-    return memory_region_read_thunk_n(mr, addr, 1);
-}
-
-static uint32_t memory_region_read_thunk_w(void *mr, target_phys_addr_t addr)
-{
-    uint32_t data;
-
-    data = memory_region_read_thunk_n(mr, addr, 2);
-    if (memory_region_wrong_endianness(mr)) {
-        data = bswap16(data);
-    }
-    return data;
-}
-
-static uint32_t memory_region_read_thunk_l(void *mr, target_phys_addr_t addr)
-{
-    uint32_t data;
-
-    data = memory_region_read_thunk_n(mr, addr, 4);
-    if (memory_region_wrong_endianness(mr)) {
-        data = bswap32(data);
-    }
-    return data;
-}
-
-static void memory_region_write_thunk_b(void *mr, target_phys_addr_t addr,
-                                        uint32_t data)
-{
-    memory_region_write_thunk_n(mr, addr, 1, data);
-}
-
-static void memory_region_write_thunk_w(void *mr, target_phys_addr_t addr,
-                                        uint32_t data)
-{
-    if (memory_region_wrong_endianness(mr)) {
-        data = bswap16(data);
-    }
-    memory_region_write_thunk_n(mr, addr, 2, data);
-}
-
-static void memory_region_write_thunk_l(void *mr, target_phys_addr_t addr,
-                                        uint32_t data)
-{
-    if (memory_region_wrong_endianness(mr)) {
-        data = bswap32(data);
-    }
-    memory_region_write_thunk_n(mr, addr, 4, data);
-}
-
-static CPUReadMemoryFunc * const memory_region_read_thunk[] = {
-    memory_region_read_thunk_b,
-    memory_region_read_thunk_w,
-    memory_region_read_thunk_l,
-};
-
-static CPUWriteMemoryFunc * const memory_region_write_thunk[] = {
-    memory_region_write_thunk_b,
-    memory_region_write_thunk_w,
-    memory_region_write_thunk_l,
-};
-
 void memory_region_init_io(MemoryRegion *mr,
                            const MemoryRegionOps *ops,
                            void *opaque,
@@ -1027,9 +991,7 @@ void memory_region_init_io(MemoryRegion *mr,
     mr->opaque = opaque;
     mr->terminates = true;
     mr->destructor = memory_region_destructor_iomem;
-    mr->ram_addr = cpu_register_io_memory(memory_region_read_thunk,
-                                          memory_region_write_thunk,
-                                          mr);
+    mr->ram_addr = cpu_register_io_memory(mr);
 }
 
 void memory_region_init_ram(MemoryRegion *mr,
@@ -1078,9 +1040,7 @@ void memory_region_init_rom_device(MemoryRegion *mr,
     mr->terminates = true;
     mr->destructor = memory_region_destructor_rom_device;
     mr->ram_addr = qemu_ram_alloc(size, mr);
-    mr->ram_addr |= cpu_register_io_memory(memory_region_read_thunk,
-                                           memory_region_write_thunk,
-                                           mr);
+    mr->ram_addr |= cpu_register_io_memory(mr);
     mr->ram_addr |= IO_MEM_ROMD;
 }
 
@@ -1552,15 +1512,13 @@ void set_system_io_map(MemoryRegion *mr)
 
 uint64_t io_mem_read(int io_index, target_phys_addr_t addr, unsigned size)
 {
-    return _io_mem_read[io_index][bitops_ffsl(size)](io_mem_opaque[io_index],
-                                                     addr);
+    return memory_region_dispatch_read(io_mem_region[io_index], addr, size);
 }
 
 void io_mem_write(int io_index, target_phys_addr_t addr,
                   uint64_t val, unsigned size)
 {
-    _io_mem_write[io_index][bitops_ffsl(size)](io_mem_opaque[io_index],
-                                               addr, val);
+    memory_region_dispatch_write(io_mem_region[io_index], addr, val, size);
 }
 
 typedef struct MemoryRegionList MemoryRegionList;
