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 +#include +#include +#include + +#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 = ¤t->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 = ¤t->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 = ¤t->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 = ¤t->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 = ¤t->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 + +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 {