From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.33) id 1BUzPp-0000Te-4W for qemu-devel@nongnu.org; Mon, 31 May 2004 22:54:13 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.33) id 1BUzPo-0000TS-IF for qemu-devel@nongnu.org; Mon, 31 May 2004 22:54:12 -0400 Received: from [199.232.76.173] (helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.33) id 1BUzPo-0000TP-FC for qemu-devel@nongnu.org; Mon, 31 May 2004 22:54:12 -0400 Received: from [213.146.130.142] (helo=trantor.org.uk) by monty-python.gnu.org with esmtp (TLSv1:DES-CBC3-SHA:168) (Exim 4.34) id 1BUzP5-0003KS-MH for qemu-devel@nongnu.org; Mon, 31 May 2004 22:53:28 -0400 From: Gianni Tedesco Content-Type: multipart/mixed; boundary="=-neC/wrS+d08gEEk/pnxV" Date: Tue, 01 Jun 2004 03:53:02 +0100 Message-Id: <1086058382.21903.53.camel@sherbert> Mime-Version: 1.0 Subject: [Qemu-devel] [PATCH,RFC]: Generic memory callback regions Reply-To: qemu-devel@nongnu.org List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org --=-neC/wrS+d08gEEk/pnxV Content-Type: text/plain Content-Transfer-Encoding: 7bit Hi, This patch adds an API for CONFIG_SOFTMMU mode that hardware drivers can use to add memory regions backed by callback functions. It simply adds a layer for storing opaque data above the basic cpu_register_io_memory / cpu_register_physical memory functions. I used a linked list to store the data structures, i think O(ln/2) avg. lookup time will be fine as I don't envisage many of these regions existing. I've tested it as far as I currently can for the pciproxy code and it seems to do the correct thing. I could work around this manually within the pciproxy code itself, but I figured a generic interface would be something useful for other drivers (such as any PCI card with MMIO resources). Ideas / thoughts / bugs? -- // Gianni Tedesco (gianni at scaramanga dot co dot uk) lynx --source www.scaramanga.co.uk/scaramanga.asc | gpg --import 8646BE7D: 6D9F 2287 870E A2C9 8F60 3A3C 91B5 7669 8646 BE7D --=-neC/wrS+d08gEEk/pnxV Content-Disposition: attachment; filename=qemu-mem-callbacks-1.diff Content-Type: text/x-patch; name=qemu-mem-callbacks-1.diff; charset=ANSI_X3.4-1968 Content-Transfer-Encoding: 7bit diff -urN qemu.orig/cpu-all.h qemu.callback_mem/cpu-all.h --- qemu.orig/cpu-all.h 2004-05-21 13:59:13.000000000 +0100 +++ qemu.callback_mem/cpu-all.h 2004-06-01 03:11:05.000000000 +0100 @@ -680,10 +680,28 @@ #define IO_MEM_UNASSIGNED (2 << IO_MEM_SHIFT) #define IO_MEM_CODE (3 << IO_MEM_SHIFT) /* used internally, never use directly */ #define IO_MEM_NOTDIRTY (4 << IO_MEM_SHIFT) /* used internally, never use directly */ +#define IO_MEM_CALLBACK (5 << IO_MEM_SHIFT) /* Callback functions */ typedef void CPUWriteMemoryFunc(target_phys_addr_t addr, uint32_t value); typedef uint32_t CPUReadMemoryFunc(target_phys_addr_t addr); +typedef void (*CPUWriteMemoryCallback)(void *opaque, + target_phys_addr_t addr, + int len, + uint32_t val); +typedef uint32_t (*CPUReadMemoryCallback)(void *opaque, + target_phys_addr_t addr, + int len); + +int cpu_register_callback_memory(target_phys_addr_t start_addr, + unsigned long size, + CPUReadMemoryCallback mem_read, + CPUWriteMemoryCallback mem_write, + void *opaque); +int cpu_unregister_callback_memory(target_phys_addr_t start_addr, + unsigned long size); + + void cpu_register_physical_memory(target_phys_addr_t start_addr, unsigned long size, unsigned long phys_offset); diff -urN qemu.orig/exec.c qemu.callback_mem/exec.c --- qemu.orig/exec.c 2004-05-25 23:48:00.000000000 +0100 +++ qemu.callback_mem/exec.c 2004-06-01 03:42:42.000000000 +0100 @@ -731,6 +731,149 @@ } } +#if defined(CONFIG_SOFTMMU) +struct cpu_callback { + struct cpu_callback *prev, *next; + target_phys_addr_t begin, end; + CPUReadMemoryCallback read; + CPUWriteMemoryCallback write; + void *opaque; +}; + +static struct cpu_callback cpu_cbfn = { + .prev = &cpu_cbfn, + .next = &cpu_cbfn, +}; + +static struct cpu_callback *cpu_cbfn_lookup(target_phys_addr_t begin, + target_phys_addr_t end) +{ + struct cpu_callback *c; + + for(c=cpu_cbfn.next; c != &cpu_cbfn; c=c->next) { + if ( (begin < c->end && end > c->end) || + (end > c->begin && begin < c->begin) ) { + fprintf(stderr, "cpu_cbfn_lookup: %x/%u straddles region: " + "%x-%x\n", begin, end-begin, c->begin, c->end); + } + if ( begin >= c->begin && end <= c->end ) + return c; + } + + fprintf(stderr, "cpu_cbfn_lookup: %x/%u not found\n", begin, end-begin); + return NULL; +} + +static void cpu_cbfn_write(target_phys_addr_t addr, int len, uint32_t value) +{ + struct cpu_callback *c; + c = cpu_cbfn_lookup(addr, addr + len); + if ( c == NULL ) + return; + if ( c->write ) + (*c->write)(c->opaque, addr, len, value); +} + +static uint32_t cpu_cbfn_read(target_phys_addr_t addr, int len) +{ + struct cpu_callback *c; + c = cpu_cbfn_lookup(addr, addr + len); + if ( c == NULL ) + return 0xffffffff; + if ( c->read ) + return (*c->read)(c->opaque, addr, len); + return 0; /* NULL read handler means return all zeros */ +} + +static uint32_t cpu_cbfn_readb(target_phys_addr_t addr) +{ + return cpu_cbfn_read(addr, 1); +} +static uint32_t cpu_cbfn_readw(target_phys_addr_t addr) +{ + return cpu_cbfn_read(addr, 2); +} +static uint32_t cpu_cbfn_readl(target_phys_addr_t addr) +{ + return cpu_cbfn_read(addr, 4); +} +static void cpu_cbfn_writeb(target_phys_addr_t addr, uint32_t value) +{ + return cpu_cbfn_write(addr, 1, value); +} +static void cpu_cbfn_writew(target_phys_addr_t addr, uint32_t value) +{ + return cpu_cbfn_write(addr, 2, value); +} +static void cpu_cbfn_writel(target_phys_addr_t addr, uint32_t value) +{ + return cpu_cbfn_write(addr, 4, value); +} + +static CPUReadMemoryFunc *cpu_callback_read[3]={ + cpu_cbfn_readb, + cpu_cbfn_readw, + cpu_cbfn_readl, +}; +static CPUWriteMemoryFunc *cpu_callback_write[3]={ + cpu_cbfn_writeb, + cpu_cbfn_writew, + cpu_cbfn_writel, +}; + +int cpu_register_callback_memory(target_phys_addr_t start_addr, + unsigned long size, + CPUReadMemoryCallback mem_read, + CPUWriteMemoryCallback mem_write, + void *opaque) +{ + struct cpu_callback *c; + target_phys_addr_t end_addr = start_addr + size; + + /* Check for overlaps (not permitted) */ + for(c=cpu_cbfn.next; c != &cpu_cbfn; c=c->next) { + if ( (c->begin >= start_addr && c->begin <= end_addr) || + (start_addr >= c->begin && start_addr <= c->end) ) { + fprintf(stderr, "mem_callback: %x:%x overlaps %x:%x\n", + start_addr, end_addr, c->begin, c->end); + return 1; + } + } + + c = malloc(sizeof(*c)); + if ( c == NULL ) + return 0; + + c->next = cpu_cbfn.next; + c->next->prev = c; + c->prev = &cpu_cbfn; + c->prev->next = c; + c->begin = start_addr; + c->end = end_addr; + c->read = mem_read; + c->write = mem_write; + c->opaque = opaque; + + cpu_register_physical_memory(start_addr, size, IO_MEM_CALLBACK); + + return 1; +} + +int cpu_unregister_callback_memory(target_phys_addr_t start_addr, + unsigned long size) +{ + struct cpu_callback *c; + c = cpu_cbfn_lookup(start_addr, start_addr + size); + if ( c == NULL ) + return 0; + + c->prev->next = c->next; + c->next->prev = c->prev; + free(c); + return 1; +} +#endif + #if !defined(CONFIG_SOFTMMU) static void tb_invalidate_phys_page(target_ulong addr, unsigned long pc, void *puc) @@ -1901,6 +2044,9 @@ cpu_register_io_memory(IO_MEM_UNASSIGNED >> IO_MEM_SHIFT, unassigned_mem_read, unassigned_mem_write); cpu_register_io_memory(IO_MEM_CODE >> IO_MEM_SHIFT, code_mem_read, code_mem_write); cpu_register_io_memory(IO_MEM_NOTDIRTY >> IO_MEM_SHIFT, code_mem_read, notdirty_mem_write); +#if defined(CONFIG_SOFTMMU) + cpu_register_io_memory(IO_MEM_CALLBACK >> IO_MEM_SHIFT, cpu_callback_read, cpu_callback_write); +#endif io_mem_nb = 5; /* alloc dirty bits array */ --=-neC/wrS+d08gEEk/pnxV--