qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH,RFC]: Generic memory callback regions
@ 2004-06-01  2:53 Gianni Tedesco
  2004-06-01 11:13 ` Joe Batt
  0 siblings, 1 reply; 3+ messages in thread
From: Gianni Tedesco @ 2004-06-01  2:53 UTC (permalink / raw)
  To: qemu-devel

[-- Attachment #1: Type: text/plain, Size: 913 bytes --]

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

[-- Attachment #2: qemu-mem-callbacks-1.diff --]
[-- Type: text/x-patch, Size: 6319 bytes --]

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 */

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [Qemu-devel] [PATCH,RFC]: Generic memory callback regions
  2004-06-01  2:53 [Qemu-devel] [PATCH,RFC]: Generic memory callback regions Gianni Tedesco
@ 2004-06-01 11:13 ` Joe Batt
  2004-06-01 11:26   ` Gianni Tedesco
  0 siblings, 1 reply; 3+ messages in thread
From: Joe Batt @ 2004-06-01 11:13 UTC (permalink / raw)
  To: qemu-devel

I haven't read your patch, but I recently wrote a SH2 emulator.  We
found that a lot of time was spent finding the right callbacks, so for
the device memory, we used a sorted array.  After each access, we
bubbled that memory region closed to the beginning of the array.  This
turned out to be faster than the tree that we were using.  The main
differences are we have 100+ regions and implemented it in Java.

Joe

On Mon, 2004-05-31 at 21:53, Gianni Tedesco wrote:
> 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?

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [Qemu-devel] [PATCH,RFC]: Generic memory callback regions
  2004-06-01 11:13 ` Joe Batt
@ 2004-06-01 11:26   ` Gianni Tedesco
  0 siblings, 0 replies; 3+ messages in thread
From: Gianni Tedesco @ 2004-06-01 11:26 UTC (permalink / raw)
  To: qemu-devel

[-- Attachment #1: Type: text/plain, Size: 1135 bytes --]

On Tue, 2004-06-01 at 06:13 -0500, Joe Batt wrote:
> I haven't read your patch, but I recently wrote a SH2 emulator.  We
> found that a lot of time was spent finding the right callbacks, so for
> the device memory, we used a sorted array.  After each access, we
> bubbled that memory region closed to the beginning of the array.  This
> turned out to be faster than the tree that we were using.  The main
> differences are we have 100+ regions and implemented it in Java.

Good call. Move-to-front heuristic seems smart because I anticipate (and
you found out for real) that memory accesses will have temporal
locality. Moving to head is theoretically the same or a little better
than 'bubbling up' actually, Knuth covers it in Vol 3 as i recall.

Well, it becomes irrelevent for this case now, as I am implementing this
privately in pciproxy.c, rather than publically in exec.c, and in my
case the list is always 1 item long :)

-- 
// 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

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2004-06-01 11:27 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2004-06-01  2:53 [Qemu-devel] [PATCH,RFC]: Generic memory callback regions Gianni Tedesco
2004-06-01 11:13 ` Joe Batt
2004-06-01 11:26   ` Gianni Tedesco

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).