From patchwork Thu Nov 25 07:35:41 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexander Graf X-Patchwork-Id: 73033 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id C447BB70A7 for ; Thu, 25 Nov 2010 19:58:24 +1100 (EST) Received: from localhost ([127.0.0.1]:52449 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1PLX8m-0008D1-4m for incoming@patchwork.ozlabs.org; Thu, 25 Nov 2010 03:25:16 -0500 Received: from [140.186.70.92] (port=44205 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1PLWN8-0007Ju-Qx for qemu-devel@nongnu.org; Thu, 25 Nov 2010 02:36:07 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1PLWN3-0001zO-Sz for qemu-devel@nongnu.org; Thu, 25 Nov 2010 02:36:02 -0500 Received: from cantor.suse.de ([195.135.220.2]:39377 helo=mx1.suse.de) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1PLWN3-0001ya-Ja for qemu-devel@nongnu.org; Thu, 25 Nov 2010 02:35:57 -0500 Received: from relay1.suse.de (charybdis-ext.suse.de [195.135.221.2]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.suse.de (Postfix) with ESMTP id 5D37F93EE3; Thu, 25 Nov 2010 08:35:56 +0100 (CET) From: Alexander Graf To: QEMU-devel Developers Date: Thu, 25 Nov 2010 08:35:41 +0100 Message-Id: <1290670555-12575-2-git-send-email-agraf@suse.de> X-Mailer: git-send-email 1.6.0.2 In-Reply-To: <1290670555-12575-1-git-send-email-agraf@suse.de> References: <1290670555-12575-1-git-send-email-agraf@suse.de> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.4-2.6 Cc: Blue Swirl , Paul Brook Subject: [Qemu-devel] [PATCH 01/15] exec: introduce endianness swapped mmio X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org 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 --- cpu-common.h | 4 ++ exec.c | 122 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+), 0 deletions(-) diff --git a/cpu-common.h b/cpu-common.h index a543b5d..839b236 100644 --- a/cpu-common.h +++ b/cpu-common.h @@ -20,6 +20,10 @@ #if !defined(CONFIG_USER_ONLY) +#define DEVICE_NATIVE_ENDIAN 0 +#define DEVICE_BIG_ENDIAN 1 +#define DEVICE_LITTLE_ENDIAN 2 + /* address in the RAM (different from a physical address) */ typedef unsigned long ram_addr_t; diff --git a/exec.c b/exec.c index db9ff55..f54a360 100644 --- a/exec.c +++ b/exec.c @@ -3336,6 +3336,109 @@ 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 + */ +#ifdef TARGET_WORDS_BIGENDIAN + +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]); + } +} + +#endif + /* 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. @@ -3349,6 +3452,7 @@ static int cpu_register_io_memory_fixed(int io_index, void *opaque) { int i; + int endian = DEVICE_NATIVE_ENDIAN; if (io_index <= 0) { io_index = get_free_io_mem_idx(); @@ -3370,6 +3474,22 @@ 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); } @@ -3385,6 +3505,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];