[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
- [Qemu-devel] [PATCH 0/3] PAM emulation improvement, Efimov Vasily, 2015/07/16
- [Qemu-devel] [PATCH 1/3] memory: make function invalidate_and_set_dirty public, Efimov Vasily, 2015/07/16
- [Qemu-devel] [PATCH 2/3] memory: make function memory_access_is_direct public, Efimov Vasily, 2015/07/16
- [Qemu-devel] [PATCH 3/3] PAM: make PAM emulation closer to documentation,
Efimov Vasily <=
- Re: [Qemu-devel] [PATCH 3/3] PAM: make PAM emulation closer to documentation, Paolo Bonzini, 2015/07/16
- Re: [Qemu-devel] [PATCH 3/3] PAM: make PAM emulation closer to documentation, Ефимов Василий, 2015/07/16
- Re: [Qemu-devel] [PATCH 3/3] PAM: make PAM emulation closer to documentation, Paolo Bonzini, 2015/07/16
- Re: [Qemu-devel] [PATCH 3/3] PAM: make PAM emulation closer to documentation, Ефимов Василий, 2015/07/16
- Re: [Qemu-devel] [PATCH 3/3] PAM: make PAM emulation closer to documentation, Paolo Bonzini, 2015/07/16
- Re: [Qemu-devel] [PATCH 3/3] PAM: make PAM emulation closer to documentation, Ефимов Василий, 2015/07/17
- Re: [Qemu-devel] [PATCH 3/3] PAM: make PAM emulation closer to documentation, Paolo Bonzini, 2015/07/17