diff --git a/hw/pci-host/pam.c b/hw/pci-host/pam.c
index 454dd120db..787428c7d8 100644
--- a/hw/pci-host/pam.c
+++ b/hw/pci-host/pam.c
@@ -28,43 +28,133 @@
  */
 
 #include "qemu/osdep.h"
+#include "qapi/error.h"
 #include "hw/pci-host/pam.h"
 
+static void
+pam_rmem_write(void *opaque, hwaddr addr, uint64_t val, unsigned int size)
+{
+    PAMMemoryRegion * const pam = (PAMMemoryRegion *)opaque;
+
+    (void)memory_region_dispatch_write(pam->pci_mr, pam->offset + addr, val,
+                                       size_memop(size), MEMTXATTRS_UNSPECIFIED);
+}
+
+static uint64_t
+pam_wmem_read(void *opaque, hwaddr addr, unsigned int size)
+{
+    PAMMemoryRegion * const pam = (PAMMemoryRegion *)opaque;
+    uint64_t val = (uint64_t)~0;
+
+    (void)memory_region_dispatch_read(pam->pci_mr, pam->offset + addr, &val,
+                                      size_memop(size), MEMTXATTRS_UNSPECIFIED);
+
+    return val;
+}
+
+static void
+pam_wmem_write(void *opaque, hwaddr addr, uint64_t val, unsigned int size)
+{
+    PAMMemoryRegion * const pam = (PAMMemoryRegion *)opaque;
+
+    switch (size) {
+    case 1:
+        stb_phys(&pam->ram_as, pam->offset + addr, val);
+        break;
+    case 2:
+        stw_le_phys(&pam->ram_as, pam->offset + addr, val);
+        break;
+    case 4:
+        stl_le_phys(&pam->ram_as, pam->offset + addr, val);
+        break;
+    case 8:
+        stq_le_phys(&pam->ram_as, pam->offset + addr, val);
+        break;
+    default:
+        g_assert_not_reached();
+    }
+}
+
+static const MemoryRegionOps pam_rmem_ops = {
+    .write = pam_rmem_write,
+};
+
+static const MemoryRegionOps pam_wmem_ops = {
+    .read = pam_wmem_read,
+    .write = pam_wmem_write,
+    .valid = {
+        .min_access_size = 1,
+        .max_access_size = 8,
+        .unaligned = true,
+    },
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 8,
+        .unaligned = true,
+    },
+};
+
 void init_pam(DeviceState *dev, MemoryRegion *ram_memory,
-              MemoryRegion *system_memory, MemoryRegion *pci_address_space,
-              PAMMemoryRegion *mem, uint32_t start, uint32_t size)
+              MemoryRegion *system, MemoryRegion *pci,
+              PAMMemoryRegion *pam, uint32_t start, uint32_t size)
 {
+    char name[12] = "pam-splitr";
     int i;
 
-    /* RAM */
-    memory_region_init_alias(&mem->alias[3], OBJECT(dev), "pam-ram", ram_memory,
-                             start, size);
-    /* ROM (XXX: not quite correct) */
-    memory_region_init_alias(&mem->alias[1], OBJECT(dev), "pam-rom", ram_memory,
-                             start, size);
-    memory_region_set_readonly(&mem->alias[1], true);
+    name[10] = (start >> 14) + 17;
+    name[11] = '\0';
+
+    /* Forward all memory accesses to the bus.  */
+    memory_region_init_alias(&pam->alias[0], OBJECT(dev), "pam-pci",
+                             pci, start, size);
 
-    /* XXX: should distinguish read/write cases */
-    memory_region_init_alias(&mem->alias[0], OBJECT(dev), "pam-pci", pci_address_space,
-                             start, size);
-    memory_region_init_alias(&mem->alias[2], OBJECT(dev), "pam-pci", ram_memory,
-                             start, size);
+    /* Split modes */
+    /* Forward reads to RAM, writes to the bus.  */
+    memory_region_init_rom_device(&pam->alias[1], OBJECT(dev),
+                                  &pam_rmem_ops, pam, name, size,
+                                  &error_fatal);
+
+    /* Forward writes to RAM, reads to the bus.  */
+    name[9] = 'w';
+    memory_region_init_io(&pam->alias[2], OBJECT(dev), &pam_wmem_ops,
+                          pam, name, size);
 
     memory_region_transaction_begin();
-    for (i = 0; i < 4; ++i) {
-        memory_region_set_enabled(&mem->alias[i], false);
-        memory_region_add_subregion_overlap(system_memory, start,
-                                            &mem->alias[i], 1);
+    for (i = 0; i < 3; ++i) {
+        /* The caller is responsible for the initial state.  */
+        memory_region_set_enabled(&pam->alias[i], false);
+        memory_region_add_subregion_overlap(system, start,
+                                            &pam->alias[i], 1);
     }
     memory_region_transaction_commit();
-    mem->current = 0;
+    address_space_init(&pam->ram_as, ram_memory, "PAMRAM");
+    pam->current = 0;
+    pam->pci_mr = pci;
+    pam->offset = start;
 }
 
-void pam_update(PAMMemoryRegion *pam, int idx, uint8_t val)
+void pam_update(PAMMemoryRegion *pam, uint8_t idx, uint8_t val)
 {
-    assert(0 <= idx && idx < PAM_REGIONS_COUNT);
+    uint8_t ai;
+    assert(idx < PAM_REGIONS_COUNT);
+
+    ai = (val >> ((!(idx & 1)) * 4)) & PAM_ATTR_MASK;
 
+    /* The caller is responsible for setting up a transaction.  */
     memory_region_set_enabled(&pam->alias[pam->current], false);
-    pam->current = (val >> ((!(idx & 1)) * 4)) & PAM_ATTR_MASK;
-    memory_region_set_enabled(&pam->alias[pam->current], true);
+    switch (ai) {
+    case 1: {
+        const hwaddr pamsize = memory_region_size(&pam->alias[ai]);
+
+        address_space_read(&pam->ram_as, pam->offset,
+                           MEMTXATTRS_UNSPECIFIED,
+                           memory_region_get_ram_ptr(&pam->alias[ai]),
+                           pamsize);
+    }
+    /* FALLTHROUGH */
+    case 0:
+    case 2:
+        memory_region_set_enabled(&pam->alias[ai], true);
+        pam->current = ai;
+    }
 }
diff --git a/include/hw/pci-host/pam.h b/include/hw/pci-host/pam.h
index c1fd06ba2a..6f12210498 100644
--- a/include/hw/pci-host/pam.h
+++ b/include/hw/pci-host/pam.h
@@ -83,12 +83,15 @@
 #define PAM_REGIONS_COUNT       13
 
 typedef struct PAMMemoryRegion {
-    MemoryRegion alias[4];  /* index = PAM value */
+    AddressSpace ram_as;
+    MemoryRegion alias[3];  /* index = PAM value */
     unsigned current;
+    ram_addr_t offset;
+    MemoryRegion *pci_mr;
 } PAMMemoryRegion;
 
 void init_pam(DeviceState *dev, MemoryRegion *ram, MemoryRegion *system,
               MemoryRegion *pci, PAMMemoryRegion *mem, uint32_t start, uint32_t size);
-void pam_update(PAMMemoryRegion *mem, int idx, uint8_t val);
+void pam_update(PAMMemoryRegion *mem, uint8_t idx, uint8_t val);
 
 #endif /* QEMU_PAM_H */
