1416b2c2dSAsias He #include "kvm/virtio-blk.h" 2b30d05adSPekka Enberg 3416b2c2dSAsias He #include "kvm/virtio-pci.h" 476b6349fSPekka Enberg 55a24a9f2SPekka Enberg #include "kvm/disk-image.h" 639d6af07SAsias He #include "kvm/virtio.h" 7b30d05adSPekka Enberg #include "kvm/ioport.h" 84ef0f4d6SPekka Enberg #include "kvm/mutex.h" 9fe99fd4eSPekka Enberg #include "kvm/util.h" 108b1ff07eSPekka Enberg #include "kvm/kvm.h" 11b30d05adSPekka Enberg #include "kvm/pci.h" 12*fb0957f2SSasha Levin #include "kvm/threadpool.h" 13b30d05adSPekka Enberg 1420c64ecaSPekka Enberg #include <linux/virtio_ring.h> 1520c64ecaSPekka Enberg #include <linux/virtio_blk.h> 160528c2a7SPekka Enberg 174155ba8cSPekka Enberg #include <inttypes.h> 180528c2a7SPekka Enberg #include <pthread.h> 194155ba8cSPekka Enberg 20bc0363c8SCyrill Gorcunov #define VIRTIO_BLK_IRQ 15 21bc0363c8SCyrill Gorcunov #define VIRTIO_BLK_PIN 1 22984b7ae0SPekka Enberg 2310eca11dSPekka Enberg #define NUM_VIRT_QUEUES 1 2410eca11dSPekka Enberg 2503110ff3SAsias He #define VIRTIO_BLK_QUEUE_SIZE 128 2610eca11dSPekka Enberg 27802e07a0SAsias He struct blk_device { 280528c2a7SPekka Enberg pthread_mutex_t mutex; 290528c2a7SPekka Enberg 3040ce993fSPekka Enberg struct virtio_blk_config blk_config; 31c435b91dSPekka Enberg uint32_t host_features; 32fbc2fbf9SPekka Enberg uint32_t guest_features; 3340ce993fSPekka Enberg uint16_t config_vector; 34fbc2fbf9SPekka Enberg uint8_t status; 3547bf1d0fSPekka Enberg 3647bf1d0fSPekka Enberg /* virtio queue */ 3747bf1d0fSPekka Enberg uint16_t queue_selector; 3810eca11dSPekka Enberg 3945e47970SAsias He struct virt_queue vqs[NUM_VIRT_QUEUES]; 40*fb0957f2SSasha Levin 41*fb0957f2SSasha Levin void *jobs[NUM_VIRT_QUEUES]; 42fbc2fbf9SPekka Enberg }; 43fbc2fbf9SPekka Enberg 4403110ff3SAsias He #define DISK_SEG_MAX 126 4540ce993fSPekka Enberg 46802e07a0SAsias He static struct blk_device blk_device = { 470528c2a7SPekka Enberg .mutex = PTHREAD_MUTEX_INITIALIZER, 480528c2a7SPekka Enberg 4940ce993fSPekka Enberg .blk_config = (struct virtio_blk_config) { 50e199f440SPekka Enberg /* VIRTIO_BLK_F_SEG_MAX */ 5103110ff3SAsias He .seg_max = DISK_SEG_MAX, 5240ce993fSPekka Enberg }, 531ef2738dSCyrill Gorcunov /* 541ef2738dSCyrill Gorcunov * Note we don't set VIRTIO_BLK_F_GEOMETRY here so the 551ef2738dSCyrill Gorcunov * node kernel will compute disk geometry by own, the 561ef2738dSCyrill Gorcunov * same applies to VIRTIO_BLK_F_BLK_SIZE 571ef2738dSCyrill Gorcunov */ 5803110ff3SAsias He .host_features = (1UL << VIRTIO_BLK_F_SEG_MAX), 59c435b91dSPekka Enberg }; 60fbc2fbf9SPekka Enberg 61416b2c2dSAsias He static bool virtio_blk_pci_io_device_specific_in(void *data, unsigned long offset, int size, uint32_t count) 6240ce993fSPekka Enberg { 63802e07a0SAsias He uint8_t *config_space = (uint8_t *) &blk_device.blk_config; 6440ce993fSPekka Enberg 6540ce993fSPekka Enberg if (size != 1 || count != 1) 6640ce993fSPekka Enberg return false; 6740ce993fSPekka Enberg 6840ce993fSPekka Enberg ioport__write8(data, config_space[offset - VIRTIO_PCI_CONFIG_NOMSI]); 6940ce993fSPekka Enberg 7040ce993fSPekka Enberg return true; 7140ce993fSPekka Enberg } 7240ce993fSPekka Enberg 73416b2c2dSAsias He static bool virtio_blk_pci_io_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count) 74fbc2fbf9SPekka Enberg { 75fbc2fbf9SPekka Enberg unsigned long offset; 760528c2a7SPekka Enberg bool ret = true; 770528c2a7SPekka Enberg 784ef0f4d6SPekka Enberg mutex_lock(&blk_device.mutex); 79fbc2fbf9SPekka Enberg 80d1888318SCyrill Gorcunov offset = port - IOPORT_VIRTIO_BLK; 81fbc2fbf9SPekka Enberg 82fbc2fbf9SPekka Enberg switch (offset) { 83fbc2fbf9SPekka Enberg case VIRTIO_PCI_HOST_FEATURES: 84802e07a0SAsias He ioport__write32(data, blk_device.host_features); 85fbc2fbf9SPekka Enberg break; 86fbc2fbf9SPekka Enberg case VIRTIO_PCI_GUEST_FEATURES: 870528c2a7SPekka Enberg ret = false; 889ee67e60SAsias He break; 89fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_PFN: 9045e47970SAsias He ioport__write32(data, blk_device.vqs[blk_device.queue_selector].pfn); 918b1ff07eSPekka Enberg break; 92fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_NUM: 9310eca11dSPekka Enberg ioport__write16(data, VIRTIO_BLK_QUEUE_SIZE); 948b1ff07eSPekka Enberg break; 95fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_SEL: 96fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_NOTIFY: 970528c2a7SPekka Enberg ret = false; 989ee67e60SAsias He break; 99fbc2fbf9SPekka Enberg case VIRTIO_PCI_STATUS: 100802e07a0SAsias He ioport__write8(data, blk_device.status); 101fbc2fbf9SPekka Enberg break; 102fbc2fbf9SPekka Enberg case VIRTIO_PCI_ISR: 1038b1ff07eSPekka Enberg ioport__write8(data, 0x1); 1048b1ff07eSPekka Enberg kvm__irq_line(self, VIRTIO_BLK_IRQ, 0); 1057e61688eSPekka Enberg break; 106fbc2fbf9SPekka Enberg case VIRTIO_MSI_CONFIG_VECTOR: 107802e07a0SAsias He ioport__write16(data, blk_device.config_vector); 10840ce993fSPekka Enberg break; 109fbc2fbf9SPekka Enberg default: 1100528c2a7SPekka Enberg ret = virtio_blk_pci_io_device_specific_in(data, offset, size, count); 111fbc2fbf9SPekka Enberg }; 112fbc2fbf9SPekka Enberg 1134ef0f4d6SPekka Enberg mutex_unlock(&blk_device.mutex); 1140528c2a7SPekka Enberg 1150528c2a7SPekka Enberg return ret; 116fbc2fbf9SPekka Enberg } 117fbc2fbf9SPekka Enberg 11845e47970SAsias He static bool virtio_blk_do_io_request(struct kvm *self, struct virt_queue *queue) 1194155ba8cSPekka Enberg { 12045e47970SAsias He struct iovec iov[VIRTIO_BLK_QUEUE_SIZE]; 1214155ba8cSPekka Enberg struct virtio_blk_outhdr *req; 12270b53f25SSasha Levin ssize_t block_cnt = -1; 12345e47970SAsias He uint16_t out, in, head; 1244155ba8cSPekka Enberg uint8_t *status; 1254155ba8cSPekka Enberg 12645e47970SAsias He head = virt_queue__get_iov(queue, iov, &out, &in, self); 1274155ba8cSPekka Enberg 12845e47970SAsias He /* head */ 12945e47970SAsias He req = iov[0].iov_base; 13003110ff3SAsias He 131258dd093SPekka Enberg switch (req->type) { 13203110ff3SAsias He case VIRTIO_BLK_T_IN: 1332d103098SSasha Levin block_cnt = disk_image__read_sector_iov(self->disk_image, req->sector, iov + 1, in + out - 2); 13470b53f25SSasha Levin 135258dd093SPekka Enberg break; 13603110ff3SAsias He case VIRTIO_BLK_T_OUT: 1372d103098SSasha Levin block_cnt = disk_image__write_sector_iov(self->disk_image, req->sector, iov + 1, in + out - 2); 13870b53f25SSasha Levin 139258dd093SPekka Enberg break; 14070b53f25SSasha Levin 141258dd093SPekka Enberg default: 1424155ba8cSPekka Enberg warning("request type %d", req->type); 14370b53f25SSasha Levin block_cnt = -1; 14403110ff3SAsias He } 14503110ff3SAsias He 14645e47970SAsias He /* status */ 14745e47970SAsias He status = iov[out + in - 1].iov_base; 14870b53f25SSasha Levin *status = (block_cnt < 0) ? VIRTIO_BLK_S_IOERR : VIRTIO_BLK_S_OK; 14903110ff3SAsias He 15045e47970SAsias He virt_queue__set_used_elem(queue, head, block_cnt); 1514155ba8cSPekka Enberg 1524155ba8cSPekka Enberg return true; 1534155ba8cSPekka Enberg } 1544155ba8cSPekka Enberg 155*fb0957f2SSasha Levin static void virtio_blk_do_io(struct kvm *kvm, void *param) 15645e47970SAsias He { 157*fb0957f2SSasha Levin struct virt_queue *vq = param; 15845e47970SAsias He 1590ea58e5bSPekka Enberg while (virt_queue__available(vq)) 1604baf6f73SSasha Levin virtio_blk_do_io_request(kvm, vq); 1610ea58e5bSPekka Enberg 1624baf6f73SSasha Levin kvm__irq_line(kvm, VIRTIO_BLK_IRQ, 1); 1634baf6f73SSasha Levin } 1640528c2a7SPekka Enberg 165416b2c2dSAsias He static bool virtio_blk_pci_io_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count) 166fbc2fbf9SPekka Enberg { 167fbc2fbf9SPekka Enberg unsigned long offset; 1680528c2a7SPekka Enberg bool ret = true; 1690528c2a7SPekka Enberg 1704ef0f4d6SPekka Enberg mutex_lock(&blk_device.mutex); 171fbc2fbf9SPekka Enberg 172d1888318SCyrill Gorcunov offset = port - IOPORT_VIRTIO_BLK; 173fbc2fbf9SPekka Enberg 174fbc2fbf9SPekka Enberg switch (offset) { 175fbc2fbf9SPekka Enberg case VIRTIO_PCI_GUEST_FEATURES: 176802e07a0SAsias He blk_device.guest_features = ioport__read32(data); 177fbc2fbf9SPekka Enberg break; 17810eca11dSPekka Enberg case VIRTIO_PCI_QUEUE_PFN: { 17910eca11dSPekka Enberg struct virt_queue *queue; 18010eca11dSPekka Enberg void *p; 18110eca11dSPekka Enberg 18245e47970SAsias He queue = &blk_device.vqs[blk_device.queue_selector]; 18310eca11dSPekka Enberg 18410eca11dSPekka Enberg queue->pfn = ioport__read32(data); 18510eca11dSPekka Enberg 18610eca11dSPekka Enberg p = guest_flat_to_host(self, queue->pfn << 12); 18710eca11dSPekka Enberg 18810eca11dSPekka Enberg vring_init(&queue->vring, VIRTIO_BLK_QUEUE_SIZE, p, 4096); 18910eca11dSPekka Enberg 190*fb0957f2SSasha Levin blk_device.jobs[blk_device.queue_selector] = 191*fb0957f2SSasha Levin thread_pool__add_jobtype(self, virtio_blk_do_io, queue); 192*fb0957f2SSasha Levin 1937e61688eSPekka Enberg break; 19410eca11dSPekka Enberg } 195fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_SEL: 196802e07a0SAsias He blk_device.queue_selector = ioport__read16(data); 1977e61688eSPekka Enberg break; 19810eca11dSPekka Enberg case VIRTIO_PCI_QUEUE_NOTIFY: { 19910eca11dSPekka Enberg uint16_t queue_index; 20010eca11dSPekka Enberg queue_index = ioport__read16(data); 201*fb0957f2SSasha Levin thread_pool__signal_work(blk_device.jobs[queue_index]); 2027e61688eSPekka Enberg break; 20310eca11dSPekka Enberg } 204fbc2fbf9SPekka Enberg case VIRTIO_PCI_STATUS: 205802e07a0SAsias He blk_device.status = ioport__read8(data); 206fbc2fbf9SPekka Enberg break; 207fbc2fbf9SPekka Enberg case VIRTIO_MSI_CONFIG_VECTOR: 208802e07a0SAsias He blk_device.config_vector = VIRTIO_MSI_NO_VECTOR; 20940ce993fSPekka Enberg break; 210fbc2fbf9SPekka Enberg case VIRTIO_MSI_QUEUE_VECTOR: 21140ce993fSPekka Enberg break; 212fbc2fbf9SPekka Enberg default: 2130528c2a7SPekka Enberg ret = false; 214fbc2fbf9SPekka Enberg }; 215fbc2fbf9SPekka Enberg 2164ef0f4d6SPekka Enberg mutex_unlock(&blk_device.mutex); 2170528c2a7SPekka Enberg 2180528c2a7SPekka Enberg return ret; 219fbc2fbf9SPekka Enberg } 220fbc2fbf9SPekka Enberg 221416b2c2dSAsias He static struct ioport_operations virtio_blk_io_ops = { 222416b2c2dSAsias He .io_in = virtio_blk_pci_io_in, 223416b2c2dSAsias He .io_out = virtio_blk_pci_io_out, 224fbc2fbf9SPekka Enberg }; 225fbc2fbf9SPekka Enberg 226b30d05adSPekka Enberg #define PCI_VENDOR_ID_REDHAT_QUMRANET 0x1af4 227b30d05adSPekka Enberg #define PCI_DEVICE_ID_VIRTIO_BLK 0x1001 228b30d05adSPekka Enberg #define PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET 0x1af4 229b30d05adSPekka Enberg #define PCI_SUBSYSTEM_ID_VIRTIO_BLK 0x0002 230b30d05adSPekka Enberg 231416b2c2dSAsias He static struct pci_device_header virtio_blk_pci_device = { 232b30d05adSPekka Enberg .vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET, 233b30d05adSPekka Enberg .device_id = PCI_DEVICE_ID_VIRTIO_BLK, 234b30d05adSPekka Enberg .header_type = PCI_HEADER_TYPE_NORMAL, 235b30d05adSPekka Enberg .revision_id = 0, 236b30d05adSPekka Enberg .class = 0x010000, 237b30d05adSPekka Enberg .subsys_vendor_id = PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET, 238b30d05adSPekka Enberg .subsys_id = PCI_SUBSYSTEM_ID_VIRTIO_BLK, 239d1888318SCyrill Gorcunov .bar[0] = IOPORT_VIRTIO_BLK | PCI_BASE_ADDRESS_SPACE_IO, 240bc0363c8SCyrill Gorcunov .irq_pin = VIRTIO_BLK_PIN, 2418b1ff07eSPekka Enberg .irq_line = VIRTIO_BLK_IRQ, 242b30d05adSPekka Enberg }; 243b30d05adSPekka Enberg 244f05bbe8dSAsias He #define PCI_VIRTIO_BLK_DEVNUM 1 245f05bbe8dSAsias He 246416b2c2dSAsias He void virtio_blk__init(struct kvm *self) 247b30d05adSPekka Enberg { 2481f848897SPekka Enberg if (!self->disk_image) 2491f848897SPekka Enberg return; 2501f848897SPekka Enberg 251802e07a0SAsias He blk_device.blk_config.capacity = self->disk_image->size / SECTOR_SIZE; 252ca7c891bSCyrill Gorcunov 253416b2c2dSAsias He pci__register(&virtio_blk_pci_device, PCI_VIRTIO_BLK_DEVNUM); 254b30d05adSPekka Enberg 255416b2c2dSAsias He ioport__register(IOPORT_VIRTIO_BLK, &virtio_blk_io_ops, IOPORT_VIRTIO_BLK_SIZE); 256b30d05adSPekka Enberg } 257