1b30d05adSPekka Enberg #include "kvm/blk-virtio.h" 2b30d05adSPekka Enberg 310eca11dSPekka Enberg #include "kvm/virtio_ring.h" 4c435b91dSPekka Enberg #include "kvm/virtio_blk.h" 5984b7ae0SPekka Enberg #include "kvm/virtio_pci.h" 6*5a24a9f2SPekka Enberg #include "kvm/disk-image.h" 7b30d05adSPekka Enberg #include "kvm/ioport.h" 8fe99fd4eSPekka Enberg #include "kvm/util.h" 98b1ff07eSPekka Enberg #include "kvm/kvm.h" 10b30d05adSPekka Enberg #include "kvm/pci.h" 11b30d05adSPekka Enberg 128b1ff07eSPekka Enberg #define VIRTIO_BLK_IRQ 14 13984b7ae0SPekka Enberg 1410eca11dSPekka Enberg #define NUM_VIRT_QUEUES 1 1510eca11dSPekka Enberg 1610eca11dSPekka Enberg #define VIRTIO_BLK_QUEUE_SIZE 16 1710eca11dSPekka Enberg 1810eca11dSPekka Enberg struct virt_queue { 1910eca11dSPekka Enberg struct vring vring; 20fe99fd4eSPekka Enberg uint32_t pfn; 21fe99fd4eSPekka Enberg /* The next_avail_ndx field is an index to ->ring of struct vring_avail. 22fe99fd4eSPekka Enberg It's where we assume the next request index is at. */ 23fe99fd4eSPekka Enberg uint16_t next_avail_ndx; 2410eca11dSPekka Enberg }; 2510eca11dSPekka Enberg 26fbc2fbf9SPekka Enberg struct device { 2740ce993fSPekka Enberg struct virtio_blk_config blk_config; 28c435b91dSPekka Enberg uint32_t host_features; 29fbc2fbf9SPekka Enberg uint32_t guest_features; 3040ce993fSPekka Enberg uint16_t config_vector; 31fbc2fbf9SPekka Enberg uint8_t status; 3247bf1d0fSPekka Enberg 3347bf1d0fSPekka Enberg /* virtio queue */ 3447bf1d0fSPekka Enberg uint16_t queue_selector; 3510eca11dSPekka Enberg 3610eca11dSPekka Enberg struct virt_queue virt_queues[NUM_VIRT_QUEUES]; 37fbc2fbf9SPekka Enberg }; 38fbc2fbf9SPekka Enberg 3940ce993fSPekka Enberg #define DISK_CYLINDERS 1024 4040ce993fSPekka Enberg #define DISK_HEADS 64 4140ce993fSPekka Enberg #define DISK_SECTORS 32 4240ce993fSPekka Enberg 43c435b91dSPekka Enberg static struct device device = { 4440ce993fSPekka Enberg .blk_config = (struct virtio_blk_config) { 4540ce993fSPekka Enberg .capacity = DISK_CYLINDERS * DISK_HEADS * DISK_SECTORS, 4640ce993fSPekka Enberg /* VIRTIO_BLK_F_GEOMETRY */ 4740ce993fSPekka Enberg .geometry = { 4840ce993fSPekka Enberg .cylinders = DISK_CYLINDERS, 4940ce993fSPekka Enberg .heads = DISK_HEADS, 5040ce993fSPekka Enberg .sectors = DISK_SECTORS, 5140ce993fSPekka Enberg }, 5240ce993fSPekka Enberg /* VIRTIO_BLK_SIZE */ 5340ce993fSPekka Enberg .blk_size = 4096, 5440ce993fSPekka Enberg }, 5598790f28SPekka Enberg .host_features = (1UL << VIRTIO_BLK_F_GEOMETRY) 56cb938db9SPekka Enberg | (1UL << VIRTIO_BLK_F_RO) 57c435b91dSPekka Enberg | (1UL << VIRTIO_BLK_F_BLK_SIZE), 58c435b91dSPekka Enberg }; 59fbc2fbf9SPekka Enberg 6040ce993fSPekka Enberg static bool virtio_blk_config_in(void *data, unsigned long offset, int size, uint32_t count) 6140ce993fSPekka Enberg { 6240ce993fSPekka Enberg uint8_t *config_space = (uint8_t *) &device.blk_config; 6340ce993fSPekka Enberg 6440ce993fSPekka Enberg if (size != 1 || count != 1) 6540ce993fSPekka Enberg return false; 6640ce993fSPekka Enberg 6740ce993fSPekka Enberg ioport__write8(data, config_space[offset - VIRTIO_PCI_CONFIG_NOMSI]); 6840ce993fSPekka Enberg 6940ce993fSPekka Enberg return true; 7040ce993fSPekka Enberg } 7140ce993fSPekka Enberg 72fbc2fbf9SPekka Enberg static bool blk_virtio_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count) 73fbc2fbf9SPekka Enberg { 74fbc2fbf9SPekka Enberg unsigned long offset; 75fbc2fbf9SPekka Enberg 76fbc2fbf9SPekka Enberg offset = port - IOPORT_VIRTIO; 77fbc2fbf9SPekka Enberg 78fbc2fbf9SPekka Enberg switch (offset) { 79fbc2fbf9SPekka Enberg case VIRTIO_PCI_HOST_FEATURES: 80c435b91dSPekka Enberg ioport__write32(data, device.host_features); 81fbc2fbf9SPekka Enberg break; 82fbc2fbf9SPekka Enberg case VIRTIO_PCI_GUEST_FEATURES: 838b1ff07eSPekka Enberg return false; 84fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_PFN: 8510eca11dSPekka Enberg ioport__write32(data, device.virt_queues[device.queue_selector].pfn); 868b1ff07eSPekka Enberg break; 87fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_NUM: 8810eca11dSPekka Enberg ioport__write16(data, VIRTIO_BLK_QUEUE_SIZE); 898b1ff07eSPekka Enberg break; 90fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_SEL: 91fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_NOTIFY: 92fbc2fbf9SPekka Enberg return false; 93fbc2fbf9SPekka Enberg case VIRTIO_PCI_STATUS: 94fbc2fbf9SPekka Enberg ioport__write8(data, device.status); 95fbc2fbf9SPekka Enberg break; 96fbc2fbf9SPekka Enberg case VIRTIO_PCI_ISR: 978b1ff07eSPekka Enberg ioport__write8(data, 0x1); 988b1ff07eSPekka Enberg kvm__irq_line(self, VIRTIO_BLK_IRQ, 0); 997e61688eSPekka Enberg break; 100fbc2fbf9SPekka Enberg case VIRTIO_MSI_CONFIG_VECTOR: 10140ce993fSPekka Enberg ioport__write16(data, device.config_vector); 10240ce993fSPekka Enberg break; 103fbc2fbf9SPekka Enberg default: 10440ce993fSPekka Enberg return virtio_blk_config_in(data, offset, size, count); 105fbc2fbf9SPekka Enberg }; 106fbc2fbf9SPekka Enberg 107fbc2fbf9SPekka Enberg return true; 108fbc2fbf9SPekka Enberg } 109fbc2fbf9SPekka Enberg 110fbc2fbf9SPekka Enberg static bool blk_virtio_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count) 111fbc2fbf9SPekka Enberg { 112fbc2fbf9SPekka Enberg unsigned long offset; 113fbc2fbf9SPekka Enberg 114fbc2fbf9SPekka Enberg offset = port - IOPORT_VIRTIO; 115fbc2fbf9SPekka Enberg 116fbc2fbf9SPekka Enberg switch (offset) { 117fbc2fbf9SPekka Enberg case VIRTIO_PCI_GUEST_FEATURES: 118fbc2fbf9SPekka Enberg device.guest_features = ioport__read32(data); 119fbc2fbf9SPekka Enberg break; 12010eca11dSPekka Enberg case VIRTIO_PCI_QUEUE_PFN: { 12110eca11dSPekka Enberg struct virt_queue *queue; 12210eca11dSPekka Enberg void *p; 12310eca11dSPekka Enberg 12410eca11dSPekka Enberg queue = &device.virt_queues[device.queue_selector]; 12510eca11dSPekka Enberg 12610eca11dSPekka Enberg queue->pfn = ioport__read32(data); 12710eca11dSPekka Enberg 12810eca11dSPekka Enberg p = guest_flat_to_host(self, queue->pfn << 12); 12910eca11dSPekka Enberg 13010eca11dSPekka Enberg vring_init(&queue->vring, VIRTIO_BLK_QUEUE_SIZE, p, 4096); 13110eca11dSPekka Enberg 1327e61688eSPekka Enberg break; 13310eca11dSPekka Enberg } 134fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_SEL: 13547bf1d0fSPekka Enberg device.queue_selector = ioport__read16(data); 1367e61688eSPekka Enberg break; 13710eca11dSPekka Enberg case VIRTIO_PCI_QUEUE_NOTIFY: { 138fe99fd4eSPekka Enberg struct virtio_blk_outhdr *req; 13910eca11dSPekka Enberg struct virt_queue *queue; 140fe99fd4eSPekka Enberg struct vring_desc *desc; 14110eca11dSPekka Enberg uint16_t queue_index; 142fe99fd4eSPekka Enberg uint16_t desc_ndx; 143*5a24a9f2SPekka Enberg uint32_t dst_len; 144*5a24a9f2SPekka Enberg uint8_t *status; 145*5a24a9f2SPekka Enberg void *dst; 14610eca11dSPekka Enberg 14710eca11dSPekka Enberg queue_index = ioport__read16(data); 14810eca11dSPekka Enberg 14910eca11dSPekka Enberg queue = &device.virt_queues[queue_index]; 15010eca11dSPekka Enberg 151fe99fd4eSPekka Enberg desc_ndx = queue->vring.avail->ring[queue->next_avail_ndx++ % queue->vring.num]; 152fe99fd4eSPekka Enberg 153fe99fd4eSPekka Enberg if (queue->vring.avail->idx != queue->next_avail_ndx) { 154fe99fd4eSPekka Enberg /* 155fe99fd4eSPekka Enberg * The hypervisor and the guest disagree on next index. 156fe99fd4eSPekka Enberg */ 157fe99fd4eSPekka Enberg warning("I/O error"); 158fe99fd4eSPekka Enberg break; 159fe99fd4eSPekka Enberg } 160fe99fd4eSPekka Enberg 161*5a24a9f2SPekka Enberg /* header */ 162fe99fd4eSPekka Enberg desc = &queue->vring.desc[desc_ndx]; 163fe99fd4eSPekka Enberg 164fe99fd4eSPekka Enberg req = guest_flat_to_host(self, desc->addr); 165fe99fd4eSPekka Enberg 166*5a24a9f2SPekka Enberg /* block */ 167*5a24a9f2SPekka Enberg desc = &queue->vring.desc[desc->next]; 168*5a24a9f2SPekka Enberg 169*5a24a9f2SPekka Enberg dst = guest_flat_to_host(self, desc->addr); 170*5a24a9f2SPekka Enberg dst_len = desc->len; 171*5a24a9f2SPekka Enberg 172*5a24a9f2SPekka Enberg /* status */ 173*5a24a9f2SPekka Enberg desc = &queue->vring.desc[desc->next]; 174*5a24a9f2SPekka Enberg 175*5a24a9f2SPekka Enberg status = guest_flat_to_host(self, desc->addr); 176*5a24a9f2SPekka Enberg 177*5a24a9f2SPekka Enberg if (self->disk_image) { 178*5a24a9f2SPekka Enberg int err; 179*5a24a9f2SPekka Enberg 180*5a24a9f2SPekka Enberg err = disk_image__read_sector(self->disk_image, req->sector, dst, dst_len); 181*5a24a9f2SPekka Enberg 182*5a24a9f2SPekka Enberg if (err) 183*5a24a9f2SPekka Enberg *status = VIRTIO_BLK_S_IOERR; 184*5a24a9f2SPekka Enberg else 185*5a24a9f2SPekka Enberg *status = VIRTIO_BLK_S_OK; 186*5a24a9f2SPekka Enberg } else 187*5a24a9f2SPekka Enberg *status = VIRTIO_BLK_S_IOERR; 188*5a24a9f2SPekka Enberg 189*5a24a9f2SPekka Enberg queue->vring.used->idx++; 190*5a24a9f2SPekka Enberg 1918b1ff07eSPekka Enberg kvm__irq_line(self, VIRTIO_BLK_IRQ, 1); 192*5a24a9f2SPekka Enberg 1937e61688eSPekka Enberg break; 19410eca11dSPekka Enberg } 195fbc2fbf9SPekka Enberg case VIRTIO_PCI_STATUS: 196fbc2fbf9SPekka Enberg device.status = ioport__read8(data); 197fbc2fbf9SPekka Enberg break; 198fbc2fbf9SPekka Enberg case VIRTIO_MSI_CONFIG_VECTOR: 19940ce993fSPekka Enberg device.config_vector = VIRTIO_MSI_NO_VECTOR; 20040ce993fSPekka Enberg break; 201fbc2fbf9SPekka Enberg case VIRTIO_MSI_QUEUE_VECTOR: 20240ce993fSPekka Enberg break; 203fbc2fbf9SPekka Enberg default: 204fbc2fbf9SPekka Enberg return false; 205fbc2fbf9SPekka Enberg }; 206fbc2fbf9SPekka Enberg 207fbc2fbf9SPekka Enberg return true; 208fbc2fbf9SPekka Enberg } 209fbc2fbf9SPekka Enberg 210fbc2fbf9SPekka Enberg static struct ioport_operations blk_virtio_io_ops = { 211fbc2fbf9SPekka Enberg .io_in = blk_virtio_in, 212fbc2fbf9SPekka Enberg .io_out = blk_virtio_out, 213fbc2fbf9SPekka Enberg }; 214fbc2fbf9SPekka Enberg 215b30d05adSPekka Enberg #define PCI_VENDOR_ID_REDHAT_QUMRANET 0x1af4 216b30d05adSPekka Enberg #define PCI_DEVICE_ID_VIRTIO_BLK 0x1001 217b30d05adSPekka Enberg #define PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET 0x1af4 218b30d05adSPekka Enberg #define PCI_SUBSYSTEM_ID_VIRTIO_BLK 0x0002 219b30d05adSPekka Enberg 220fbc2fbf9SPekka Enberg static struct pci_device_header blk_virtio_pci_device = { 221b30d05adSPekka Enberg .vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET, 222b30d05adSPekka Enberg .device_id = PCI_DEVICE_ID_VIRTIO_BLK, 223b30d05adSPekka Enberg .header_type = PCI_HEADER_TYPE_NORMAL, 224b30d05adSPekka Enberg .revision_id = 0, 225b30d05adSPekka Enberg .class = 0x010000, 226b30d05adSPekka Enberg .subsys_vendor_id = PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET, 227b30d05adSPekka Enberg .subsys_id = PCI_SUBSYSTEM_ID_VIRTIO_BLK, 228b30d05adSPekka Enberg .bar[0] = IOPORT_VIRTIO | PCI_BASE_ADDRESS_SPACE_IO, 229dc53a427SPekka Enberg .irq_pin = 1, 2308b1ff07eSPekka Enberg .irq_line = VIRTIO_BLK_IRQ, 231b30d05adSPekka Enberg }; 232b30d05adSPekka Enberg 233b30d05adSPekka Enberg void blk_virtio__init(void) 234b30d05adSPekka Enberg { 235fbc2fbf9SPekka Enberg pci__register(&blk_virtio_pci_device, 1); 236b30d05adSPekka Enberg 2378b1ff07eSPekka Enberg ioport__register(IOPORT_VIRTIO, &blk_virtio_io_ops, 256); 238b30d05adSPekka Enberg } 239