summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexander Graf <agraf@suse.de>2010-12-08 12:05:36 +0100
committerBlue Swirl <blauwirbel@gmail.com>2010-12-11 15:24:25 +0000
commitdd310534e3bf8045096654df41471fd7132887b2 (patch)
tree2511555233e06d50d07baad7ec8dfdccd17d7172
parent85882c71a946796c0ddc87dc84cc6fcb05b375c7 (diff)
downloadhqemu-dd310534e3bf8045096654df41471fd7132887b2.zip
hqemu-dd310534e3bf8045096654df41471fd7132887b2.tar.gz
exec: introduce endianness swapped mmio
The way we're currently modeling mmio is too simplified. We assume that every device has the same endianness as the target CPU. In reality, most devices are little endian (all PCI and ISA ones I'm aware of). Some are big endian (special system devices) and a very little fraction is target native endian (fw_cfg). So instead of assuming every device to be native endianness, let's move to a model where the device tells us which endianness it's in. That way we can compile the devices only once and get rid of all the ugly swap will be done by the underlying layer. For the same of readability, this patch only introduces the helper framework but doesn't allow the registering code to set its endianness yet. Signed-off-by: Alexander Graf <agraf@suse.de> Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
-rw-r--r--cpu-common.h8
-rw-r--r--exec.c123
2 files changed, 128 insertions, 3 deletions
diff --git a/cpu-common.h b/cpu-common.h
index bb6b137..6d4a898 100644
--- a/cpu-common.h
+++ b/cpu-common.h
@@ -20,6 +20,12 @@
#if !defined(CONFIG_USER_ONLY)
+enum device_endian {
+ DEVICE_NATIVE_ENDIAN,
+ DEVICE_BIG_ENDIAN,
+ DEVICE_LITTLE_ENDIAN,
+};
+
/* address in the RAM (different from a physical address) */
typedef unsigned long ram_addr_t;
@@ -55,7 +61,7 @@ ram_addr_t qemu_ram_addr_from_host_nofail(void *ptr);
int cpu_register_io_memory(CPUReadMemoryFunc * const *mem_read,
CPUWriteMemoryFunc * const *mem_write,
- void *opaque);
+ void *opaque, enum device_endian endian);
void cpu_unregister_io_memory(int table_address);
void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf,
diff --git a/exec.c b/exec.c
index 42a35e0..af1e89d 100644
--- a/exec.c
+++ b/exec.c
@@ -3355,6 +3355,106 @@ static int get_free_io_mem_idx(void)
return -1;
}
+/*
+ * Usually, devices operate in little endian mode. There are devices out
+ * there that operate in big endian too. Each device gets byte swapped
+ * mmio if plugged onto a CPU that does the other endianness.
+ *
+ * CPU Device swap?
+ *
+ * little little no
+ * little big yes
+ * big little yes
+ * big big no
+ */
+
+typedef struct SwapEndianContainer {
+ CPUReadMemoryFunc *read[3];
+ CPUWriteMemoryFunc *write[3];
+ void *opaque;
+} SwapEndianContainer;
+
+static uint32_t swapendian_mem_readb (void *opaque, target_phys_addr_t addr)
+{
+ uint32_t val;
+ SwapEndianContainer *c = opaque;
+ val = c->read[0](c->opaque, addr);
+ return val;
+}
+
+static uint32_t swapendian_mem_readw(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t val;
+ SwapEndianContainer *c = opaque;
+ val = bswap16(c->read[1](c->opaque, addr));
+ return val;
+}
+
+static uint32_t swapendian_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t val;
+ SwapEndianContainer *c = opaque;
+ val = bswap32(c->read[2](c->opaque, addr));
+ return val;
+}
+
+static CPUReadMemoryFunc * const swapendian_readfn[3]={
+ swapendian_mem_readb,
+ swapendian_mem_readw,
+ swapendian_mem_readl
+};
+
+static void swapendian_mem_writeb(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ SwapEndianContainer *c = opaque;
+ c->write[0](c->opaque, addr, val);
+}
+
+static void swapendian_mem_writew(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ SwapEndianContainer *c = opaque;
+ c->write[1](c->opaque, addr, bswap16(val));
+}
+
+static void swapendian_mem_writel(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ SwapEndianContainer *c = opaque;
+ c->write[2](c->opaque, addr, bswap32(val));
+}
+
+static CPUWriteMemoryFunc * const swapendian_writefn[3]={
+ swapendian_mem_writeb,
+ swapendian_mem_writew,
+ swapendian_mem_writel
+};
+
+static void swapendian_init(int io_index)
+{
+ SwapEndianContainer *c = qemu_malloc(sizeof(SwapEndianContainer));
+ int i;
+
+ /* Swap mmio for big endian targets */
+ c->opaque = io_mem_opaque[io_index];
+ for (i = 0; i < 3; i++) {
+ c->read[i] = io_mem_read[io_index][i];
+ c->write[i] = io_mem_write[io_index][i];
+
+ io_mem_read[io_index][i] = swapendian_readfn[i];
+ io_mem_write[io_index][i] = swapendian_writefn[i];
+ }
+ io_mem_opaque[io_index] = c;
+}
+
+static void swapendian_del(int io_index)
+{
+ if (io_mem_read[io_index][0] == swapendian_readfn[0]) {
+ qemu_free(io_mem_opaque[io_index]);
+ }
+}
+
/* mem_read and mem_write are arrays of functions containing the
function to access byte (index 0), word (index 1) and dword (index
2). Functions can be omitted with a NULL function pointer.
@@ -3365,9 +3465,10 @@ static int get_free_io_mem_idx(void)
static int cpu_register_io_memory_fixed(int io_index,
CPUReadMemoryFunc * const *mem_read,
CPUWriteMemoryFunc * const *mem_write,
- void *opaque)
+ void *opaque, enum device_endian endian)
{
int i;
+ int endian = DEVICE_NATIVE_ENDIAN;
if (io_index <= 0) {
io_index = get_free_io_mem_idx();
@@ -3389,12 +3490,28 @@ static int cpu_register_io_memory_fixed(int io_index,
}
io_mem_opaque[io_index] = opaque;
+ switch (endian) {
+ case DEVICE_BIG_ENDIAN:
+#ifndef TARGET_WORDS_BIGENDIAN
+ swapendian_init(io_index);
+#endif
+ break;
+ case DEVICE_LITTLE_ENDIAN:
+#ifdef TARGET_WORDS_BIGENDIAN
+ swapendian_init(io_index);
+#endif
+ break;
+ case DEVICE_NATIVE_ENDIAN:
+ default:
+ break;
+ }
+
return (io_index << IO_MEM_SHIFT);
}
int cpu_register_io_memory(CPUReadMemoryFunc * const *mem_read,
CPUWriteMemoryFunc * const *mem_write,
- void *opaque)
+ void *opaque, enum device_endian endian)
{
return cpu_register_io_memory_fixed(0, mem_read, mem_write, opaque);
}
@@ -3404,6 +3521,8 @@ void cpu_unregister_io_memory(int io_table_address)
int i;
int io_index = io_table_address >> IO_MEM_SHIFT;
+ swapendian_del(io_index);
+
for (i=0;i < 3; i++) {
io_mem_read[io_index][i] = unassigned_mem_read[i];
io_mem_write[io_index][i] = unassigned_mem_write[i];
OpenPOWER on IntegriCloud