[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 01/15] exec: introduce endianness swapped mmio
From: |
Alexander Graf |
Subject: |
[Qemu-devel] [PATCH 01/15] exec: introduce endianness swapped mmio |
Date: |
Wed, 8 Dec 2010 12:05:36 +0100 |
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 <address@hidden>
---
v0 -> v1:
- don't restrict to big endian targets
- move constants to enum
---
cpu-common.h | 8 +++-
exec.c | 123 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
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];
--
1.6.0.2
- [Qemu-devel] [PATCH 00/15] MMIO endianness cleanup v2, Alexander Graf, 2010/12/08
- [Qemu-devel] [PATCH 11/15] openpic: Replace explicit byte swap with endian hints, Alexander Graf, 2010/12/08
- [Qemu-devel] [PATCH 04/15] dbdma: Make little endian, Alexander Graf, 2010/12/08
- [Qemu-devel] [PATCH 03/15] Make simple io mem handler endian aware, Alexander Graf, 2010/12/08
- [Qemu-devel] [PATCH 06/15] uninorth: Get rid of bswap, Alexander Graf, 2010/12/08
- [Qemu-devel] [PATCH 01/15] exec: introduce endianness swapped mmio,
Alexander Graf <=
- [Qemu-devel] [PATCH 09/15] versatile_pci: Declare as little endian, Alexander Graf, 2010/12/08
- [Qemu-devel] [PATCH 08/15] prep: Declare as little endian, Alexander Graf, 2010/12/08
- [Qemu-devel] [PATCH 07/15] e1000: Make little endian, Alexander Graf, 2010/12/08
- [Qemu-devel] [PATCH 05/15] pci-host: Delegate bswap to mmio layer, Alexander Graf, 2010/12/08
- [Qemu-devel] [PATCH 15/15] usb_ohci: Always use little endian, Alexander Graf, 2010/12/08
- [Qemu-devel] [PATCH 13/15] heathrow_pic: Declare as little endian, Alexander Graf, 2010/12/08
- [Qemu-devel] [PATCH 14/15] isa_mmio: Always use little endian, Alexander Graf, 2010/12/08
- [Qemu-devel] [PATCH 10/15] ppc4xx_pci: Declare as little endian, Alexander Graf, 2010/12/08
- [Qemu-devel] [PATCH 12/15] rtl8139: Declare as little endian, Alexander Graf, 2010/12/08
- [Qemu-devel] [PATCH 02/15] Add endianness as io mem parameter, Alexander Graf, 2010/12/08