diff --git a/exec.c b/exec.c
index 8ffde75..d0f45b4 100644
--- a/exec.c
+++ b/exec.c
@@ -2563,6 +2563,11 @@ static MemTxResult address_space_write_continue(AddressSpace *as, hwaddr addr,
             /* RAM case */
             ptr = qemu_map_ram_ptr(mr->ram_block, addr1);
             memcpy(ptr, buf, l);
+            if (mr->ram_ops) {
+                mr->ram_ops->write(ptr, buf, l, attrs);
+            } else {
+                memcpy(ptr, buf, l);
+            }
             invalidate_and_set_dirty(mr, addr1, l);
         }
 
@@ -2653,7 +2658,10 @@ MemTxResult address_space_read_continue(AddressSpace *as, hwaddr addr,
         } else {
             /* RAM case */
             ptr = qemu_map_ram_ptr(mr->ram_block, addr1);
-            memcpy(buf, ptr, l);
+            if (mr->ram_ops)
+                mr->ram_ops->read(buf, ptr, l, attrs);
+            else
+                memcpy(buf, ptr, l);
         }
 
         if (release_lock) {
@@ -2719,7 +2727,8 @@ enum write_rom_type {
 };
 
 static inline void cpu_physical_memory_write_rom_internal(AddressSpace *as,
-    hwaddr addr, const uint8_t *buf, int len, enum write_rom_type type)
+    hwaddr addr, const uint8_t *buf, int len, MemTxAttrs attrs,
+    enum write_rom_type type)
 {
     hwaddr l;
     uint8_t *ptr;
@@ -2739,7 +2748,11 @@ static inline void cpu_physical_memory_write_rom_internal(AddressSpace *as,
             ptr = qemu_map_ram_ptr(mr->ram_block, addr1);
             switch (type) {
             case WRITE_DATA:
-                memcpy(ptr, buf, l);
+                if (mr->ram_ops) {
+                    mr->ram_ops->write(ptr, buf, l, attrs);
+                } else {
+                    memcpy(ptr, buf, l);
+                }
                 invalidate_and_set_dirty(mr, addr1, l);
                 break;
             case FLUSH_CACHE:
@@ -2758,7 +2771,8 @@ static inline void cpu_physical_memory_write_rom_internal(AddressSpace *as,
 void cpu_physical_memory_write_rom(AddressSpace *as, hwaddr addr,
                                    const uint8_t *buf, int len)
 {
-    cpu_physical_memory_write_rom_internal(as, addr, buf, len, WRITE_DATA);
+    cpu_physical_memory_write_rom_internal(as, addr, buf, len,
+            MEMTXATTRS_UNSPECIFIED, WRITE_DATA);
 }
 
 void cpu_flush_icache_range(hwaddr start, int len)
@@ -2774,7 +2788,9 @@ void cpu_flush_icache_range(hwaddr start, int len)
     }
 
     cpu_physical_memory_write_rom_internal(&address_space_memory,
-                                           start, NULL, len, FLUSH_CACHE);
+                                           start, NULL, len,
+                                           MEMTXATTRS_UNSPECIFIED,
+                                           FLUSH_CACHE);
 }
 
 typedef struct {
@@ -2998,6 +3014,47 @@ void cpu_physical_memory_unmap(void *buffer, hwaddr len,
     return address_space_unmap(&address_space_memory, buffer, len, is_write, access_len);
 }
 
+#if defined(HOST_WORDS_BIGENDIAN)
+#define be_bswap(v, size) (v)
+#define le_bswap(v, size) glue(bswap, size)(v)
+#else
+#define le_bswap(v, size) (v)
+#define be_bswap(v, size) glue(bswap, size)(v)
+#endif
+
+static inline uint64_t rw_ops_ldq_internal(MemoryRegion *mr,
+                                           const uint8_t *ptr,
+                                           MemTxResult *ret,
+                                           MemTxAttrs attrs,
+                                           enum device_endian endian)
+{
+    uint64_t val;
+    uint64_t dst;
+
+    if (mr->ram_ops->read((uint8_t *)&dst, ptr, 8, attrs)) {
+        *ret = MEMTX_ERROR;
+        return 0;
+    }
+
+    switch (endian) {
+    case DEVICE_LITTLE_ENDIAN:
+        val = le_bswap(dst, 64);
+        break;
+    case DEVICE_BIG_ENDIAN:
+        val = be_bswap(dst, 64);
+        break;
+    default:
+#if defined(TARGET_WORDS_BIGENDIAN)
+        val = be_bswap(dst, 64);
+#else
+        val = le_bswap(dst, 64);
+#endif
+        break;
+    }
+
+    return val;
+}
+
 /* warning: addr must be aligned */
 static inline uint32_t address_space_ldl_internal(AddressSpace *as, hwaddr addr,
                                                   MemTxAttrs attrs,
@@ -3031,16 +3088,20 @@ static inline uint32_t address_space_ldl_internal(AddressSpace *as, hwaddr addr,
     } else {
         /* RAM case */
         ptr = qemu_map_ram_ptr(mr->ram_block, addr1);
-        switch (endian) {
-        case DEVICE_LITTLE_ENDIAN:
-            val = ldl_le_p(ptr);
-            break;
-        case DEVICE_BIG_ENDIAN:
-            val = ldl_be_p(ptr);
-            break;
-        default:
-            val = ldl_p(ptr);
-            break;
+        if (mr->ram_ops) {
+            val = rw_ops_ldq_internal(mr, ptr, &r, attrs, endian);
+        } else {
+            switch (endian) {
+            case DEVICE_LITTLE_ENDIAN:
+                val = ldl_le_p(ptr);
+                break;
+            case DEVICE_BIG_ENDIAN:
+                val = ldl_be_p(ptr);
+                break;
+            default:
+                val = ldl_p(ptr);
+                break;
+            }
         }
         r = MEMTX_OK;
     }
@@ -3124,16 +3185,20 @@ static inline uint64_t address_space_ldq_internal(AddressSpace *as, hwaddr addr,
     } else {
         /* RAM case */
         ptr = qemu_map_ram_ptr(mr->ram_block, addr1);
-        switch (endian) {
-        case DEVICE_LITTLE_ENDIAN:
-            val = ldq_le_p(ptr);
-            break;
-        case DEVICE_BIG_ENDIAN:
-            val = ldq_be_p(ptr);
-            break;
-        default:
-            val = ldq_p(ptr);
-            break;
+        if (mr->ram_ops) {
+            val = rw_ops_ldq_internal(mr, ptr, &r, attrs, endian);
+        } else {
+            switch (endian) {
+            case DEVICE_LITTLE_ENDIAN:
+                val = ldq_le_p(ptr);
+                break;
+            case DEVICE_BIG_ENDIAN:
+                val = ldq_be_p(ptr);
+                break;
+            default:
+                val = ldq_p(ptr);
+                break;
+            }
         }
         r = MEMTX_OK;
     }
@@ -3202,6 +3267,39 @@ uint32_t ldub_phys(AddressSpace *as, hwaddr addr)
     return address_space_ldub(as, addr, MEMTXATTRS_UNSPECIFIED, NULL);
 }
 
+static inline uint16_t rw_ops_lduw_internal(MemoryRegion *mr,
+                                            const uint8_t *ptr,
+                                            MemTxResult *ret,
+                                            MemTxAttrs attrs,
+                                            enum device_endian endian)
+{
+    uint64_t val;
+    uint64_t dst;
+
+    if (mr->ram_ops->read((uint8_t *)&dst, ptr, 2, attrs)) {
+        *ret = MEMTX_ERROR;
+        return 0;
+    }
+
+    switch (endian) {
+    case DEVICE_LITTLE_ENDIAN:
+        val = le_bswap(dst, 16);
+        break;
+    case DEVICE_BIG_ENDIAN:
+        val = be_bswap(dst, 16);
+        break;
+    default:
+#if defined(TARGET_WORDS_BIGENDIAN)
+        val = be_bswap(dst, 16);
+#else
+        val = le_bswap(dst, 16);
+#endif
+        break;
+    }
+
+    return (uint16_t) val;
+}
+
 /* warning: addr must be aligned */
 static inline uint32_t address_space_lduw_internal(AddressSpace *as,
                                                    hwaddr addr,
@@ -3237,16 +3335,20 @@ static inline uint32_t address_space_lduw_internal(AddressSpace *as,
     } else {
         /* RAM case */
         ptr = qemu_map_ram_ptr(mr->ram_block, addr1);
-        switch (endian) {
-        case DEVICE_LITTLE_ENDIAN:
-            val = lduw_le_p(ptr);
-            break;
-        case DEVICE_BIG_ENDIAN:
-            val = lduw_be_p(ptr);
-            break;
-        default:
-            val = lduw_p(ptr);
-            break;
+        if (mr->ram_ops) {
+            val = rw_ops_lduw_internal(mr, ptr, &r, attrs, endian);
+        } else {
+            switch (endian) {
+            case DEVICE_LITTLE_ENDIAN:
+                val = lduw_le_p(ptr);
+                break;
+            case DEVICE_BIG_ENDIAN:
+                val = lduw_be_p(ptr);
+                break;
+            default:
+                val = lduw_p(ptr);
+                break;
+            }
         }
         r = MEMTX_OK;
     }
@@ -3341,6 +3443,37 @@ void stl_phys_notdirty(AddressSpace *as, hwaddr addr, uint32_t val)
     address_space_stl_notdirty(as, addr, val, MEMTXATTRS_UNSPECIFIED, NULL);
 }
 
+static inline void rw_ops_stl_internal(MemoryRegion *mr, MemTxResult *r,
+                                       uint8_t *ptr, uint32_t val,
+                                       MemTxAttrs attrs,
+                                       enum device_endian endian)
+{
+    uint32_t data;
+
+    switch (endian) {
+    case DEVICE_LITTLE_ENDIAN:
+        data = le_bswap(val, 32);
+        break;
+    case DEVICE_BIG_ENDIAN:
+        data = be_bswap(val, 32);
+        break;
+    default:
+#if defined(TARGET_WORDS_BIGENDIAN)
+        data = be_bswap(val, 32);
+#else
+        data = le_bswap(val, 32);
+#endif
+        break;
+    }
+
+    if (mr->ram_ops->write(ptr, (uint8_t *)&data, 4, attrs)) {
+        *r = MEMTX_ERROR;
+        return;
+    }
+
+    *r = MEMTX_OK;
+}
+
 /* warning: addr must be aligned */
 static inline void address_space_stl_internal(AddressSpace *as,
                                               hwaddr addr, uint32_t val,
@@ -3374,19 +3507,26 @@ static inline void address_space_stl_internal(AddressSpace *as,
     } else {
         /* RAM case */
         ptr = qemu_map_ram_ptr(mr->ram_block, addr1);
-        switch (endian) {
-        case DEVICE_LITTLE_ENDIAN:
-            stl_le_p(ptr, val);
-            break;
-        case DEVICE_BIG_ENDIAN:
-            stl_be_p(ptr, val);
-            break;
-        default:
-            stl_p(ptr, val);
-            break;
+        if (mr->ram_ops) {
+            rw_ops_stl_internal(mr, &r, ptr, val, attrs, endian);
+            if (r == MEMTX_OK) {
+                invalidate_and_set_dirty(mr, addr1, 4);
+            }
+        } else {
+            switch (endian) {
+            case DEVICE_LITTLE_ENDIAN:
+                stl_le_p(ptr, val);
+                break;
+            case DEVICE_BIG_ENDIAN:
+                stl_be_p(ptr, val);
+                break;
+            default:
+                stl_p(ptr, val);
+                break;
+            }
+            invalidate_and_set_dirty(mr, addr1, 4);
+            r = MEMTX_OK;
         }
-        invalidate_and_set_dirty(mr, addr1, 4);
-        r = MEMTX_OK;
     }
     if (result) {
         *result = r;
@@ -3451,6 +3591,37 @@ void stb_phys(AddressSpace *as, hwaddr addr, uint32_t val)
     address_space_stb(as, addr, val, MEMTXATTRS_UNSPECIFIED, NULL);
 }
 
+static inline void rw_ops_stw_internal(MemoryRegion *mr, MemTxResult *r,
+                                       uint8_t *ptr, uint32_t val,
+                                       MemTxAttrs attrs,
+                                       enum device_endian endian)
+{
+    uint32_t data;
+
+    switch (endian) {
+    case DEVICE_LITTLE_ENDIAN:
+        data = le_bswap(val, 16);
+        break;
+    case DEVICE_BIG_ENDIAN:
+        data = be_bswap(val, 16);
+        break;
+    default:
+#if defined(TARGET_WORDS_BIGENDIAN)
+        data = be_bswap(val, 16);
+#else
+        data = le_bswap(val, 16);
+#endif
+        break;
+    }
+
+    if (mr->ram_ops->write(ptr, (uint8_t *)&data, 2, attrs)) {
+        *r = MEMTX_ERROR;
+        return;
+    }
+
+    *r = MEMTX_OK;
+}
+
 /* warning: addr must be aligned */
 static inline void address_space_stw_internal(AddressSpace *as,
                                               hwaddr addr, uint32_t val,
@@ -3483,19 +3654,26 @@ static inline void address_space_stw_internal(AddressSpace *as,
     } else {
         /* RAM case */
         ptr = qemu_map_ram_ptr(mr->ram_block, addr1);
-        switch (endian) {
-        case DEVICE_LITTLE_ENDIAN:
-            stw_le_p(ptr, val);
-            break;
-        case DEVICE_BIG_ENDIAN:
-            stw_be_p(ptr, val);
-            break;
-        default:
-            stw_p(ptr, val);
-            break;
+        if (mr->ram_ops) {
+            rw_ops_stw_internal(mr, &r, ptr, val, attrs, endian);
+            if (r == MEMTX_OK) {
+                invalidate_and_set_dirty(mr, addr1, 2);
+            }
+        } else {
+            switch (endian) {
+            case DEVICE_LITTLE_ENDIAN:
+                stw_le_p(ptr, val);
+                break;
+            case DEVICE_BIG_ENDIAN:
+                stw_be_p(ptr, val);
+                break;
+            default:
+                stw_p(ptr, val);
+                break;
+            }
+            invalidate_and_set_dirty(mr, addr1, 2);
+            r = MEMTX_OK;
         }
-        invalidate_and_set_dirty(mr, addr1, 2);
-        r = MEMTX_OK;
     }
     if (result) {
         *result = r;
@@ -3613,12 +3791,12 @@ int cpu_memory_rw_debug(CPUState *cpu, target_ulong addr,
             l = len;
         phys_addr += (addr & ~TARGET_PAGE_MASK);
         if (is_write) {
-            cpu_physical_memory_write_rom(cpu->cpu_ases[asidx].as,
-                                          phys_addr, buf, l);
+            cpu_physical_memory_write_rom_internal(cpu->cpu_ases[asidx].as,
+                                          phys_addr, buf, l, attrs,
+                                          WRITE_DATA);
         } else {
             address_space_rw(cpu->cpu_ases[asidx].as, phys_addr,
-                             MEMTXATTRS_UNSPECIFIED,
-                             buf, l, 0);
+                             attrs, buf, l, 0);
         }
         len -= l;
         buf += l;
diff --git a/include/exec/memory.h b/include/exec/memory.h
index 3e4d416..80af3b0 100644
--- a/include/exec/memory.h
+++ b/include/exec/memory.h
@@ -146,6 +146,18 @@ struct MemoryRegionOps {
     const MemoryRegionMmio old_mmio;
 };
 
+/* Memory Region RAM callback */
+typedef struct MemoryRegionRAMReadWriteOps MemoryRegionRAMReadWriteOps;
+
+struct MemoryRegionRAMReadWriteOps {
+    /* Write data into guest memory */
+    int (*write) (uint8_t *dest, const uint8_t *src,
+                  uint32_t len, MemTxAttrs attrs);
+    /* Read data from guest memory */
+    int (*read) (uint8_t *dest, const uint8_t *src,
+                 uint32_t len, MemTxAttrs attrs);
+};
+
 typedef struct MemoryRegionIOMMUOps MemoryRegionIOMMUOps;
 
 struct MemoryRegionIOMMUOps {
@@ -179,6 +191,7 @@ struct MemoryRegion {
     RAMBlock *ram_block;
     Object *owner;
     const MemoryRegionIOMMUOps *iommu_ops;
+    const MemoryRegionRAMReadWriteOps *ram_ops;
 
     const MemoryRegionOps *ops;
     void *opaque;
@@ -506,6 +519,18 @@ static inline void memory_region_init_reservation(MemoryRegion *mr,
 }
 
 /**
+ * memory_region_set_ram_ops: Set the Read/Write ops for accessing the RAM
+ *
+ * @mr: the #MemoryRegion to be initialized
+ * @ops: a function that will be used to read/write @target region
+ */
+static inline void memory_region_set_ram_ops(MemoryRegion *mr,
+                               const MemoryRegionRAMReadWriteOps *ops)
+{
+    mr->ram_ops = ops;
+}
+
+/**
  * memory_region_init_iommu: Initialize a memory region that translates
  * addresses
  *
