|  | /* | 
|  | * Virtio Support | 
|  | * | 
|  | * Copyright IBM, Corp. 2007 | 
|  | * | 
|  | * Authors: | 
|  | *  Anthony Liguori   <aliguori@us.ibm.com> | 
|  | * | 
|  | * This work is licensed under the terms of the GNU GPL, version 2.  See | 
|  | * the COPYING file in the top-level directory. | 
|  | * | 
|  | */ | 
|  |  | 
|  | #include <inttypes.h> | 
|  |  | 
|  | #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 | 
|  |  | 
|  | /* QEMU doesn't strictly need write barriers since everything runs in | 
|  | * lock-step.  We'll leave the calls to wmb() in though to make it obvious for | 
|  | * KVM or if kqemu gets SMP support. | 
|  | */ | 
|  | #define wmb() do { } while (0) | 
|  |  | 
|  | typedef struct VRingDesc | 
|  | { | 
|  | uint64_t addr; | 
|  | uint32_t len; | 
|  | uint16_t flags; | 
|  | uint16_t next; | 
|  | } VRingDesc; | 
|  |  | 
|  | typedef struct VRingAvail | 
|  | { | 
|  | uint16_t flags; | 
|  | uint16_t idx; | 
|  | uint16_t ring[0]; | 
|  | } VRingAvail; | 
|  |  | 
|  | typedef struct VRingUsedElem | 
|  | { | 
|  | uint32_t id; | 
|  | uint32_t len; | 
|  | } VRingUsedElem; | 
|  |  | 
|  | typedef struct VRingUsed | 
|  | { | 
|  | uint16_t flags; | 
|  | uint16_t idx; | 
|  | VRingUsedElem ring[0]; | 
|  | } VRingUsed; | 
|  |  | 
|  | typedef struct VRing | 
|  | { | 
|  | unsigned int num; | 
|  | target_phys_addr_t desc; | 
|  | target_phys_addr_t avail; | 
|  | target_phys_addr_t used; | 
|  | } VRing; | 
|  |  | 
|  | struct VirtQueue | 
|  | { | 
|  | VRing vring; | 
|  | uint32_t pfn; | 
|  | uint16_t last_avail_idx; | 
|  | int inuse; | 
|  | void (*handle_output)(VirtIODevice *vdev, VirtQueue *vq); | 
|  | }; | 
|  |  | 
|  | #define VIRTIO_PCI_QUEUE_MAX        16 | 
|  |  | 
|  | /* virt queue functions */ | 
|  | static void virtqueue_init(VirtQueue *vq, target_phys_addr_t pa) | 
|  | { | 
|  | vq->vring.desc = pa; | 
|  | vq->vring.avail = pa + vq->vring.num * sizeof(VRingDesc); | 
|  | vq->vring.used = vring_align(vq->vring.avail + | 
|  | offsetof(VRingAvail, ring[vq->vring.num]), | 
|  | VIRTIO_PCI_VRING_ALIGN); | 
|  | } | 
|  |  | 
|  | static inline uint64_t vring_desc_addr(VirtQueue *vq, int i) | 
|  | { | 
|  | target_phys_addr_t pa; | 
|  | pa = vq->vring.desc + sizeof(VRingDesc) * i + offsetof(VRingDesc, addr); | 
|  | return ldq_phys(pa); | 
|  | } | 
|  |  | 
|  | static inline uint32_t vring_desc_len(VirtQueue *vq, int i) | 
|  | { | 
|  | target_phys_addr_t pa; | 
|  | pa = vq->vring.desc + sizeof(VRingDesc) * i + offsetof(VRingDesc, len); | 
|  | return ldl_phys(pa); | 
|  | } | 
|  |  | 
|  | static inline uint16_t vring_desc_flags(VirtQueue *vq, int i) | 
|  | { | 
|  | target_phys_addr_t pa; | 
|  | pa = vq->vring.desc + sizeof(VRingDesc) * i + offsetof(VRingDesc, flags); | 
|  | return lduw_phys(pa); | 
|  | } | 
|  |  | 
|  | static inline uint16_t vring_desc_next(VirtQueue *vq, int i) | 
|  | { | 
|  | target_phys_addr_t pa; | 
|  | pa = vq->vring.desc + sizeof(VRingDesc) * i + offsetof(VRingDesc, next); | 
|  | return lduw_phys(pa); | 
|  | } | 
|  |  | 
|  | static inline uint16_t vring_avail_flags(VirtQueue *vq) | 
|  | { | 
|  | target_phys_addr_t pa; | 
|  | pa = vq->vring.avail + offsetof(VRingAvail, flags); | 
|  | return lduw_phys(pa); | 
|  | } | 
|  |  | 
|  | static inline uint16_t vring_avail_idx(VirtQueue *vq) | 
|  | { | 
|  | target_phys_addr_t pa; | 
|  | pa = vq->vring.avail + offsetof(VRingAvail, idx); | 
|  | return lduw_phys(pa); | 
|  | } | 
|  |  | 
|  | static inline uint16_t vring_avail_ring(VirtQueue *vq, int i) | 
|  | { | 
|  | target_phys_addr_t pa; | 
|  | pa = vq->vring.avail + offsetof(VRingAvail, ring[i]); | 
|  | return lduw_phys(pa); | 
|  | } | 
|  |  | 
|  | static inline void vring_used_ring_id(VirtQueue *vq, int i, uint32_t val) | 
|  | { | 
|  | target_phys_addr_t pa; | 
|  | pa = vq->vring.used + offsetof(VRingUsed, ring[i].id); | 
|  | stl_phys(pa, val); | 
|  | } | 
|  |  | 
|  | static inline void vring_used_ring_len(VirtQueue *vq, int i, uint32_t val) | 
|  | { | 
|  | target_phys_addr_t pa; | 
|  | pa = vq->vring.used + offsetof(VRingUsed, ring[i].len); | 
|  | stl_phys(pa, val); | 
|  | } | 
|  |  | 
|  | static uint16_t vring_used_idx(VirtQueue *vq) | 
|  | { | 
|  | target_phys_addr_t pa; | 
|  | pa = vq->vring.used + offsetof(VRingUsed, idx); | 
|  | return lduw_phys(pa); | 
|  | } | 
|  |  | 
|  | static inline void vring_used_idx_increment(VirtQueue *vq, uint16_t val) | 
|  | { | 
|  | target_phys_addr_t pa; | 
|  | pa = vq->vring.used + offsetof(VRingUsed, idx); | 
|  | stw_phys(pa, vring_used_idx(vq) + val); | 
|  | } | 
|  |  | 
|  | static inline void vring_used_flags_set_bit(VirtQueue *vq, int mask) | 
|  | { | 
|  | target_phys_addr_t pa; | 
|  | pa = vq->vring.used + offsetof(VRingUsed, flags); | 
|  | stw_phys(pa, lduw_phys(pa) | mask); | 
|  | } | 
|  |  | 
|  | static inline void vring_used_flags_unset_bit(VirtQueue *vq, int mask) | 
|  | { | 
|  | target_phys_addr_t pa; | 
|  | pa = vq->vring.used + offsetof(VRingUsed, flags); | 
|  | stw_phys(pa, lduw_phys(pa) & ~mask); | 
|  | } | 
|  |  | 
|  | void virtio_queue_set_notification(VirtQueue *vq, int enable) | 
|  | { | 
|  | if (enable) | 
|  | vring_used_flags_unset_bit(vq, VRING_USED_F_NO_NOTIFY); | 
|  | else | 
|  | vring_used_flags_set_bit(vq, VRING_USED_F_NO_NOTIFY); | 
|  | } | 
|  |  | 
|  | int virtio_queue_ready(VirtQueue *vq) | 
|  | { | 
|  | return vq->vring.avail != 0; | 
|  | } | 
|  |  | 
|  | int virtio_queue_empty(VirtQueue *vq) | 
|  | { | 
|  | return vring_avail_idx(vq) == vq->last_avail_idx; | 
|  | } | 
|  |  | 
|  | void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem, | 
|  | unsigned int len, unsigned int idx) | 
|  | { | 
|  | unsigned int offset; | 
|  | int i; | 
|  |  | 
|  | offset = 0; | 
|  | for (i = 0; i < elem->in_num; i++) { | 
|  | size_t size = MIN(len - offset, elem->in_sg[i].iov_len); | 
|  |  | 
|  | cpu_physical_memory_unmap(elem->in_sg[i].iov_base, | 
|  | elem->in_sg[i].iov_len, | 
|  | 1, size); | 
|  |  | 
|  | offset += elem->in_sg[i].iov_len; | 
|  | } | 
|  |  | 
|  | for (i = 0; i < elem->out_num; i++) | 
|  | cpu_physical_memory_unmap(elem->out_sg[i].iov_base, | 
|  | elem->out_sg[i].iov_len, | 
|  | 0, elem->out_sg[i].iov_len); | 
|  |  | 
|  | idx = (idx + vring_used_idx(vq)) % vq->vring.num; | 
|  |  | 
|  | /* Get a pointer to the next entry in the used ring. */ | 
|  | vring_used_ring_id(vq, idx, elem->index); | 
|  | vring_used_ring_len(vq, idx, len); | 
|  | } | 
|  |  | 
|  | void virtqueue_flush(VirtQueue *vq, unsigned int count) | 
|  | { | 
|  | /* Make sure buffer is written before we update index. */ | 
|  | wmb(); | 
|  | vring_used_idx_increment(vq, count); | 
|  | vq->inuse -= count; | 
|  | } | 
|  |  | 
|  | void virtqueue_push(VirtQueue *vq, const VirtQueueElement *elem, | 
|  | unsigned int len) | 
|  | { | 
|  | virtqueue_fill(vq, elem, len, 0); | 
|  | virtqueue_flush(vq, 1); | 
|  | } | 
|  |  | 
|  | static int virtqueue_num_heads(VirtQueue *vq, unsigned int idx) | 
|  | { | 
|  | uint16_t num_heads = vring_avail_idx(vq) - idx; | 
|  |  | 
|  | /* Check it isn't doing very strange things with descriptor numbers. */ | 
|  | if (num_heads > vq->vring.num) { | 
|  | fprintf(stderr, "Guest moved used index from %u to %u", | 
|  | idx, vring_avail_idx(vq)); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | return num_heads; | 
|  | } | 
|  |  | 
|  | static unsigned int virtqueue_get_head(VirtQueue *vq, unsigned int idx) | 
|  | { | 
|  | unsigned int head; | 
|  |  | 
|  | /* Grab the next descriptor number they're advertising, and increment | 
|  | * the index we've seen. */ | 
|  | head = vring_avail_ring(vq, idx % vq->vring.num); | 
|  |  | 
|  | /* If their number is silly, that's a fatal mistake. */ | 
|  | if (head >= vq->vring.num) { | 
|  | fprintf(stderr, "Guest says index %u is available", head); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | return head; | 
|  | } | 
|  |  | 
|  | static unsigned virtqueue_next_desc(VirtQueue *vq, unsigned int i) | 
|  | { | 
|  | unsigned int next; | 
|  |  | 
|  | /* If this descriptor says it doesn't chain, we're done. */ | 
|  | if (!(vring_desc_flags(vq, i) & VRING_DESC_F_NEXT)) | 
|  | return vq->vring.num; | 
|  |  | 
|  | /* Check they're not leading us off end of descriptors. */ | 
|  | next = vring_desc_next(vq, i); | 
|  | /* Make sure compiler knows to grab that: we don't want it changing! */ | 
|  | wmb(); | 
|  |  | 
|  | if (next >= vq->vring.num) { | 
|  | fprintf(stderr, "Desc next is %u", next); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | return next; | 
|  | } | 
|  |  | 
|  | int virtqueue_avail_bytes(VirtQueue *vq, int in_bytes, int out_bytes) | 
|  | { | 
|  | unsigned int idx; | 
|  | int num_bufs, in_total, out_total; | 
|  |  | 
|  | idx = vq->last_avail_idx; | 
|  |  | 
|  | num_bufs = in_total = out_total = 0; | 
|  | while (virtqueue_num_heads(vq, idx)) { | 
|  | int i; | 
|  |  | 
|  | i = virtqueue_get_head(vq, idx++); | 
|  | do { | 
|  | /* If we've got too many, that implies a descriptor loop. */ | 
|  | if (++num_bufs > vq->vring.num) { | 
|  | fprintf(stderr, "Looped descriptor"); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | if (vring_desc_flags(vq, i) & VRING_DESC_F_WRITE) { | 
|  | if (in_bytes > 0 && | 
|  | (in_total += vring_desc_len(vq, i)) >= in_bytes) | 
|  | return 1; | 
|  | } else { | 
|  | if (out_bytes > 0 && | 
|  | (out_total += vring_desc_len(vq, i)) >= out_bytes) | 
|  | return 1; | 
|  | } | 
|  | } while ((i = virtqueue_next_desc(vq, i)) != vq->vring.num); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem) | 
|  | { | 
|  | unsigned int i, head; | 
|  | target_phys_addr_t len; | 
|  |  | 
|  | if (!virtqueue_num_heads(vq, vq->last_avail_idx)) | 
|  | return 0; | 
|  |  | 
|  | /* When we start there are none of either input nor output. */ | 
|  | elem->out_num = elem->in_num = 0; | 
|  |  | 
|  | i = head = virtqueue_get_head(vq, vq->last_avail_idx++); | 
|  | do { | 
|  | struct iovec *sg; | 
|  | int is_write = 0; | 
|  |  | 
|  | if (vring_desc_flags(vq, i) & VRING_DESC_F_WRITE) { | 
|  | elem->in_addr[elem->in_num] = vring_desc_addr(vq, i); | 
|  | sg = &elem->in_sg[elem->in_num++]; | 
|  | is_write = 1; | 
|  | } else | 
|  | sg = &elem->out_sg[elem->out_num++]; | 
|  |  | 
|  | /* Grab the first descriptor, and check it's OK. */ | 
|  | sg->iov_len = vring_desc_len(vq, i); | 
|  | len = sg->iov_len; | 
|  |  | 
|  | sg->iov_base = cpu_physical_memory_map(vring_desc_addr(vq, i), &len, is_write); | 
|  |  | 
|  | if (sg->iov_base == NULL || len != sg->iov_len) { | 
|  | fprintf(stderr, "virtio: trying to map MMIO memory\n"); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | /* If we've got too many, that implies a descriptor loop. */ | 
|  | if ((elem->in_num + elem->out_num) > vq->vring.num) { | 
|  | fprintf(stderr, "Looped descriptor"); | 
|  | exit(1); | 
|  | } | 
|  | } while ((i = virtqueue_next_desc(vq, i)) != vq->vring.num); | 
|  |  | 
|  | elem->index = head; | 
|  |  | 
|  | vq->inuse++; | 
|  |  | 
|  | return elem->in_num + elem->out_num; | 
|  | } | 
|  |  | 
|  | /* virtio device */ | 
|  |  | 
|  | static VirtIODevice *to_virtio_device(PCIDevice *pci_dev) | 
|  | { | 
|  | return (VirtIODevice *)pci_dev; | 
|  | } | 
|  |  | 
|  | static void virtio_update_irq(VirtIODevice *vdev) | 
|  | { | 
|  | qemu_set_irq(vdev->pci_dev.irq[0], vdev->isr & 1); | 
|  | } | 
|  |  | 
|  | static void virtio_reset(void *opaque) | 
|  | { | 
|  | VirtIODevice *vdev = opaque; | 
|  | int i; | 
|  |  | 
|  | if (vdev->reset) | 
|  | vdev->reset(vdev); | 
|  |  | 
|  | vdev->features = 0; | 
|  | vdev->queue_sel = 0; | 
|  | vdev->status = 0; | 
|  | vdev->isr = 0; | 
|  | virtio_update_irq(vdev); | 
|  |  | 
|  | for(i = 0; i < VIRTIO_PCI_QUEUE_MAX; i++) { | 
|  | vdev->vq[i].vring.desc = 0; | 
|  | vdev->vq[i].vring.avail = 0; | 
|  | vdev->vq[i].vring.used = 0; | 
|  | vdev->vq[i].last_avail_idx = 0; | 
|  | vdev->vq[i].pfn = 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) | 
|  | { | 
|  | 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; | 
|  |  | 
|  | memcpy(&val, vdev->config + addr, sizeof(val)); | 
|  | return val; | 
|  | } | 
|  |  | 
|  | static uint32_t virtio_config_readw(void *opaque, 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; | 
|  |  | 
|  | memcpy(&val, vdev->config + addr, sizeof(val)); | 
|  | return val; | 
|  | } | 
|  |  | 
|  | static uint32_t virtio_config_readl(void *opaque, 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; | 
|  |  | 
|  | memcpy(&val, vdev->config + addr, sizeof(val)); | 
|  | return val; | 
|  | } | 
|  |  | 
|  | static void virtio_config_writeb(void *opaque, 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; | 
|  |  | 
|  | memcpy(vdev->config + addr, &val, sizeof(val)); | 
|  |  | 
|  | if (vdev->set_config) | 
|  | vdev->set_config(vdev, vdev->config); | 
|  | } | 
|  |  | 
|  | static void virtio_config_writew(void *opaque, 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; | 
|  |  | 
|  | memcpy(vdev->config + addr, &val, sizeof(val)); | 
|  |  | 
|  | if (vdev->set_config) | 
|  | vdev->set_config(vdev, vdev->config); | 
|  | } | 
|  |  | 
|  | static void virtio_config_writel(void *opaque, 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; | 
|  |  | 
|  | memcpy(vdev->config + addr, &val, sizeof(val)); | 
|  |  | 
|  | if (vdev->set_config) | 
|  | vdev->set_config(vdev, vdev->config); | 
|  | } | 
|  |  | 
|  | static void virtio_map(PCIDevice *pci_dev, int region_num, | 
|  | uint32_t addr, uint32_t size, int type) | 
|  | { | 
|  | 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 (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); | 
|  | } | 
|  | } | 
|  |  | 
|  | VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_size, | 
|  | void (*handle_output)(VirtIODevice *, VirtQueue *)) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | for (i = 0; i < VIRTIO_PCI_QUEUE_MAX; i++) { | 
|  | if (vdev->vq[i].vring.num == 0) | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (i == VIRTIO_PCI_QUEUE_MAX || queue_size > VIRTQUEUE_MAX_SIZE) | 
|  | abort(); | 
|  |  | 
|  | vdev->vq[i].vring.num = queue_size; | 
|  | vdev->vq[i].handle_output = handle_output; | 
|  |  | 
|  | return &vdev->vq[i]; | 
|  | } | 
|  |  | 
|  | void virtio_notify(VirtIODevice *vdev, VirtQueue *vq) | 
|  | { | 
|  | /* Always notify when queue is empty (when feature acknowledge) */ | 
|  | if ((vring_avail_flags(vq) & VRING_AVAIL_F_NO_INTERRUPT) && | 
|  | (!(vdev->features & (1 << VIRTIO_F_NOTIFY_ON_EMPTY)) || | 
|  | (vq->inuse || vring_avail_idx(vq) != vq->last_avail_idx))) | 
|  | return; | 
|  |  | 
|  | vdev->isr |= 0x01; | 
|  | virtio_update_irq(vdev); | 
|  | } | 
|  |  | 
|  | void virtio_notify_config(VirtIODevice *vdev) | 
|  | { | 
|  | if (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) | 
|  | return; | 
|  |  | 
|  | vdev->isr |= 0x03; | 
|  | virtio_update_irq(vdev); | 
|  | } | 
|  |  | 
|  | void virtio_save(VirtIODevice *vdev, QEMUFile *f) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | 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); | 
|  | qemu_put_be32s(f, &vdev->features); | 
|  | qemu_put_be32(f, vdev->config_len); | 
|  | qemu_put_buffer(f, vdev->config, vdev->config_len); | 
|  |  | 
|  | for (i = 0; i < VIRTIO_PCI_QUEUE_MAX; i++) { | 
|  | if (vdev->vq[i].vring.num == 0) | 
|  | break; | 
|  | } | 
|  |  | 
|  | qemu_put_be32(f, i); | 
|  |  | 
|  | for (i = 0; i < VIRTIO_PCI_QUEUE_MAX; i++) { | 
|  | if (vdev->vq[i].vring.num == 0) | 
|  | break; | 
|  |  | 
|  | qemu_put_be32(f, vdev->vq[i].vring.num); | 
|  | qemu_put_be32s(f, &vdev->vq[i].pfn); | 
|  | qemu_put_be16s(f, &vdev->vq[i].last_avail_idx); | 
|  | } | 
|  | } | 
|  |  | 
|  | void virtio_load(VirtIODevice *vdev, QEMUFile *f) | 
|  | { | 
|  | int num, i; | 
|  |  | 
|  | 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); | 
|  | qemu_get_be32s(f, &vdev->features); | 
|  | vdev->config_len = qemu_get_be32(f); | 
|  | qemu_get_buffer(f, vdev->config, vdev->config_len); | 
|  |  | 
|  | num = qemu_get_be32(f); | 
|  |  | 
|  | for (i = 0; i < num; i++) { | 
|  | vdev->vq[i].vring.num = qemu_get_be32(f); | 
|  | qemu_get_be32s(f, &vdev->vq[i].pfn); | 
|  | 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); | 
|  | } | 
|  | } | 
|  |  | 
|  | virtio_update_irq(vdev); | 
|  | } | 
|  |  | 
|  | VirtIODevice *virtio_init_pci(PCIBus *bus, 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, size_t struct_size) | 
|  | { | 
|  | VirtIODevice *vdev; | 
|  | PCIDevice *pci_dev; | 
|  | uint8_t *config; | 
|  | uint32_t size; | 
|  |  | 
|  | pci_dev = pci_register_device(bus, name, struct_size, | 
|  | -1, NULL, NULL); | 
|  | if (!pci_dev) | 
|  | return NULL; | 
|  |  | 
|  | vdev = to_virtio_device(pci_dev); | 
|  |  | 
|  | 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[0x0e] = 0x00; | 
|  |  | 
|  | 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) | 
|  | vdev->config = qemu_mallocz(config_size); | 
|  | 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; | 
|  | } |