diff --git a/include/exec/ioport.h b/include/exec/ioport.h
index fc28350..398fe14 100644
--- a/include/exec/ioport.h
+++ b/include/exec/ioport.h
@@ -55,13 +55,14 @@ uint32_t cpu_inl(pio_addr_t addr);
 
 struct MemoryRegion;
 struct MemoryRegionPortio;
+struct PortioListState;
 
 typedef struct PortioList {
     const struct MemoryRegionPortio *ports;
     struct MemoryRegion *address_space;
     unsigned nr;
     struct MemoryRegion **regions;
-    struct MemoryRegion **aliases;
+    struct PortioListState **states;
     void *opaque;
     const char *name;
 } PortioList;
diff --git a/ioport.c b/ioport.c
index a0ac2a0..d26d3e1 100644
--- a/ioport.c
+++ b/ioport.c
@@ -28,6 +28,7 @@
 #include "exec/ioport.h"
 #include "trace.h"
 #include "exec/memory.h"
+#include "qemu/host-utils.h"
 
 /***********************************************************/
 /* IO Port */
@@ -330,6 +331,12 @@ uint32_t cpu_inl(pio_addr_t addr)
     return val;
 }
 
+typedef struct PortioListState {
+    const MemoryRegionPortio *portio;
+    uint32_t offset;
+    void *opaque;
+} PortioListState;
+
 void portio_list_init(PortioList *piolist,
                       const MemoryRegionPortio *callbacks,
                       void *opaque, const char *name)
@@ -343,16 +350,95 @@ void portio_list_init(PortioList *piolist,
     piolist->ports = callbacks;
     piolist->nr = 0;
     piolist->regions = g_new0(MemoryRegion *, n);
-    piolist->aliases = g_new0(MemoryRegion *, n);
+    piolist->states = g_new0(PortioListState *, n);
     piolist->address_space = NULL;
     piolist->opaque = opaque;
     piolist->name = name;
 }
 
+static const MemoryRegionPortio *portio_find(const MemoryRegionPortio *mrp,
+                                             uint64_t offset,
+                                             unsigned int width, bool write)
+{
+    for (; mrp->size; ++mrp) {
+        if (offset >= mrp->offset && offset < mrp->offset + mrp->len
+            && width == mrp->size
+            && (write ? (bool)mrp->write : (bool)mrp->read)) {
+            return mrp;
+        }
+    }
+    return NULL;
+}
+
+static uint64_t portio_read(void *opaque, hwaddr addr, unsigned int size)
+{
+    const PortioListState *s = opaque;
+    const MemoryRegionPortio *mrp;
+    uint16_t data;
+
+    addr += s->portio->offset;
+    mrp = portio_find(s->portio, addr, size, false);
+    if (!mrp && size == 2) {
+        mrp = portio_find(s->portio, addr, 1, false);
+        assert(mrp);
+        data = mrp->read(s->opaque, addr) |
+              (mrp->read(s->opaque, addr + 1) << 8);
+        return data;
+    }
+    assert(mrp);
+    return mrp->read(s->opaque, addr);
+}
+
+static void portio_write(void *opaque, hwaddr addr, uint64_t data,
+                         unsigned int size)
+{
+    const PortioListState *s = opaque;
+    const MemoryRegionPortio *mrp;
+
+    addr += s->offset;
+    mrp = portio_find(s->portio, addr, size, true);
+    if (!mrp && size == 2) {
+        mrp = portio_find(s->portio, addr, 1, true);
+        assert(mrp);
+        mrp->write(s->opaque, addr, data & 0xff);
+        mrp->write(s->opaque, addr + 1, data >> 8);
+        return;
+    }
+    assert(mrp);
+    mrp->write(s->opaque, addr, data);
+}
+
+static bool portio_accepts(void *opaque, hwaddr addr, unsigned int size,
+                           bool is_write)
+{
+    const PortioListState *s = opaque;
+    const MemoryRegionPortio *mrp;
+
+    addr += s->offset;
+    mrp = portio_find(s->portio, addr, size, is_write);
+    if (!mrp && size == 2) {
+        mrp = portio_find(s->portio, addr, 1, is_write);
+    }
+    if (!mrp) {
+        LOG_UNUSED_IOPORT("unused %s%c: port=0x%04" HWADDR_PRIx "\n",
+                           is_write ? "out" : "in",
+                           size == 1 ? 'b' : size == 2 ? 'w' : 'l',
+                           addr);
+    }
+    return mrp;
+}
+
+const MemoryRegionOps portio_ops = {
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .read = portio_read,
+    .write = portio_write,
+    .valid.accepts = portio_accepts,
+};
+
 void portio_list_destroy(PortioList *piolist)
 {
     g_free(piolist->regions);
-    g_free(piolist->aliases);
+    g_free(piolist->states);
 }
 
 static void portio_list_add_1(PortioList *piolist,
@@ -361,8 +447,8 @@ static void portio_list_add_1(PortioList *piolist,
                               unsigned off_low, unsigned off_high)
 {
     MemoryRegionPortio *pio;
-    MemoryRegionOps *ops;
-    MemoryRegion *region, *alias;
+    MemoryRegion *region = g_new(MemoryRegion, 1);
+    PortioListState *s = g_new(PortioListState, 1);
     unsigned i;
 
     /* Copy the sub-list and null-terminate it.  */
@@ -370,28 +456,20 @@ static void portio_list_add_1(PortioList *piolist,
     memcpy(pio, pio_init, sizeof(MemoryRegionPortio) * count);
     memset(pio + count, 0, sizeof(MemoryRegionPortio));
 
-    /* Adjust the offsets to all be zero-based for the region.  */
+    /* Adjust the offsets to all be address-based for the region. */
     for (i = 0; i < count; ++i) {
-        pio[i].offset -= off_low;
+        pio[i].offset += start;
     }
 
-    ops = g_new0(MemoryRegionOps, 1);
-    ops->old_portio = pio;
-
-    region = g_new(MemoryRegion, 1);
-    alias = g_new(MemoryRegion, 1);
-    /*
-     * Use an alias so that the callback is called with an absolute address,
-     * rather than an offset relative to to start + off_low.
-     */
-    memory_region_init_io(region, ops, piolist->opaque, piolist->name,
-                          INT64_MAX);
-    memory_region_init_alias(alias, piolist->name,
-                             region, start + off_low, off_high - off_low);
+    s->offset = start + off_low;
+    s->portio = pio;
+    s->opaque = piolist->opaque;
+    memory_region_init_io(region, &portio_ops, s, piolist->name,
+                          off_high - off_low);
     memory_region_add_subregion(piolist->address_space,
-                                start + off_low, alias);
+                                s->offset, region);
     piolist->regions[piolist->nr] = region;
-    piolist->aliases[piolist->nr] = alias;
+    piolist->states[piolist->nr] = s;
     ++piolist->nr;
 }
 
@@ -434,19 +512,19 @@ void portio_list_add(PortioList *piolist,
 
 void portio_list_del(PortioList *piolist)
 {
-    MemoryRegion *mr, *alias;
+    MemoryRegion *mr;
+    PortioListState *s;
     unsigned i;
 
     for (i = 0; i < piolist->nr; ++i) {
         mr = piolist->regions[i];
-        alias = piolist->aliases[i];
-        memory_region_del_subregion(piolist->address_space, alias);
-        memory_region_destroy(alias);
+        s = piolist->states[i];
+        memory_region_del_subregion(piolist->address_space, mr);
         memory_region_destroy(mr);
         g_free((MemoryRegionOps *)mr->ops);
         g_free(mr);
-        g_free(alias);
+        g_free(s);
         piolist->regions[i] = NULL;
-        piolist->aliases[i] = NULL;
+        piolist->states[i] = NULL;
     }
 }
