WARNING - OLD ARCHIVES

This is an archived copy of the Xen.org mailing list, which we have preserved to ensure that existing links to archives are not broken. The live archive, which contains the latest emails, can be found at http://lists.xen.org/
   
 
 
Xen 
 
Home Products Support Community News
 
   
 

xen-devel

[Xen-devel] [PATCH] Std VGA Performance

To: xen-devel <xen-devel@xxxxxxxxxxxxxxxxxxx>
Subject: [Xen-devel] [PATCH] Std VGA Performance
From: Ben Guthro <bguthro@xxxxxxxxxxxxxxx>
Date: Wed, 24 Oct 2007 17:36:20 -0400
Cc: Robert Phillips <rphillips@xxxxxxxxxxxxxxx>
Delivery-date: Wed, 24 Oct 2007 14:37:09 -0700
Envelope-to: www-data@xxxxxxxxxxxxxxxxxx
List-help: <mailto:xen-devel-request@lists.xensource.com?subject=help>
List-id: Xen developer discussion <xen-devel.lists.xensource.com>
List-post: <mailto:xen-devel@lists.xensource.com>
List-subscribe: <http://lists.xensource.com/cgi-bin/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=subscribe>
List-unsubscribe: <http://lists.xensource.com/cgi-bin/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=unsubscribe>
Sender: xen-devel-bounces@xxxxxxxxxxxxxxxxxxx
User-agent: Thunderbird 2.0.0.5 (X11/20070719)
This patch improves the performance of Standard VGA,
the mode used during Windows boot and by the Linux
splash screen.

It does so by buffering all the stdvga programmed output ops
and memory mapped ops (both reads and writes) that are sent to QEMU.

We maintain locally essential VGA state so we can respond
immediately to input and read ops without waiting for
QEMU.  We snoop output and write ops to keep our state
up-to-date.

PIO input ops are satisfied from cached state without
bothering QEMU.

PIO output and mmio ops are passed through to QEMU, including
mmio read ops.  This is necessary because mmio reads
can have side effects.

I have changed the format of the buffered_iopage.
It used to contain 80 elements of type ioreq_t (48 bytes each).
Now it contains 672 elements of type buf_ioreq_t (6 bytes each).
Being able to pipeline 8 times as many ops improves
VGA performance by a factor of 8.

I changed hvm_buffered_io_intercept to use the same
registration and callback mechanism as hvm_portio_intercept
rather than the hacky hardcoding it used before.

In platform.c, I fixed send_timeoffset_req() to sets its
ioreq size to 8 (rather than 4), and its count to 1 (which
was missing).

Signed-off-by: Ben Guthro <bguthro@xxxxxxxxxxxxxx>
Signed-off-by: Robert Phillips <rphillips@xxxxxxxxxxxxxxx>
diff -r 118a21c66fd5 tools/ioemu/target-i386-dm/helper2.c
--- a/tools/ioemu/target-i386-dm/helper2.c      Mon Oct 22 21:06:11 2007 +0100
+++ b/tools/ioemu/target-i386-dm/helper2.c      Wed Oct 24 17:31:57 2007 -0400
@@ -478,6 +478,7 @@ void cpu_ioreq_timeoffset(CPUState *env,
 
     time_offset += (ulong)req->data;
 
+    fprintf(logfile, "Time offset set %ld, added offset %ld\n", time_offset, 
req->data);
     sprintf(b, "%ld", time_offset);
     xenstore_vm_write(domid, "rtc/timeoffset", b);
 }
@@ -538,20 +539,39 @@ void __handle_ioreq(CPUState *env, ioreq
 
 void __handle_buffered_iopage(CPUState *env)
 {
-    ioreq_t *req = NULL;
+    buf_ioreq_t *buf_req = NULL;
+    ioreq_t req;
+    int qw = 0;
 
     if (!buffered_io_page)
         return;
 
     while (buffered_io_page->read_pointer !=
            buffered_io_page->write_pointer) {
-        req = &buffered_io_page->ioreq[buffered_io_page->read_pointer %
+        memset(&req, 0, sizeof(req));
+        buf_req = &buffered_io_page->buf_ioreq[buffered_io_page->read_pointer %
                                       IOREQ_BUFFER_SLOT_NUM];
-
-        __handle_ioreq(env, req);
+        req.size = 1UL << buf_req->size;
+        req.count = 1;
+        req.data = buf_req->data;
+        req.state = STATE_IOREQ_READY;
+        req.dir  = buf_req->dir;
+        req.type = buf_req->type;
+        qw = req.size == 8;
+        if (qw) {
+            req.data |= ((uint64_t)buf_req->addr) << 16;
+            buf_req = 
&buffered_io_page->buf_ioreq[(buffered_io_page->read_pointer+1) %
+                                               IOREQ_BUFFER_SLOT_NUM];
+            req.data |= ((uint64_t)buf_req->data) << 32;
+            req.data |= ((uint64_t)buf_req->addr) << 48;
+        }
+        else
+            req.addr = buf_req->addr;
+
+        __handle_ioreq(env, &req);
 
         mb();
-        buffered_io_page->read_pointer++;
+        buffered_io_page->read_pointer += qw ? 2 : 1;
     }
 }
 
diff -r 118a21c66fd5 tools/ioemu/xenstore.c
--- a/tools/ioemu/xenstore.c    Mon Oct 22 21:06:11 2007 +0100
+++ b/tools/ioemu/xenstore.c    Wed Oct 24 17:31:57 2007 -0400
@@ -724,7 +724,7 @@ int xenstore_vm_write(int domid, char *k
 
     pasprintf(&buf, "%s/%s", path, key);
     rc = xs_write(xsh, XBT_NULL, buf, value, strlen(value));
-    if (rc) {
+    if (rc == 0) {
         fprintf(logfile, "xs_write(%s, %s): write error\n", buf, key);
         goto out;
     }
diff -r 118a21c66fd5 xen/arch/x86/hvm/Makefile
--- a/xen/arch/x86/hvm/Makefile Mon Oct 22 21:06:11 2007 +0100
+++ b/xen/arch/x86/hvm/Makefile Wed Oct 24 17:31:57 2007 -0400
@@ -17,3 +17,4 @@ obj-y += vlapic.o
 obj-y += vlapic.o
 obj-y += vpic.o
 obj-y += save.o
+obj-y += stdvga.o
diff -r 118a21c66fd5 xen/arch/x86/hvm/hvm.c
--- a/xen/arch/x86/hvm/hvm.c    Mon Oct 22 21:06:11 2007 +0100
+++ b/xen/arch/x86/hvm/hvm.c    Wed Oct 24 17:31:57 2007 -0400
@@ -238,6 +238,8 @@ int hvm_domain_initialise(struct domain 
     if ( rc != 0 )
         return rc;
 
+    stdvga_init(d);
+
     hvm_init_ioreq_page(d, &d->arch.hvm_domain.ioreq);
     hvm_init_ioreq_page(d, &d->arch.hvm_domain.buf_ioreq);
 
@@ -253,6 +255,7 @@ void hvm_domain_relinquish_resources(str
     rtc_deinit(d);
     pmtimer_deinit(d);
     hpet_deinit(d);
+    stdvga_deinit(d);
 }
 
 void hvm_domain_destroy(struct domain *d)
diff -r 118a21c66fd5 xen/arch/x86/hvm/intercept.c
--- a/xen/arch/x86/hvm/intercept.c      Mon Oct 22 21:06:11 2007 +0100
+++ b/xen/arch/x86/hvm/intercept.c      Wed Oct 24 17:31:57 2007 -0400
@@ -45,20 +45,6 @@ static struct hvm_mmio_handler *hvm_mmio
     &vioapic_mmio_handler
 };
 
-struct hvm_buffered_io_range {
-    unsigned long start_addr;
-    unsigned long length;
-};
-
-#define HVM_BUFFERED_IO_RANGE_NR 1
-
-static struct hvm_buffered_io_range buffered_stdvga_range = {0xA0000, 0x20000};
-static struct hvm_buffered_io_range
-*hvm_buffered_io_ranges[HVM_BUFFERED_IO_RANGE_NR] =
-{
-    &buffered_stdvga_range
-};
-
 static inline void hvm_mmio_access(struct vcpu *v,
                                    ioreq_t *p,
                                    hvm_mmio_read_t read_handler,
@@ -170,47 +156,68 @@ int hvm_buffered_io_send(ioreq_t *p)
     struct vcpu *v = current;
     struct hvm_ioreq_page *iorp = &v->domain->arch.hvm_domain.buf_ioreq;
     buffered_iopage_t *pg = iorp->va;
-
+    buf_ioreq_t bp;
+    /* Timeoffset sends 64b data, but no address.  Use two consecutive slots. 
*/
+    int qw = 0;
+
+    /* Ensure buffered_iopage fits in a page */
+    BUILD_BUG_ON(sizeof(buffered_iopage_t) > PAGE_SIZE);
+
+    /* Return 0 for the cases we can't deal with. */
+    if (p->addr > 0xffffful || p->data_is_ptr || p->df || p->count != 1)
+        return 0;
+
+    bp.type = p->type;
+    bp.dir  = p->dir;
+    switch (p->size) {
+    case 1:
+        bp.size = 0;
+        break;
+    case 2:
+        bp.size = 1;
+        break;
+    case 4:
+        bp.size = 2;
+        break;
+    case 8:
+        bp.size = 3;
+        qw = 1;
+        gdprintk(XENLOG_INFO, "quadword ioreq type:%d data:%ld\n", p->type, 
p->data);
+        break;
+    default:
+        gdprintk(XENLOG_WARNING, "unexpected ioreq size:%ld\n", p->size);
+        return 0;
+    }
+    
+    bp.data = p->data;
+    bp.addr = qw ? ((p->data >> 16) & 0xfffful) : (p->addr & 0xffffful);
+    
     spin_lock(&iorp->lock);
 
-    if ( (pg->write_pointer - pg->read_pointer) == IOREQ_BUFFER_SLOT_NUM )
+    if ( (pg->write_pointer - pg->read_pointer) >= IOREQ_BUFFER_SLOT_NUM - (qw 
? 1 : 0))
     {
         /* The queue is full: send the iopacket through the normal path. */
         spin_unlock(&iorp->lock);
         return 0;
     }
-
-    memcpy(&pg->ioreq[pg->write_pointer % IOREQ_BUFFER_SLOT_NUM],
-           p, sizeof(ioreq_t));
+    
+    memcpy(&pg->buf_ioreq[pg->write_pointer % IOREQ_BUFFER_SLOT_NUM],
+           &bp, sizeof(bp));
+    
+    if (qw) {
+        bp.data = p->data >> 32;
+        bp.addr = (p->data >> 48) & 0xfffful;
+        memcpy(&pg->buf_ioreq[(pg->write_pointer+1) % IOREQ_BUFFER_SLOT_NUM],
+               &bp, sizeof(bp));
+    }
 
     /* Make the ioreq_t visible /before/ write_pointer. */
     wmb();
-    pg->write_pointer++;
-
+    pg->write_pointer += qw ? 2 : 1;
+    
     spin_unlock(&iorp->lock);
-
+    
     return 1;
-}
-
-int hvm_buffered_io_intercept(ioreq_t *p)
-{
-    int i;
-
-    /* ignore READ ioreq_t! */
-    if ( p->dir == IOREQ_READ )
-        return 0;
-
-    for ( i = 0; i < HVM_BUFFERED_IO_RANGE_NR; i++ ) {
-        if ( p->addr >= hvm_buffered_io_ranges[i]->start_addr &&
-             p->addr + p->size - 1 < hvm_buffered_io_ranges[i]->start_addr +
-                                     hvm_buffered_io_ranges[i]->length )
-            break;
-    }
-
-    if ( i == HVM_BUFFERED_IO_RANGE_NR )
-        return 0;
-
-    return hvm_buffered_io_send(p);
 }
 
 int hvm_mmio_intercept(ioreq_t *p)
@@ -253,7 +260,7 @@ int hvm_io_intercept(ioreq_t *p, int typ
         addr = handler->hdl_list[i].addr;
         size = handler->hdl_list[i].size;
         if (p->addr >= addr &&
-            p->addr <  addr + size)
+            p->addr + p->size <=  addr + size)
             return handler->hdl_list[i].action(p);
     }
     return 0;
diff -r 118a21c66fd5 xen/arch/x86/hvm/platform.c
--- a/xen/arch/x86/hvm/platform.c       Mon Oct 22 21:06:11 2007 +0100
+++ b/xen/arch/x86/hvm/platform.c       Wed Oct 24 17:31:57 2007 -0400
@@ -944,7 +944,8 @@ void send_timeoffset_req(unsigned long t
     memset(p, 0, sizeof(*p));
 
     p->type = IOREQ_TYPE_TIMEOFFSET;
-    p->size = 4;
+    p->size = 8;
+    p->count = 1;
     p->dir = IOREQ_WRITE;
     p->data = timeoff;
 
diff -r 118a21c66fd5 xen/arch/x86/hvm/stdvga.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/xen/arch/x86/hvm/stdvga.c Wed Oct 24 17:34:54 2007 -0400
@@ -0,0 +1,712 @@
+/*
+ *  Copyright (c) 2003-2007, Virtual Iron Software, Inc.
+ *
+ *  Portions have been modified by Virtual Iron Software, Inc.
+ *  (c) 2007. This file and the modifications can be redistributed and/or
+ *  modified under the terms and conditions of the GNU General Public
+ *  License, version 2.1 and not any later version of the GPL, as published
+ *  by the Free Software Foundation. 
+ *
+ *
+ *
+ *  This improves the performance of Standard VGA,
+ *  the mode used during Windows boot and by the Linux
+ *  splash screen.
+ *
+ *  It does so by buffering all the stdvga programmed output ops
+ *  and memory mapped ops (both reads and writes) that are sent to QEMU.
+ *
+ *  We maintain locally essential VGA state so we can respond
+ *  immediately to input and read ops without waiting for
+ *  QEMU.  We snoop output and write ops to keep our state
+ *  up-to-date.
+ *
+ *  PIO input ops are satisfied from cached state without
+ *  bothering QEMU.
+ *
+    PIO output and mmio ops are passed through to QEMU, including
+ *  mmio read ops.  This is necessary because mmio reads
+ *  can have side effects.
+ */
+
+#include <xen/config.h>
+#include <xen/types.h>
+#include <xen/sched.h>
+#include <asm/hvm/support.h>
+
+#define vram_b(_s, _a) (((uint8_t*) 
(_s)->vram_ptr[((_a)>>12)&0x3f])[(_a)&0xfff])
+#define vram_w(_s, _a) 
(((uint16_t*)(_s)->vram_ptr[((_a)>>11)&0x3f])[(_a)&0x7ff])
+#define vram_l(_s, _a) 
(((uint32_t*)(_s)->vram_ptr[((_a)>>10)&0x3f])[(_a)&0x3ff])
+
+#ifdef STDVGA_STATS
+#define UPDATE_STATS(x) x
+#else
+#define UPDATE_STATS(x)
+#endif
+
+#define PAT(x) (x)
+static const uint32_t mask16[16] = {
+    PAT(0x00000000),
+    PAT(0x000000ff),
+    PAT(0x0000ff00),
+    PAT(0x0000ffff),
+    PAT(0x00ff0000),
+    PAT(0x00ff00ff),
+    PAT(0x00ffff00),
+    PAT(0x00ffffff),
+    PAT(0xff000000),
+    PAT(0xff0000ff),
+    PAT(0xff00ff00),
+    PAT(0xff00ffff),
+    PAT(0xffff0000),
+    PAT(0xffff00ff),
+    PAT(0xffffff00),
+    PAT(0xffffffff),
+};
+
+/* force some bits to zero */
+const uint8_t sr_mask[8] = {
+    (uint8_t)~0xfc,
+    (uint8_t)~0xc2,
+    (uint8_t)~0xf0,
+    (uint8_t)~0xc0,
+    (uint8_t)~0xf1,
+    (uint8_t)~0xff,
+    (uint8_t)~0xff,
+    (uint8_t)~0x00,
+};
+
+const uint8_t gr_mask[16] = {
+    (uint8_t)~0xf0, /* 0x00 */
+    (uint8_t)~0xf0, /* 0x01 */
+    (uint8_t)~0xf0, /* 0x02 */
+    (uint8_t)~0xe0, /* 0x03 */
+    (uint8_t)~0xfc, /* 0x04 */
+    (uint8_t)~0x84, /* 0x05 */
+    (uint8_t)~0xf0, /* 0x06 */
+    (uint8_t)~0xf0, /* 0x07 */
+    (uint8_t)~0x00, /* 0x08 */
+};
+
+static uint64_t stdvga_inb(uint64_t addr)
+{
+    struct hvm_hw_stdvga *s = &current->domain->arch.hvm_domain.stdvga;
+    uint8_t val = 0;
+    switch (addr) {
+    case 0x3c4:                 /* sequencer address register */
+        val = s->sr_index;
+        break;
+
+    case 0x3c5:                 /* sequencer data register */
+        if (s->sr_index < sizeof(s->sr))
+            val = s->sr[s->sr_index];
+        break;
+
+    case 0x3ce:                 /* graphics address register */
+        val = s->gr_index;
+        break;
+
+    case 0x3cf:                 /* graphics data register */
+        val = s->gr[s->gr_index];
+        break;
+
+    default:
+        gdprintk(XENLOG_WARNING, "unexpected io addr 0x%04x\n", (int)addr);
+    }
+    return val;
+}
+
+static uint64_t stdvga_in(ioreq_t *p)
+{
+    /* Satisfy reads from sequence and graphics registers using local values */
+    uint64_t data = 0;
+    switch (p->size) {
+    case 1:
+        data = stdvga_inb(p->addr);
+        break;
+
+    case 2:
+        data = stdvga_inb(p->addr);
+        data |= stdvga_inb(p->addr + 1) << 8;
+        break;
+
+    case 4:
+        data = stdvga_inb(p->addr);
+        data |= stdvga_inb(p->addr + 1) << 8;
+        data |= stdvga_inb(p->addr + 2) << 16;
+        data |= stdvga_inb(p->addr + 3) << 24;
+        break;
+
+    case 8:
+        data = stdvga_inb(p->addr);
+        data |= stdvga_inb(p->addr + 1) << 8;
+        data |= stdvga_inb(p->addr + 2) << 16;
+        data |= stdvga_inb(p->addr + 3) << 24;
+        data |= stdvga_inb(p->addr + 4) << 32;
+        data |= stdvga_inb(p->addr + 5) << 40;
+        data |= stdvga_inb(p->addr + 6) << 48;
+        data |= stdvga_inb(p->addr + 7) << 56;
+        break;
+
+    default:
+        gdprintk(XENLOG_WARNING, "invalid io size:%d\n", (int)p->size);
+    }
+    return data;
+}
+
+static void stdvga_outb(uint64_t addr, uint8_t val)
+{
+    /* Bookkeep (via snooping) the sequencer and graphics registers */
+
+    struct hvm_hw_stdvga *s = &current->domain->arch.hvm_domain.stdvga;
+    int prev_stdvga = s->stdvga;
+
+    switch (addr) {
+    case 0x3c4:                 /* sequencer address register */
+        s->sr_index = val;
+        break;
+
+    case 0x3c5:                 /* sequencer data register */
+        switch (s->sr_index) {
+        case 0x00 ... 0x05:
+        case 0x07:
+            s->sr[s->sr_index] = val & sr_mask[s->sr_index];
+            break;
+        case 0x06:
+            s->sr[s->sr_index] = ((val & 0x17) == 0x12) ? 0x12 : 0x0f;
+            break;
+        default:
+            if (s->sr_index < sizeof(s->sr))
+                s->sr[s->sr_index] = val;
+            break;
+        }
+        break;
+
+    case 0x3ce:                 /* graphics address register */
+        s->gr_index = val;
+        break;
+
+    case 0x3cf:                 /* graphics data register */
+        if (s->gr_index < sizeof(gr_mask)) {
+            s->gr[s->gr_index] = val & gr_mask[s->gr_index];
+        }
+        else if (s->gr_index == 0xff && s->vram_ptr != NULL) {
+            uint32_t addr;
+            for (addr = 0xa0000; addr < 0xa4000; addr += 2)
+                vram_w(s, addr) = (val << 8) | s->gr[0xfe];
+        }
+        else
+            s->gr[s->gr_index] = val;
+        break;
+    }
+
+    /* When in standard vga mode, emulate here all writes to the vram buffer
+     * so we can immediately satisfy reads without waiting for qemu. */
+    s->stdvga =
+        s->sr[0x07] == 0 &&          /* standard vga mode */
+        s->gr[6] == 0x05;            /* misc graphics register w/ 
MemoryMapSelect=1  0xa0000-0xaffff (64K region) and AlphaDis=1 */
+
+    if (!prev_stdvga && s->stdvga) {
+        s->cache = 1;       /* (re)start caching video buffer */
+        gdprintk(XENLOG_INFO, "entering stdvga and caching modes\n");
+    }
+    else
+    if (prev_stdvga && !s->stdvga)
+        gdprintk(XENLOG_INFO, "leaving  stdvga\n");
+}
+
+static void stdvga_outv(uint64_t addr, uint64_t data, uint32_t size)
+{
+    switch (size) {
+    case 1:
+        stdvga_outb(addr, data);
+        break;
+
+    case 2:
+        stdvga_outb(addr+0, data >>  0);
+        stdvga_outb(addr+1, data >>  8);
+        break;
+
+    case 4:
+        stdvga_outb(addr+0, data >>  0);
+        stdvga_outb(addr+1, data >>  8);
+        stdvga_outb(addr+2, data >> 16);
+        stdvga_outb(addr+3, data >> 24);
+        break;
+
+    case 8:
+        stdvga_outb(addr+0, data >>  0);
+        stdvga_outb(addr+1, data >>  8);
+        stdvga_outb(addr+2, data >> 16);
+        stdvga_outb(addr+3, data >> 24);
+        stdvga_outb(addr+4, data >> 32);
+        stdvga_outb(addr+5, data >> 40);
+        stdvga_outb(addr+6, data >> 48);
+        stdvga_outb(addr+7, data >> 56);
+        break;
+
+    default:
+        gdprintk(XENLOG_WARNING, "invalid io size:%d\n", size);
+    }
+}
+
+static void stdvga_out(ioreq_t *p)
+{
+    if (p->data_is_ptr) {
+        int i, sign = p->df ? -1 : 1;
+        uint64_t addr = p->addr, data = p->data, tmp;
+        for (i = 0; i < p->count; i++) {
+            hvm_copy_from_guest_phys(&tmp, data, p->size);
+            stdvga_outv(addr, tmp, p->size);
+            data += sign * p->size;
+            addr += sign * p->size;
+        }
+    }
+    else
+        stdvga_outv(p->addr, p->data, p->size);
+}
+
+int stdvga_intercept_pio(ioreq_t *p)
+{
+    struct hvm_hw_stdvga *s = &current->domain->arch.hvm_domain.stdvga;
+    int buf = 0;
+
+    if (p->size > 8) {
+        gdprintk(XENLOG_WARNING, "stdvga bad access size %d\n", (int)p->size);
+        return 0;
+    }
+
+    spin_lock(&s->lock);
+    if ( p->dir == IOREQ_READ ) {
+        if (p->size != 1)
+            gdprintk(XENLOG_WARNING, "unexpected io size:%d\n", (int)p->size);
+        if (!(p->addr == 0x3c5 && s->sr_index >= sizeof(sr_mask)) &&
+            !(p->addr == 0x3cf && s->gr_index >= sizeof(gr_mask)))
+        {
+            p->data = stdvga_in(p);
+            buf = 1;
+        }
+    }
+    else {
+        stdvga_out(p);
+        buf = 1;
+    }
+
+    if (buf && hvm_buffered_io_send(p)) {
+        UPDATE_STATS(s->stats.nr_pio_buffered_wr++);
+        spin_unlock(&s->lock);
+        return 1;
+    }
+    else {
+        UPDATE_STATS(s->stats.nr_pio_unbuffered_wr++);
+        spin_unlock(&s->lock);
+        return 0;
+    }
+}
+
+#define GET_PLANE(data, p) (((data) >> ((p) * 8)) & 0xff)
+
+static uint8_t stdvga_mem_readb(uint64_t addr)
+{
+    struct hvm_hw_stdvga *s = &current->domain->arch.hvm_domain.stdvga;
+    int plane;
+    uint32_t ret;
+
+    addr &= 0x1ffff;
+    if (addr >= 0x10000)
+        return 0xff;
+
+    if (s->sr[4] & 0x08) {
+        /* chain 4 mode : simplest access */
+        ret = vram_b(s, addr);
+    } else if (s->gr[5] & 0x10) {
+        /* odd/even mode (aka text mode mapping) */
+        plane = (s->gr[4] & 2) | (addr & 1);
+        ret = vram_b(s, ((addr & ~1) << 1) | plane);
+    } else {
+        /* standard VGA latched access */
+        s->latch = vram_l(s, addr);
+
+        if (!(s->gr[5] & 0x08)) {
+            /* read mode 0 */
+            plane = s->gr[4];
+            ret = GET_PLANE(s->latch, plane);
+        } else {
+            /* read mode 1 */
+            ret = (s->latch ^ mask16[s->gr[2]]) & mask16[s->gr[7]];
+            ret |= ret >> 16;
+            ret |= ret >> 8;
+            ret = (~ret) & 0xff;
+        }
+    }
+    return ret;
+}
+
+static uint32_t stdvga_mem_read(uint32_t addr, uint32_t size)
+{
+    uint32_t data = 0;
+
+    switch (size) {
+    case 1:
+        data = stdvga_mem_readb(addr);
+        break;
+
+    case 2:
+        data = stdvga_mem_readb(addr);
+        data |= stdvga_mem_readb(addr + 1) << 8;
+        break;
+
+    case 4:
+        data = stdvga_mem_readb(addr);
+        data |= stdvga_mem_readb(addr + 1) << 8;
+        data |= stdvga_mem_readb(addr + 2) << 16;
+        data |= stdvga_mem_readb(addr + 3) << 24;
+        break;
+
+    default:
+        gdprintk(XENLOG_WARNING, "invalid io size:%d\n", size);
+    }
+    return data;
+}
+
+static void stdvga_mem_writeb(uint64_t addr, uint32_t val)
+{
+    struct hvm_hw_stdvga *s = &current->domain->arch.hvm_domain.stdvga;
+    int plane, write_mode, b, func_select, mask;
+    uint32_t write_mask, bit_mask, set_mask;
+
+    addr &= 0x1ffff;
+    if (addr >= 0x10000)
+        return;
+
+    if (s->sr[4] & 0x08) {
+        /* chain 4 mode : simplest access */
+        plane = addr & 3;
+        mask = (1 << plane);
+        if (s->sr[2] & mask) {
+            vram_b(s, addr) = val;
+        }
+    } else if (s->gr[5] & 0x10) {
+        /* odd/even mode (aka text mode mapping) */
+        plane = (s->gr[4] & 2) | (addr & 1);
+        mask = (1 << plane);
+        if (s->sr[2] & mask) {
+            addr = ((addr & ~1) << 1) | plane;
+            vram_b(s, addr) = val;
+        }
+    } else {
+        write_mode = s->gr[5] & 3;
+        switch(write_mode) {
+        default:
+        case 0:
+            /* rotate */
+            b = s->gr[3] & 7;
+            val = ((val >> b) | (val << (8 - b))) & 0xff;
+            val |= val << 8;
+            val |= val << 16;
+
+            /* apply set/reset mask */
+            set_mask = mask16[s->gr[1]];
+            val = (val & ~set_mask) | (mask16[s->gr[0]] & set_mask);
+            bit_mask = s->gr[8];
+            break;
+        case 1:
+            val = s->latch;
+            goto do_write;
+        case 2:
+            val = mask16[val & 0x0f];
+            bit_mask = s->gr[8];
+            break;
+        case 3:
+            /* rotate */
+            b = s->gr[3] & 7;
+            val = (val >> b) | (val << (8 - b));
+
+            bit_mask = s->gr[8] & val;
+            val = mask16[s->gr[0]];
+            break;
+        }
+
+        /* apply logical operation */
+        func_select = s->gr[3] >> 3;
+        switch(func_select) {
+        case 0:
+        default:
+            /* nothing to do */
+            break;
+        case 1:
+            /* and */
+            val &= s->latch;
+            break;
+        case 2:
+            /* or */
+            val |= s->latch;
+            break;
+        case 3:
+            /* xor */
+            val ^= s->latch;
+            break;
+        }
+
+        /* apply bit mask */
+        bit_mask |= bit_mask << 8;
+        bit_mask |= bit_mask << 16;
+        val = (val & bit_mask) | (s->latch & ~bit_mask);
+
+    do_write:
+        /* mask data according to sr[2] */
+        mask = s->sr[2];
+        write_mask = mask16[mask];
+        vram_l(s, addr) =
+            (vram_l(s, addr) & ~write_mask) |
+            (val & write_mask);
+    }
+}
+
+static void stdvga_mem_write(uint32_t addr, uint32_t data, uint32_t size)
+{
+    /* Intercept mmio write */
+    switch (size) {
+    case 1:
+        stdvga_mem_writeb(addr, (data >>  0) & 0xff);
+        break;
+
+    case 2:
+        stdvga_mem_writeb(addr+0, (data >>  0) & 0xff);
+        stdvga_mem_writeb(addr+1, (data >>  8) & 0xff);
+        break;
+
+    case 4:
+        stdvga_mem_writeb(addr+0, (data >>  0) & 0xff);
+        stdvga_mem_writeb(addr+1, (data >>  8) & 0xff);
+        stdvga_mem_writeb(addr+2, (data >> 16) & 0xff);
+        stdvga_mem_writeb(addr+3, (data >> 24) & 0xff);
+        break;
+
+    default:
+        gdprintk(XENLOG_WARNING, "invalid io size:%d\n", size);
+    }
+}
+
+static uint32_t read_data;
+
+static int mmio_move(struct hvm_hw_stdvga *s, ioreq_t *p)
+{
+    int i;
+    int sign = p->df ? -1 : 1;
+
+    if (p->data_is_ptr) {
+        if (p->dir == IOREQ_READ ) {
+            uint32_t addr = p->addr, data = p->data, tmp;
+            for (i = 0; i < p->count; i++) {
+                tmp = stdvga_mem_read(addr, p->size);
+                hvm_copy_to_guest_phys(data, &tmp, p->size);
+                data += sign * p->size;
+                addr += sign * p->size;
+            }
+        }
+        else {
+            uint32_t addr = p->addr, data = p->data, tmp;
+            for (i = 0; i < p->count; i++) {
+                hvm_copy_from_guest_phys(&tmp, data, p->size);
+                stdvga_mem_write(addr, tmp, p->size);
+                data += sign * p->size;
+                addr += sign * p->size;
+            }
+        }
+    }
+    else {
+        if (p->dir == IOREQ_READ ) {
+            uint32_t addr = p->addr;
+            for (i = 0; i < p->count; i++) {
+                p->data = stdvga_mem_read(addr, p->size);
+                addr += sign * p->size;
+            }
+        }
+        else {
+            uint32_t addr = p->addr;
+            for (i = 0; i < p->count; i++) {
+                stdvga_mem_write(addr, p->data, p->size);
+                addr += sign * p->size;
+            }
+        }
+    }
+
+    read_data = p->data;
+    return 1;
+}
+
+static uint32_t op_and(uint32_t a, uint32_t b) { return a & b; }
+static uint32_t op_or (uint32_t a, uint32_t b) { return a | b; }
+static uint32_t op_xor(uint32_t a, uint32_t b) { return a ^ b; }
+static uint32_t op_add(uint32_t a, uint32_t b) { return a + b; }
+static uint32_t op_sub(uint32_t a, uint32_t b) { return a - b; }
+static uint32_t (*op_array[])(uint32_t, uint32_t) = {
+    [IOREQ_TYPE_AND] = op_and,
+    [IOREQ_TYPE_OR ] = op_or,
+    [IOREQ_TYPE_XOR] = op_xor,
+    [IOREQ_TYPE_ADD] = op_add,
+    [IOREQ_TYPE_SUB] = op_sub
+};
+
+static int mmio_op(struct hvm_hw_stdvga *s, ioreq_t *p)
+{
+    uint32_t orig, mod = 0;
+    orig = stdvga_mem_read(p->addr, p->size);
+    if (p->dir == IOREQ_WRITE) {
+        mod = (op_array[p->type])(orig, p->data);
+        stdvga_mem_write(p->addr, mod, p->size);
+    }
+    // p->data = orig; // Can't modify p->data yet.  QEMU still needs to use 
it.  So return zero below.
+    return 0; /* Don't try to buffer these operations */
+}
+
+int stdvga_intercept_mmio(ioreq_t *p)
+{
+    struct domain *d = current->domain;
+    struct hvm_hw_stdvga *s = &d->arch.hvm_domain.stdvga;
+    int buf = 0;
+
+    if (p->size > 8) {
+        gdprintk(XENLOG_WARNING, "invalid mmio size %d\n", (int)p->size);
+        return 0;
+    }
+
+    spin_lock(&s->lock);
+
+    if (s->stdvga && s->cache) {
+        switch (p->type) {
+        case IOREQ_TYPE_COPY:
+            buf = mmio_move(s, p);
+            break;
+        case IOREQ_TYPE_AND:
+        case IOREQ_TYPE_OR:
+        case IOREQ_TYPE_XOR:
+        case IOREQ_TYPE_ADD:
+        case IOREQ_TYPE_SUB:
+            buf = mmio_op(s, p);
+            break;
+        default:
+            gdprintk(XENLOG_ERR, "unsupported mmio request type:%d "
+                     "addr:0x%04x data:0x%04x size:%d count:%d state:%d 
isptr:%d dir:%d df:%d\n",
+                     p->type,
+                     (int)p->addr, (int)p->data, (int)p->size, (int)p->count, 
p->state,
+                     p->data_is_ptr, p->dir, p->df);
+            s->cache = 0;
+        }
+    }
+    if (buf && hvm_buffered_io_send(p)) {
+        UPDATE_STATS(p->dir == IOREQ_READ ? s->stats.nr_mmio_buffered_rd++ : 
s->stats.nr_mmio_buffered_wr++);
+        spin_unlock(&s->lock);
+        return 1;
+    }
+    else {
+        UPDATE_STATS(p->dir == IOREQ_READ ? s->stats.nr_mmio_unbuffered_rd++ : 
s->stats.nr_mmio_unbuffered_wr++);
+        spin_unlock(&s->lock);
+        return 0;
+    }
+}
+
+void stdvga_init(struct domain *d)
+{
+    int i;
+    struct hvm_hw_stdvga *s = &d->arch.hvm_domain.stdvga;
+    memset(s, 0, sizeof(*s));
+    spin_lock_init(&s->lock);
+    
+    for (i = 0; i != ARRAY_SIZE(s->vram_ptr); i++) {
+        struct page_info *vram_page;
+        vram_page = alloc_domheap_page(NULL);
+        if (!vram_page)
+            break;
+        s->vram_ptr[i] = page_to_virt(vram_page);
+        memset(s->vram_ptr[i], 0, PAGE_SIZE);
+    }
+    if (i == ARRAY_SIZE(s->vram_ptr)) {
+        register_portio_handler(d, 0x3c4, 2, stdvga_intercept_pio); /* 
sequencer registers */
+        register_portio_handler(d, 0x3ce, 2, stdvga_intercept_pio); /* 
graphics registers */
+        register_buffered_io_handler(d, 0xa0000, 0x10000, 
stdvga_intercept_mmio); /* mmio */
+    }
+}
+
+void stdvga_deinit(struct domain *d)
+{
+    struct hvm_hw_stdvga *s = &d->arch.hvm_domain.stdvga;
+    int i;
+    for (i = 0; i != ARRAY_SIZE(s->vram_ptr); i++) {
+        struct page_info *vram_page;
+        if (s->vram_ptr[i] == NULL)
+            continue;
+        vram_page = virt_to_page(s->vram_ptr[i]);
+        free_domheap_page(vram_page);
+        s->vram_ptr[i] = NULL;
+    }
+}
+
+#ifdef STDVGA_STATS
+static void stdvga_stats_dump(unsigned char key)
+{
+    struct domain *d;
+
+    printk("%s: key '%c' pressed\n", __FUNCTION__, key);
+
+    rcu_read_lock(&domlist_read_lock);
+
+    for_each_domain ( d )
+    {
+        struct hvm_hw_stdvga *s;
+        int i;
+
+        if ( !is_hvm_domain(d) )
+            continue;
+
+        s = &d->arch.hvm_domain.stdvga;
+        spin_lock(&s->lock);
+        printk("\n>>> Domain %d <<<\n", d->domain_id);
+        printk("    modes: stdvga:%d caching:%d\n", s->stdvga, s->cache);
+        printk("                       %8s %8s\n", "read", "write");
+        printk("    nr_mmio_buffered:  %8u %8u\n", 
s->stats.nr_mmio_buffered_rd, s->stats.nr_mmio_buffered_wr);
+        printk("    nr_mmio_unbuffered:%8u %8u\n", 
s->stats.nr_mmio_unbuffered_rd, s->stats.nr_mmio_unbuffered_wr);
+        printk("    nr_pio_buffered:   %8u %8u\n", 
s->stats.nr_pio_buffered_rd, s->stats.nr_pio_buffered_wr);
+        printk("    nr_pio_unbuffered: %8u %8u\n", 
s->stats.nr_pio_unbuffered_rd, s->stats.nr_pio_unbuffered_wr);
+
+        for (i = 0; i != sizeof(s->sr); i++) {
+            if (i % 8 == 0)
+                printk("    sr[0x%02x] ", i);
+            printk("%02x ", s->sr[i]);
+            if (i % 8 == 7)
+                printk("\n");
+        }
+        if (i % 8 != 7)
+            printk("\n");
+
+        for (i = 0; i != sizeof(s->gr); i++) {
+            if (i % 8 == 0)
+                printk("    gr[0x%02x] ", i);
+            printk("%02x ", s->gr[i]);
+            if (i % 8 == 7)
+                printk("\n");
+        }
+        if (i % 8 != 7)
+            printk("\n");
+
+        memset(&s->stats, 0, sizeof(s->stats));
+
+        spin_unlock(&s->lock);
+    }
+
+    rcu_read_unlock(&domlist_read_lock);
+}
+
+#include <xen/keyhandler.h>
+
+static int __init setup_stdvga_stats_dump(void)
+{
+    register_keyhandler('<', stdvga_stats_dump, "dump stdvga stats");
+    return 0;
+}
+
+__initcall(setup_stdvga_stats_dump);
+
+#endif
+
diff -r 118a21c66fd5 xen/include/asm-x86/hvm/domain.h
--- a/xen/include/asm-x86/hvm/domain.h  Mon Oct 22 21:06:11 2007 +0100
+++ b/xen/include/asm-x86/hvm/domain.h  Wed Oct 24 17:31:57 2007 -0400
@@ -51,6 +51,7 @@ struct hvm_domain {
     struct hvm_irq         irq;
     struct hvm_hw_vpic     vpic[2]; /* 0=master; 1=slave */
     struct hvm_vioapic    *vioapic;
+    struct hvm_hw_stdvga   stdvga;
 
     /* hvm_print_line() logging. */
     char                   pbuf[80];
diff -r 118a21c66fd5 xen/include/asm-x86/hvm/io.h
--- a/xen/include/asm-x86/hvm/io.h      Mon Oct 22 21:06:11 2007 +0100
+++ b/xen/include/asm-x86/hvm/io.h      Wed Oct 24 17:31:57 2007 -0400
@@ -80,10 +80,11 @@ struct hvm_io_op {
     struct cpu_user_regs    io_context; /* current context */
 };
 
-#define MAX_IO_HANDLER              9
+#define MAX_IO_HANDLER             12
 
 #define HVM_PORTIO                  0
 #define HVM_MMIO                    1
+#define HVM_BUFFERED_IO             2
 
 typedef int (*intercept_action_t)(ioreq_t *);
 typedef unsigned long (*hvm_mmio_read_t)(struct vcpu *v,
@@ -126,15 +127,26 @@ static inline int hvm_portio_intercept(i
     return hvm_io_intercept(p, HVM_PORTIO);
 }
 
+static inline int hvm_buffered_io_intercept(ioreq_t *p)
+{
+    return hvm_io_intercept(p, HVM_BUFFERED_IO);
+}
+
 extern int hvm_mmio_intercept(ioreq_t *p);
 extern int hvm_buffered_io_send(ioreq_t *p);
-extern int hvm_buffered_io_intercept(ioreq_t *p);
 
 static inline int register_portio_handler(
     struct domain *d, unsigned long addr,
     unsigned long size, intercept_action_t action)
 {
     return register_io_handler(d, addr, size, action, HVM_PORTIO);
+}
+
+static inline int register_buffered_io_handler(
+    struct domain *d, unsigned long addr,
+    unsigned long size, intercept_action_t action)
+{
+    return register_io_handler(d, addr, size, action, HVM_BUFFERED_IO);
 }
 
 #if defined(__i386__) || defined(__x86_64__)
@@ -154,5 +166,38 @@ extern void hvm_dpci_eoi(struct domain *
 extern void hvm_dpci_eoi(struct domain *d, unsigned int guest_irq,
                          union vioapic_redir_entry *ent);
 
+
+#undef  STDVGA_STATS /* #define to enable stdvga statistics */
+#undef  STDVGA_CHECK /* debug: ensure cached value matches qemu value */
+
+struct hvm_hw_stdvga {
+    uint8_t sr_index;
+    uint8_t sr[0x18];
+    uint8_t gr_index;
+    uint8_t gr[256];
+    uint32_t latch;
+    int stdvga;
+    int cache;
+    uint8_t *vram_ptr[64];  /* shadow of 0xa0000-0xaffff */
+    spinlock_t lock;
+    
+#ifdef STDVGA_STATS
+    struct {
+        uint32_t nr_mmio_buffered_rd;
+        uint32_t nr_mmio_buffered_wr;
+        uint32_t nr_mmio_unbuffered_rd;
+        uint32_t nr_mmio_unbuffered_wr;
+        uint32_t nr_pio_buffered_rd;
+        uint32_t nr_pio_buffered_wr;
+        uint32_t nr_pio_unbuffered_rd;
+        uint32_t nr_pio_unbuffered_wr;
+    } stats;
+#endif
+};
+
+extern void stdvga_init(struct domain *d);
+extern void stdvga_deinit(struct domain *d);
+extern void stdvga_check_cached_value(ioreq_t *p);
+
 #endif /* __ASM_X86_HVM_IO_H__ */
 
diff -r 118a21c66fd5 xen/include/public/hvm/ioreq.h
--- a/xen/include/public/hvm/ioreq.h    Mon Oct 22 21:06:11 2007 +0100
+++ b/xen/include/public/hvm/ioreq.h    Wed Oct 24 17:31:57 2007 -0400
@@ -77,13 +77,26 @@ struct shared_iopage {
 };
 typedef struct shared_iopage shared_iopage_t;
 
-#define IOREQ_BUFFER_SLOT_NUM     80
+#pragma pack(push,2)
+
+struct buf_ioreq {
+    uint8_t  type;   /*  I/O type                    */
+    uint8_t  dir:1;  /*  1=read, 0=write             */
+    uint8_t  size:2; /*  0=>1, 1=>2, 3=>8. If 8 then use two contig buf_ioreqs 
*/
+    uint32_t addr:20; /*  physical address or high-order data */
+    uint16_t data;   /*  (low order) data            */
+};
+typedef struct buf_ioreq buf_ioreq_t;
+
+#define IOREQ_BUFFER_SLOT_NUM     672
 struct buffered_iopage {
-    unsigned int    read_pointer;
-    unsigned int    write_pointer;
-    ioreq_t         ioreq[IOREQ_BUFFER_SLOT_NUM];
+    volatile unsigned int read_pointer;
+    volatile unsigned int write_pointer;
+    buf_ioreq_t buf_ioreq[IOREQ_BUFFER_SLOT_NUM];
 }; /* NB. Size of this structure must be no greater than one page. */
 typedef struct buffered_iopage buffered_iopage_t;
+
+#pragma pack(pop)
 
 #if defined(__ia64__)
 struct pio_buffer {
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel