qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH 3/3] PAM: make PAM emulation closer to documentation


From: Efimov Vasily
Subject: [Qemu-devel] [PATCH 3/3] PAM: make PAM emulation closer to documentation
Date: Thu, 16 Jul 2015 11:35:04 +0300

This patch improves PAM emulation.

PAM defines 4 memory access redirection modes. In mode 1 reads are directed to
RAM and writes are directed to PCI. In mode 2 it is contrary. In mode 0 all
access is directed to PCI. In mode 3 it is directed to RAM. Modes 0 and 3 are
well emulated but modes 1 and 2 are not. The cause is: aliases are used
while more complicated logic is required.

The idea is to use ROM device like memory regions for mode 1 and 2 emulation
instead of aliases. Writes are directed to proper destination region by
specified I/O callback. Read redirection depends on type of source region.
In most cases source region is RAM (or ROM), so ram_addr of PAM region is set to
ram_addr of source region with offset. Otherwise, when source region is an I/O
region, reading is redirected to source region read callback by PAM region one.

Read source and write destination regions are updated by the memory
commit callback.

Note that we cannot use I/O region for PAM as it will violate "trying to execute
code outside RAM or ROM" assertion.

Signed-off-by: Efimov Vasily <address@hidden>
---
 hw/pci-host/pam.c         | 238 +++++++++++++++++++++++++++++++++++++++++-----
 include/hw/pci-host/pam.h |  10 +-
 2 files changed, 223 insertions(+), 25 deletions(-)

diff --git a/hw/pci-host/pam.c b/hw/pci-host/pam.c
index 17d826c..9729b2b 100644
--- a/hw/pci-host/pam.c
+++ b/hw/pci-host/pam.c
@@ -27,43 +27,233 @@
  * THE SOFTWARE.
  */
 
-#include "qom/object.h"
-#include "sysemu/sysemu.h"
 #include "hw/pci-host/pam.h"
+#include "exec/address-spaces.h"
+#include "exec/memory-internal.h"
+#include "qemu/bswap.h"
+
+static void pam_write(void *opaque, hwaddr addr, uint64_t data,
+                      unsigned size)
+{
+    PAMMemoryRegion *pam = (PAMMemoryRegion *) opaque;
+    void *ptr;
+
+    /* Destination region can be both RAM and IO. */
+    if (!memory_access_is_direct(pam->mr_write_to, true)) {
+        memory_region_dispatch_write(pam->mr_write_to,
+                                     addr + pam->write_offset, data, size,
+                                     MEMTXATTRS_UNSPECIFIED);
+    } else {
+        ptr = memory_region_get_ram_ptr(pam->mr_write_to) + addr
+                                                          + pam->write_offset;
+
+        switch (size) {
+        case 1:
+            stb_p(ptr, data);
+            break;
+        case 2:
+            stw_he_p(ptr, data);
+            break;
+        case 4:
+            stl_he_p(ptr, data);
+            break;
+        case 8:
+            stq_he_p(ptr, data);
+            break;
+        default:
+            abort();
+        }
+
+        invalidate_and_set_dirty(pam->mr_write_to, addr + pam->pam_offset,
+                                 size);
+    }
+}
+
+static uint64_t pam_read(void *opaque, hwaddr addr, unsigned size)
+{
+    PAMMemoryRegion *pam = (PAMMemoryRegion *) opaque;
+    uint64_t ret = 0;
+
+    /* Source region can be IO only. */
+    memory_region_dispatch_read(pam->mr_read_from, pam->read_offset + addr,
+                                &ret, size, MEMTXATTRS_UNSPECIFIED);
+
+    return ret;
+}
+
+static MemoryRegionOps pam_ops = {
+    .write = pam_write,
+    .read = pam_read,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void pam_set_current(PAMMemoryRegion *pam, unsigned new_current)
+{
+    assert(new_current <= 3);
+
+    if (new_current != pam->current) {
+#ifdef DEBUG_PAM
+        printf("PAM 0x"TARGET_FMT_plx": %d <=> %s\n", pam->pam_offset,
+               new_current, pam->region[new_current].name);
+#endif
+        memory_region_set_enabled(&pam->region[pam->current], false);
+        pam->current = new_current;
+        memory_region_set_enabled(&pam->region[new_current], true);
+    }
+}
+
+static void pam_leaf_mr_lookup(MemoryRegion *mr, hwaddr offset,
+                               MemoryRegion **leaf, hwaddr *offset_within_leaf)
+{
+    MemoryRegion *other;
+    if (mr->alias) {
+        pam_leaf_mr_lookup(mr->alias, offset + mr->alias_offset, leaf,
+                           offset_within_leaf);
+    } else {
+        if (QTAILQ_EMPTY(&mr->subregions)
+            && int128_lt(int128_make64(offset), mr->size)) {
+            *leaf = mr;
+            *offset_within_leaf = offset;
+        } else {
+            QTAILQ_FOREACH(other, &mr->subregions, subregions_link) {
+                if (!other->enabled) {
+                    continue;
+                }
+                if (other->addr <= offset && int128_lt(int128_make64(offset),
+                        int128_add(int128_make64(other->addr), other->size))) {
+                    pam_leaf_mr_lookup(other, offset - other->addr, leaf,
+                                       offset_within_leaf);
+                    if (*leaf) {
+                        return;
+                    }
+                }
+            }
+            *leaf = NULL;
+        }
+    }
+}
+
+static bool mr_has_mr(MemoryRegion *container, MemoryRegion *mr,
+                      hwaddr *offset)
+{
+    hwaddr tmp_offset;
+    MemoryRegion *other;
+
+    if (container == mr) {
+        *offset = 0;
+        return true;
+    }
+    if (container->alias) {
+        if (mr_has_mr(container->alias, mr, &tmp_offset)) {
+            *offset = tmp_offset - container->alias_offset;
+            return true;
+        } else {
+            return false;
+        }
+    }
+    QTAILQ_FOREACH(other, &container->subregions, subregions_link) {
+        if (mr_has_mr(other, mr, &tmp_offset)) {
+            *offset = tmp_offset + other->addr;
+            return true;
+        }
+    }
+    return false;
+}
+
+static void pam_mem_commit(MemoryListener *listener)
+{
+    PAMMemoryRegion *pam = container_of(listener, PAMMemoryRegion, listener);
+    MemoryRegion *cur, *read_src, *write_dst;
+    hwaddr offset_within_leaf;
+
+    if (pam->current == 1) {
+        /* Read from RAM and write to PCI */
+        /* Note pam->region[3].alias points to RAM by creation. */
+        read_src = pam->region[3].alias;
+        pam->read_offset = pam->pam_offset;
+
+        pam_leaf_mr_lookup(&pam->region[0], 0, &write_dst, 
&offset_within_leaf);
+
+        assert(write_dst);
+
+        pam->write_offset = offset_within_leaf;
+    } else if (pam->current == 2) {
+        /* Read from PCI and write to RAM */
+        pam_leaf_mr_lookup(&pam->region[0], 0, &read_src, &offset_within_leaf);
+
+        assert(read_src);
+
+        pam->read_offset = offset_within_leaf;
+
+        write_dst = pam->region[3].alias;
+        pam->write_offset = pam->pam_offset;
+    } else {
+        return;
+    }
+
+    cur = &pam->region[pam->current];
+
+    if (memory_region_is_ram(read_src)) {
+        /* Current region parent is system_memory by creation. */
+        assert(mr_has_mr(cur->container, read_src, &offset_within_leaf));
+        cur->ram_addr = read_src->ram_addr + pam->pam_offset
+                      - offset_within_leaf;
+        cur->rom_device = true;
+        cur->romd_mode = true;
+    } else {
+        cur->ram_addr = ~(ram_addr_t)0;
+        cur->rom_device = false;
+        cur->romd_mode = false;
+    }
+
+    pam->mr_read_from = read_src;
+    pam->mr_write_to = write_dst;
+}
 
 void init_pam(DeviceState *dev, MemoryRegion *ram_memory,
               MemoryRegion *system_memory, MemoryRegion *pci_address_space,
-              PAMMemoryRegion *mem, uint32_t start, uint32_t size)
+              PAMMemoryRegion *pam, uint32_t start, uint32_t size)
 {
     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);
-
-    /* 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);
-
-    for (i = 0; i < 4; ++i) {
-        memory_region_set_enabled(&mem->alias[i], false);
+    /* All access to PCI */
+    memory_region_init_alias(&pam->region[0], OBJECT(dev), "pam-pci",
+                             pci_address_space, start, size);
+
+    /* Read from RAM and write to PCI */
+    memory_region_init_io(&pam->region[1], OBJECT(dev), &pam_ops, pam,
+            "pam-r-ram-w-pci", size);
+
+    /* Read from PCI and write to RAM */
+    memory_region_init_io(&pam->region[2], OBJECT(dev), &pam_ops, pam,
+            "pam-r-pci-w-ram", size);
+
+    /* All access to RAM */
+    memory_region_init_alias(&pam->region[3], OBJECT(dev), "pam-ram",
+                             ram_memory, start, size);
+
+    memory_region_add_subregion_overlap(system_memory, start,
+                                        &pam->region[0], 1);
+    memory_region_set_enabled(&pam->region[0], true);
+    for (i = 1; i < 4; i++) {
+        memory_region_set_enabled(&pam->region[i], false);
         memory_region_add_subregion_overlap(system_memory, start,
-                                            &mem->alias[i], 1);
+                                            &pam->region[i], 1);
     }
-    mem->current = 0;
+
+    pam->current = 0;
+    pam->pam_offset = start;
+
+    pam->listener = (MemoryListener) {
+        .commit = pam_mem_commit,
+    };
+
+    memory_listener_register(&pam->listener, &address_space_memory);
 }
 
 void pam_update(PAMMemoryRegion *pam, int idx, uint8_t val)
 {
     assert(0 <= idx && idx <= 12);
 
-    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);
+    pam_set_current(pam, (val >> ((!(idx & 1)) * 4)) & PAM_ATTR_MASK);
 }
diff --git a/include/hw/pci-host/pam.h b/include/hw/pci-host/pam.h
index 6116c63..b918029 100644
--- a/include/hw/pci-host/pam.h
+++ b/include/hw/pci-host/pam.h
@@ -53,6 +53,8 @@
 #include "qemu-common.h"
 #include "exec/memory.h"
 
+/* #define DEBUG_PAM */
+
 #define SMRAM_C_BASE    0xa0000
 #define SMRAM_C_END     0xc0000
 #define SMRAM_C_SIZE    0x20000
@@ -82,8 +84,14 @@
 #define SMRAM_C_BASE_SEG       ((uint8_t)0x2)  /* hardwired to b010 */
 
 typedef struct PAMMemoryRegion {
-    MemoryRegion alias[4];  /* index = PAM value */
+    MemoryRegion region[4];  /* index = PAM value */
     unsigned current;
+    hwaddr pam_offset;
+    MemoryRegion *mr_write_to;
+    hwaddr write_offset;
+    MemoryRegion *mr_read_from;
+    hwaddr read_offset;
+    MemoryListener listener;
 } PAMMemoryRegion;
 
 void init_pam(DeviceState *dev, MemoryRegion *ram, MemoryRegion *system,
-- 
1.9.1




reply via email to

[Prev in Thread] Current Thread [Next in Thread]