aboutsummaryrefslogtreecommitdiff
path: root/hw/virtio.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/virtio.c')
-rw-r--r--hw/virtio.c274
1 files changed, 56 insertions, 218 deletions
diff --git a/hw/virtio.c b/hw/virtio.c
index 21108c5c2b..463d74b394 100644
--- a/hw/virtio.c
+++ b/hw/virtio.c
@@ -16,43 +16,6 @@
#include "virtio.h"
#include "sysemu.h"
-/* from Linux's linux/virtio_pci.h */
-
-/* A 32-bit r/o bitmask of the features supported by the host */
-#define VIRTIO_PCI_HOST_FEATURES 0
-
-/* A 32-bit r/w bitmask of features activated by the guest */
-#define VIRTIO_PCI_GUEST_FEATURES 4
-
-/* A 32-bit r/w PFN for the currently selected queue */
-#define VIRTIO_PCI_QUEUE_PFN 8
-
-/* A 16-bit r/o queue size for the currently selected queue */
-#define VIRTIO_PCI_QUEUE_NUM 12
-
-/* A 16-bit r/w queue selector */
-#define VIRTIO_PCI_QUEUE_SEL 14
-
-/* A 16-bit r/w queue notifier */
-#define VIRTIO_PCI_QUEUE_NOTIFY 16
-
-/* An 8-bit device status register. */
-#define VIRTIO_PCI_STATUS 18
-
-/* An 8-bit r/o interrupt status register. Reading the value will return the
- * current contents of the ISR and will also clear it. This is effectively
- * a read-and-acknowledge. */
-#define VIRTIO_PCI_ISR 19
-
-#define VIRTIO_PCI_CONFIG 20
-
-/* Virtio ABI version, if we increment this, we break the guest driver. */
-#define VIRTIO_PCI_ABI_VERSION 0
-
-/* How many bits to shift physical queue address written to QUEUE_PFN.
- * 12 is historical, and due to x86 page size. */
-#define VIRTIO_PCI_QUEUE_ADDR_SHIFT 12
-
/* The alignment to use between consumer and producer parts of vring.
* x86 pagesize again. */
#define VIRTIO_PCI_VRING_ALIGN 4096
@@ -102,7 +65,7 @@ typedef struct VRing
struct VirtQueue
{
VRing vring;
- uint32_t pfn;
+ target_phys_addr_t pa;
uint16_t last_avail_idx;
int inuse;
void (*handle_output)(VirtIODevice *vdev, VirtQueue *vq);
@@ -111,8 +74,10 @@ struct VirtQueue
#define VIRTIO_PCI_QUEUE_MAX 16
/* virt queue functions */
-static void virtqueue_init(VirtQueue *vq, target_phys_addr_t pa)
+static void virtqueue_init(VirtQueue *vq)
{
+ target_phys_addr_t pa = vq->pa;
+
vq->vring.desc = pa;
vq->vring.avail = pa + vq->vring.num * sizeof(VRingDesc);
vq->vring.used = vring_align(vq->vring.avail +
@@ -409,17 +374,14 @@ int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem)
/* virtio device */
-static VirtIODevice *to_virtio_device(PCIDevice *pci_dev)
+void virtio_update_irq(VirtIODevice *vdev)
{
- return (VirtIODevice *)pci_dev;
-}
-
-static void virtio_update_irq(VirtIODevice *vdev)
-{
- qemu_set_irq(vdev->pci_dev.irq[0], vdev->isr & 1);
+ if (vdev->binding->update_irq) {
+ vdev->binding->update_irq(vdev->binding_opaque);
+ }
}
-static void virtio_reset(void *opaque)
+void virtio_reset(void *opaque)
{
VirtIODevice *vdev = opaque;
int i;
@@ -438,103 +400,16 @@ static void virtio_reset(void *opaque)
vdev->vq[i].vring.avail = 0;
vdev->vq[i].vring.used = 0;
vdev->vq[i].last_avail_idx = 0;
- vdev->vq[i].pfn = 0;
+ vdev->vq[i].pa = 0;
}
}
-static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)
-{
- VirtIODevice *vdev = to_virtio_device(opaque);
- ram_addr_t pa;
-
- addr -= vdev->addr;
-
- switch (addr) {
- case VIRTIO_PCI_GUEST_FEATURES:
- /* Guest does not negotiate properly? We have to assume nothing. */
- if (val & (1 << VIRTIO_F_BAD_FEATURE)) {
- if (vdev->bad_features)
- val = vdev->bad_features(vdev);
- else
- val = 0;
- }
- if (vdev->set_features)
- vdev->set_features(vdev, val);
- vdev->features = val;
- break;
- case VIRTIO_PCI_QUEUE_PFN:
- pa = (ram_addr_t)val << VIRTIO_PCI_QUEUE_ADDR_SHIFT;
- vdev->vq[vdev->queue_sel].pfn = val;
- if (pa == 0) {
- virtio_reset(vdev);
- } else {
- virtqueue_init(&vdev->vq[vdev->queue_sel], pa);
- }
- break;
- case VIRTIO_PCI_QUEUE_SEL:
- if (val < VIRTIO_PCI_QUEUE_MAX)
- vdev->queue_sel = val;
- break;
- case VIRTIO_PCI_QUEUE_NOTIFY:
- if (val < VIRTIO_PCI_QUEUE_MAX && vdev->vq[val].vring.desc)
- vdev->vq[val].handle_output(vdev, &vdev->vq[val]);
- break;
- case VIRTIO_PCI_STATUS:
- vdev->status = val & 0xFF;
- if (vdev->status == 0)
- virtio_reset(vdev);
- break;
- }
-}
-
-static uint32_t virtio_ioport_read(void *opaque, uint32_t addr)
-{
- VirtIODevice *vdev = to_virtio_device(opaque);
- uint32_t ret = 0xFFFFFFFF;
-
- addr -= vdev->addr;
-
- switch (addr) {
- case VIRTIO_PCI_HOST_FEATURES:
- ret = vdev->get_features(vdev);
- ret |= (1 << VIRTIO_F_NOTIFY_ON_EMPTY) | (1 << VIRTIO_F_BAD_FEATURE);
- break;
- case VIRTIO_PCI_GUEST_FEATURES:
- ret = vdev->features;
- break;
- case VIRTIO_PCI_QUEUE_PFN:
- ret = vdev->vq[vdev->queue_sel].pfn;
- break;
- case VIRTIO_PCI_QUEUE_NUM:
- ret = vdev->vq[vdev->queue_sel].vring.num;
- break;
- case VIRTIO_PCI_QUEUE_SEL:
- ret = vdev->queue_sel;
- break;
- case VIRTIO_PCI_STATUS:
- ret = vdev->status;
- break;
- case VIRTIO_PCI_ISR:
- /* reading from the ISR also clears it. */
- ret = vdev->isr;
- vdev->isr = 0;
- virtio_update_irq(vdev);
- break;
- default:
- break;
- }
-
- return ret;
-}
-
-static uint32_t virtio_config_readb(void *opaque, uint32_t addr)
+uint32_t virtio_config_readb(VirtIODevice *vdev, uint32_t addr)
{
- VirtIODevice *vdev = opaque;
uint8_t val;
vdev->get_config(vdev, vdev->config);
- addr -= vdev->addr + VIRTIO_PCI_CONFIG;
if (addr > (vdev->config_len - sizeof(val)))
return (uint32_t)-1;
@@ -542,14 +417,12 @@ static uint32_t virtio_config_readb(void *opaque, uint32_t addr)
return val;
}
-static uint32_t virtio_config_readw(void *opaque, uint32_t addr)
+uint32_t virtio_config_readw(VirtIODevice *vdev, uint32_t addr)
{
- VirtIODevice *vdev = opaque;
uint16_t val;
vdev->get_config(vdev, vdev->config);
- addr -= vdev->addr + VIRTIO_PCI_CONFIG;
if (addr > (vdev->config_len - sizeof(val)))
return (uint32_t)-1;
@@ -557,14 +430,12 @@ static uint32_t virtio_config_readw(void *opaque, uint32_t addr)
return val;
}
-static uint32_t virtio_config_readl(void *opaque, uint32_t addr)
+uint32_t virtio_config_readl(VirtIODevice *vdev, uint32_t addr)
{
- VirtIODevice *vdev = opaque;
uint32_t val;
vdev->get_config(vdev, vdev->config);
- addr -= vdev->addr + VIRTIO_PCI_CONFIG;
if (addr > (vdev->config_len - sizeof(val)))
return (uint32_t)-1;
@@ -572,12 +443,10 @@ static uint32_t virtio_config_readl(void *opaque, uint32_t addr)
return val;
}
-static void virtio_config_writeb(void *opaque, uint32_t addr, uint32_t data)
+void virtio_config_writeb(VirtIODevice *vdev, uint32_t addr, uint32_t data)
{
- VirtIODevice *vdev = opaque;
uint8_t val = data;
- addr -= vdev->addr + VIRTIO_PCI_CONFIG;
if (addr > (vdev->config_len - sizeof(val)))
return;
@@ -587,12 +456,10 @@ static void virtio_config_writeb(void *opaque, uint32_t addr, uint32_t data)
vdev->set_config(vdev, vdev->config);
}
-static void virtio_config_writew(void *opaque, uint32_t addr, uint32_t data)
+void virtio_config_writew(VirtIODevice *vdev, uint32_t addr, uint32_t data)
{
- VirtIODevice *vdev = opaque;
uint16_t val = data;
- addr -= vdev->addr + VIRTIO_PCI_CONFIG;
if (addr > (vdev->config_len - sizeof(val)))
return;
@@ -602,12 +469,10 @@ static void virtio_config_writew(void *opaque, uint32_t addr, uint32_t data)
vdev->set_config(vdev, vdev->config);
}
-static void virtio_config_writel(void *opaque, uint32_t addr, uint32_t data)
+void virtio_config_writel(VirtIODevice *vdev, uint32_t addr, uint32_t data)
{
- VirtIODevice *vdev = opaque;
uint32_t val = data;
- addr -= vdev->addr + VIRTIO_PCI_CONFIG;
if (addr > (vdev->config_len - sizeof(val)))
return;
@@ -617,33 +482,30 @@ static void virtio_config_writel(void *opaque, uint32_t addr, uint32_t data)
vdev->set_config(vdev, vdev->config);
}
-static void virtio_map(PCIDevice *pci_dev, int region_num,
- uint32_t addr, uint32_t size, int type)
+void virtio_queue_set_addr(VirtIODevice *vdev, int n, target_phys_addr_t addr)
{
- VirtIODevice *vdev = to_virtio_device(pci_dev);
- int i;
-
- vdev->addr = addr;
- for (i = 0; i < 3; i++) {
- register_ioport_write(addr, 20, 1 << i, virtio_ioport_write, vdev);
- register_ioport_read(addr, 20, 1 << i, virtio_ioport_read, vdev);
+ if (addr == 0) {
+ virtio_reset(vdev);
+ } else {
+ vdev->vq[n].pa = addr;
+ virtqueue_init(&vdev->vq[n]);
}
+}
+
+target_phys_addr_t virtio_queue_get_addr(VirtIODevice *vdev, int n)
+{
+ return vdev->vq[n].pa;
+}
+
+int virtio_queue_get_num(VirtIODevice *vdev, int n)
+{
+ return vdev->vq[n].vring.num;
+}
- if (vdev->config_len) {
- register_ioport_write(addr + 20, vdev->config_len, 1,
- virtio_config_writeb, vdev);
- register_ioport_write(addr + 20, vdev->config_len, 2,
- virtio_config_writew, vdev);
- register_ioport_write(addr + 20, vdev->config_len, 4,
- virtio_config_writel, vdev);
- register_ioport_read(addr + 20, vdev->config_len, 1,
- virtio_config_readb, vdev);
- register_ioport_read(addr + 20, vdev->config_len, 2,
- virtio_config_readw, vdev);
- register_ioport_read(addr + 20, vdev->config_len, 4,
- virtio_config_readl, vdev);
-
- vdev->get_config(vdev, vdev->config);
+void virtio_queue_notify(VirtIODevice *vdev, int n)
+{
+ if (n < VIRTIO_PCI_QUEUE_MAX && vdev->vq[n].vring.desc) {
+ vdev->vq[n].handle_output(vdev, &vdev->vq[n]);
}
}
@@ -691,9 +553,9 @@ void virtio_save(VirtIODevice *vdev, QEMUFile *f)
{
int i;
- pci_device_save(&vdev->pci_dev, f);
+ /* FIXME: load/save binding. */
+ //pci_device_save(&vdev->pci_dev, f);
- qemu_put_be32s(f, &vdev->addr);
qemu_put_8s(f, &vdev->status);
qemu_put_8s(f, &vdev->isr);
qemu_put_be16s(f, &vdev->queue_sel);
@@ -713,7 +575,7 @@ void virtio_save(VirtIODevice *vdev, QEMUFile *f)
break;
qemu_put_be32(f, vdev->vq[i].vring.num);
- qemu_put_be32s(f, &vdev->vq[i].pfn);
+ qemu_put_be64(f, vdev->vq[i].pa);
qemu_put_be16s(f, &vdev->vq[i].last_avail_idx);
}
}
@@ -722,9 +584,9 @@ void virtio_load(VirtIODevice *vdev, QEMUFile *f)
{
int num, i;
- pci_device_load(&vdev->pci_dev, f);
+ /* FIXME: load/save binding. */
+ //pci_device_load(&vdev->pci_dev, f);
- qemu_get_be32s(f, &vdev->addr);
qemu_get_8s(f, &vdev->status);
qemu_get_8s(f, &vdev->isr);
qemu_get_be16s(f, &vdev->queue_sel);
@@ -736,14 +598,11 @@ void virtio_load(VirtIODevice *vdev, QEMUFile *f)
for (i = 0; i < num; i++) {
vdev->vq[i].vring.num = qemu_get_be32(f);
- qemu_get_be32s(f, &vdev->vq[i].pfn);
+ vdev->vq[i].pa = qemu_get_be64(f);
qemu_get_be16s(f, &vdev->vq[i].last_avail_idx);
- if (vdev->vq[i].pfn) {
- target_phys_addr_t pa;
-
- pa = (ram_addr_t)vdev->vq[i].pfn << VIRTIO_PCI_QUEUE_ADDR_SHIFT;
- virtqueue_init(&vdev->vq[i], pa);
+ if (vdev->vq[i].pa) {
+ virtqueue_init(&vdev->vq[i]);
}
}
@@ -757,40 +616,19 @@ void virtio_cleanup(VirtIODevice *vdev)
qemu_free(vdev->vq);
}
-VirtIODevice *virtio_init_pci(PCIDevice *pci_dev, const char *name,
- uint16_t vendor, uint16_t device,
- uint16_t subvendor, uint16_t subdevice,
- uint16_t class_code, uint8_t pif,
- size_t config_size)
+VirtIODevice *virtio_common_init(const char *name, uint16_t device_id,
+ size_t config_size, size_t struct_size)
{
VirtIODevice *vdev;
- uint8_t *config;
- uint32_t size;
- vdev = to_virtio_device(pci_dev);
+ vdev = qemu_mallocz(struct_size);
+ vdev->device_id = device_id;
vdev->status = 0;
vdev->isr = 0;
vdev->queue_sel = 0;
vdev->vq = qemu_mallocz(sizeof(VirtQueue) * VIRTIO_PCI_QUEUE_MAX);
- config = pci_dev->config;
- pci_config_set_vendor_id(config, vendor);
- pci_config_set_device_id(config, device);
-
- config[0x08] = VIRTIO_PCI_ABI_VERSION;
-
- config[0x09] = pif;
- pci_config_set_class(config, class_code);
- config[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_NORMAL;
-
- config[0x2c] = subvendor & 0xFF;
- config[0x2d] = (subvendor >> 8) & 0xFF;
- config[0x2e] = subdevice & 0xFF;
- config[0x2f] = (subdevice >> 8) & 0xFF;
-
- config[0x3d] = 1;
-
vdev->name = name;
vdev->config_len = config_size;
if (vdev->config_len)
@@ -798,13 +636,13 @@ VirtIODevice *virtio_init_pci(PCIDevice *pci_dev, const char *name,
else
vdev->config = NULL;
- size = 20 + config_size;
- if (size & (size-1))
- size = 1 << qemu_fls(size);
-
- pci_register_io_region(pci_dev, 0, size, PCI_ADDRESS_SPACE_IO,
- virtio_map);
qemu_register_reset(virtio_reset, vdev);
-
return vdev;
}
+
+void virtio_bind_device(VirtIODevice *vdev, const VirtIOBindings *binding,
+ void *opaque)
+{
+ vdev->binding = binding;
+ vdev->binding_opaque = opaque;
+}