From c102babe701f4d19be4b4c064e9412aef939576f Mon Sep 17 00:00:00 2001 From: Eduardo Habkost <ehabkost@redhat.com> Date: Thu, 29 Jan 2009 15:34:04 -0200 Subject: [PATCH 40/54] vmchannel: Update to hypercall device implementation sent by Uri Got from: http://violet-rega.eng.lab.tlv.redhat.com/vmchannel (I hope the piix3_devfn stuff is correct.) Signed-off-by: Eduardo Habkost <ehabkost@redhat.com> --- qemu/hw/hypercall.c | 115 +++++++++++++++++++++++++++++++++++--------------- qemu/hw/hypercall.h | 20 +++++---- qemu/hw/pc.c | 2 +- qemu/sysemu.h | 2 +- 4 files changed, 93 insertions(+), 46 deletions(-) diff --git a/qemu/hw/hypercall.c b/qemu/hw/hypercall.c index 73f6bb1..5bec27a 100644 --- a/qemu/hw/hypercall.c +++ b/qemu/hw/hypercall.c @@ -48,31 +48,35 @@ typedef struct HypercallState { uint32_t txsize; uint32_t txbuff; uint32_t rxsize; + uint32_t version; uint8_t RxBuff[HP_MEM_SIZE]; uint8_t txbufferaccu[HP_MEM_SIZE]; int txbufferaccu_offset; int irq; PCIDevice *pci_dev; uint32_t index; + uint32_t driver_state; } HypercallState; static HypercallState *pHypercallStates[MAX_VMCHANNEL_DEVICES] = {NULL}; //#define HYPERCALL_DEBUG 1 +static void hypercall_update_irq(HypercallState *s); + static void hp_reset(HypercallState *s) { - s->hcr = HCR_DI; + s->hcr = 0; s->hsr = 0; s->txsize = 0; s->txbuff = 0; s->rxsize= 0; s->txbufferaccu_offset = 0; + s->version = HP_VERSION; + s->driver_state = 0; + hypercall_update_irq(s); } -static void hypercall_update_irq(HypercallState *s); - - static void hp_ioport_write(void *opaque, uint32_t addr, uint32_t val) { HypercallState *s = opaque; @@ -84,17 +88,22 @@ static void hp_ioport_write(void *opaque, uint32_t addr, uint32_t val) switch(addr) { - case HCR_REGISTER: - { - s->hcr = val; - if (s->hcr & HCR_DI) + case HSR_REGISTER: { + s->hsr = (s->hsr & ~HSR_PENDING) + (val & HSR_PENDING); hypercall_update_irq(s); - if (val & HCR_GRS){ - hp_reset(s); - } - break; } - + break; + case HCR_REGISTER: { + s->hcr = (s->hcr & ~HCR_IRQ_ON) + (val & HCR_IRQ_ON); + hypercall_update_irq(s); + } + break; + case HP_VERSION_REGISTER: + if (s->version == val) + s->driver_state |= HP_DRIVER_INITELIZED; + else + printf("%s:version mismatch: ours %d, driver's %d\n", __FUNCTION__, s->version, val); + break; case HP_TXSIZE: { // handle the case when the we are being called when txsize is not 0 @@ -103,6 +112,7 @@ static void hp_ioport_write(void *opaque, uint32_t addr, uint32_t val) } if (val > HP_MEM_SIZE) { printf("txsize is larger than allowed by hw!!!\n"); + break; } s->txsize = val; s->txbufferaccu_offset = 0; @@ -119,7 +129,12 @@ static void hp_ioport_write(void *opaque, uint32_t addr, uint32_t val) s->txbufferaccu[s->txbufferaccu_offset] = val; s->txbufferaccu_offset++; if (s->txbufferaccu_offset >= s->txsize) { - qemu_chr_write(vmchannel_hds[s->index].vmchannel_hd, s->txbufferaccu, s->txsize); + int rs; + rs = qemu_chr_write(vmchannel_hds[s->index].vmchannel_hd, s->txbufferaccu, s->txsize); + if (rs < 0) + printf("%s:error from qemu_chr_write\n", __FUNCTION__); + else if (rs != s->txsize) + printf("%s:error from qemu_chr_write, read %d/%d bytes\n", __FUNCTION__, rs, s->txsize); s->txbufferaccu_offset = 0; s->txsize = 0; } @@ -139,8 +154,6 @@ static uint32_t hp_ioport_read(void *opaque, uint32_t addr) addr &= 0xff; #ifdef HYPERCALL_DEBUG - // Since HSR_REGISTER is being repeatedly read in the guest ISR we don't print it - if (addr != HSR_REGISTER) printf("%s: addr=0x%x", __FUNCTION__, addr); #endif @@ -156,11 +169,14 @@ static uint32_t hp_ioport_read(void *opaque, uint32_t addr) switch (addr) { - case HSR_REGISTER: + case HP_VERSION_REGISTER: + ret = s->version; + break; + case HSR_REGISTER: ret = s->hsr; - if (ret & HSR_VDR) { - s->hsr &= ~HSR_VDR; - } + break; + case HCR_REGISTER: + ret = s->hcr; break; case HP_RXSIZE: ret = s->rxsize; @@ -198,8 +214,15 @@ static void hp_map(PCIDevice *pci_dev, int region_num, static void hypercall_update_irq(HypercallState *s) { - /* PCI irq */ - qemu_set_irq(s->pci_dev->irq[0], !(s->hcr & HCR_DI)); + int irq_level = (s->hsr & HSR_PENDING) && (s->hcr & HCR_IRQ_ON); + + if (!(s->driver_state & HP_DRIVER_INITELIZED)) + return; + +#ifdef HYPERCALL_DEBUG + printf("%s, irq_level=%d ,hsr=%d hcr=%d\n", __FUNCTION__, irq_level, s->hsr, s->hcr); +#endif + qemu_set_irq(s->pci_dev->irq[s->pci_dev->config[0x3d]-1], irq_level != 0); } static void hc_save(QEMUFile* f,void* opaque) @@ -218,7 +241,7 @@ static void hc_save(QEMUFile* f,void* opaque) qemu_put_be32s(f, &s->txbufferaccu_offset); qemu_put_be32s(f, &s->irq); qemu_put_be32s(f, &s->index); - + qemu_put_be32s(f, &s->driver_state); } static int hc_load(QEMUFile* f,void* opaque,int version_id) @@ -226,7 +249,7 @@ static int hc_load(QEMUFile* f,void* opaque,int version_id) HypercallState* s=(HypercallState*)opaque; int ret; - if (version_id != 1) + if (version_id > 2) return -EINVAL; ret = pci_device_load(s->pci_dev, f); @@ -244,10 +267,15 @@ static int hc_load(QEMUFile* f,void* opaque,int version_id) qemu_get_be32s(f, &s->irq); qemu_get_be32s(f, &s->index); + if (version_id >= 2) + qemu_get_be32s(f, &s->driver_state); + else + s->driver_state = 0; + return 0; } -static void pci_hypercall_single_init(PCIBus *bus, uint32_t deviceid, uint32_t index) +static void pci_hypercall_single_init(PCIBus *bus, uint32_t deviceid, uint32_t index,int devfn) { PCIHypercallState *d; HypercallState *s; @@ -267,8 +295,13 @@ static void pci_hypercall_single_init(PCIBus *bus, uint32_t deviceid, uint32_t i d = (PCIHypercallState *)pci_register_device(bus, name, sizeof(PCIHypercallState), - -1, + devfn, NULL, NULL); + if (!d) { + fprintf(stderr, "Failed to register PCI device for vmchannel\n"); + return; + } + pci_conf = d->dev.config; pci_conf[0x00] = 0x02; // Qumranet vendor ID 0x5002 @@ -281,7 +314,7 @@ static void pci_hypercall_single_init(PCIBus *bus, uint32_t deviceid, uint32_t i pci_conf[0x0b] = 0x05; // BaseClass pci_conf[0x0e] = 0x00; // header_type - pci_conf[0x3d] = 1; // interrupt pin 0 + pci_conf[0x3d] = 2; // interrupt pin 1 pci_register_io_region(&d->dev, 0, HYPERCALL_IOPORT_SIZE, PCI_ADDRESS_SPACE_IO, hp_map); @@ -289,27 +322,38 @@ static void pci_hypercall_single_init(PCIBus *bus, uint32_t deviceid, uint32_t i pHypercallStates[index] = s; s->index = index; s->irq = 16; /* PCI interrupt */ - s->pci_dev = (PCIDevice *)d; + s->pci_dev = &d->dev; hp_reset(s); - register_savevm(name, index, 1, hc_save, hc_load, s); + qemu_register_reset(hp_reset, s); + register_savevm(name, index, 2, hc_save, hc_load, s); } -void pci_hypercall_init(PCIBus *bus) +int pci_hypercall_init(PCIBus *bus,int devfn) { int i; // loop devices & call pci_hypercall_single_init with device id's for(i = 0; i < MAX_VMCHANNEL_DEVICES; i++){ if (vmchannel_hds[i].vmchannel_hd) { - pci_hypercall_single_init(bus, vmchannel_hds[i].deviceid, i); + pci_hypercall_single_init(bus, vmchannel_hds[i].deviceid, i,++devfn); } } + + return devfn; } + +// If the VDR is set, data is waiting to be read by the guest. +// don't let the vmchannel device write into +// the buffer since the guest might be in the middle of a read static int vmchannel_can_read(void *opaque) { - return 128; + long index = (long)opaque; + if (pHypercallStates[index]->hsr & HSR_PENDING) + return 0; + else + return HP_MEM_SIZE; } static void vmchannel_event(void *opaque, int event) @@ -324,7 +368,7 @@ static void vmchannel_event(void *opaque, int event) return; } -// input from vmchannel outside caller +// input from vmchannel device in order of inject it into the guest static void vmchannel_read(void *opaque, const uint8_t *buf, int size) { int i; @@ -335,7 +379,8 @@ static void vmchannel_read(void *opaque, const uint8_t *buf, int size) #endif // if the hypercall device is in interrupts disabled state, don't accept the data - if (pHypercallStates[index]->hcr & HCR_DI) { + if (!(pHypercallStates[index]->hcr & HCR_IRQ_ON)) { + printf("%s: error: got read during interrupt disabled\n", __FUNCTION__); return; } @@ -343,7 +388,7 @@ static void vmchannel_read(void *opaque, const uint8_t *buf, int size) pHypercallStates[index]->RxBuff[i] = buf[i]; } pHypercallStates[index]->rxsize = size; - pHypercallStates[index]->hsr = HSR_VDR; + pHypercallStates[index]->hsr |= HSR_PENDING; hypercall_update_irq(pHypercallStates[index]); } diff --git a/qemu/hw/hypercall.h b/qemu/hw/hypercall.h index 97434a7..de0a414 100644 --- a/qemu/hw/hypercall.h +++ b/qemu/hw/hypercall.h @@ -23,22 +23,24 @@ * THE SOFTWARE. */ -#define HCR_REGISTER 0x00 // Hypercall Command Register WR -#define HSR_REGISTER 0x04 // Hypercall Status Register RD +#define HCR_REGISTER 0x0 // Command register +#define HSR_REGISTER 0x04 // Status register #define HP_TXSIZE 0x08 #define HP_TXBUFF 0x0c #define HP_RXSIZE 0x10 -#define HP_RXBUFF 0x14 +#define HP_VERSION_REGISTER 0x14 +#define HP_RXBUFF 0x18 -// HCR_REGISTER commands -#define HCR_DI 1 // disable interrupts -#define HCR_EI 2 // enable interrupts -#define HCR_GRS 4 // Global reset -#define HCR_RESET (HCR_GRS|HCR_DI) +#define HP_VERSION 3 +//After version check +#define HP_DRIVER_INITELIZED 1 + +// Bits in HCR_REGISTER +#define HCR_IRQ_ON 1 // Bits in HSR_REGISTER -#define HSR_VDR 0x01 // vmchannel Data is ready to be read +#define HSR_PENDING 1 #define HP_MEM_SIZE 0xE0 diff --git a/qemu/hw/pc.c b/qemu/hw/pc.c index 951c4f4..7e5cb1c 100644 --- a/qemu/hw/pc.c +++ b/qemu/hw/pc.c @@ -1086,7 +1086,7 @@ static void pc_init1(ram_addr_t ram_size, int vga_ram_size, qemu_system_hot_add_init(cpu_model); #define USE_HYPERCALL #ifdef USE_HYPERCALL - pci_hypercall_init(pci_bus); + piix3_devfn = pci_hypercall_init(pci_bus, piix3_devfn); #endif if (drive_get_max_bus(IF_IDE) >= MAX_IDE_BUS) { diff --git a/qemu/sysemu.h b/qemu/sysemu.h index 7e271fa..d368a91 100644 --- a/qemu/sysemu.h +++ b/qemu/sysemu.h @@ -185,7 +185,7 @@ void device_hot_remove_success(int pcibus, int slot); /* vmchannel devices */ #define MAX_VMCHANNEL_DEVICES 4 -void pci_hypercall_init(PCIBus *bus); +int pci_hypercall_init(PCIBus *bus, int devfn); void vmchannel_init(CharDriverState *hd, uint32_t deviceid, uint32_t index); /* serial ports */ -- 1.6.1