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" 8fe99fd4eSPekka Enberg #include "kvm/util.h" 98b1ff07eSPekka Enberg #include "kvm/kvm.h" 10b30d05adSPekka Enberg #include "kvm/pci.h" 11b30d05adSPekka Enberg 1220c64ecaSPekka Enberg #include <linux/virtio_ring.h> 1320c64ecaSPekka Enberg #include <linux/virtio_blk.h> 14*0528c2a7SPekka Enberg 154155ba8cSPekka Enberg #include <inttypes.h> 16*0528c2a7SPekka Enberg #include <pthread.h> 174155ba8cSPekka Enberg 188b1ff07eSPekka Enberg #define VIRTIO_BLK_IRQ 14 19984b7ae0SPekka Enberg 2010eca11dSPekka Enberg #define NUM_VIRT_QUEUES 1 2110eca11dSPekka Enberg 2203110ff3SAsias He #define VIRTIO_BLK_QUEUE_SIZE 128 2310eca11dSPekka Enberg 24802e07a0SAsias He struct blk_device { 25*0528c2a7SPekka Enberg pthread_mutex_t mutex; 26*0528c2a7SPekka Enberg 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 3645e47970SAsias He struct virt_queue vqs[NUM_VIRT_QUEUES]; 37fbc2fbf9SPekka Enberg }; 38fbc2fbf9SPekka Enberg 3903110ff3SAsias He #define DISK_SEG_MAX 126 4040ce993fSPekka Enberg 41802e07a0SAsias He static struct blk_device blk_device = { 42*0528c2a7SPekka Enberg .mutex = PTHREAD_MUTEX_INITIALIZER, 43*0528c2a7SPekka Enberg 4440ce993fSPekka Enberg .blk_config = (struct virtio_blk_config) { 45e199f440SPekka Enberg /* VIRTIO_BLK_F_SEG_MAX */ 4603110ff3SAsias He .seg_max = DISK_SEG_MAX, 4740ce993fSPekka Enberg }, 481ef2738dSCyrill Gorcunov /* 491ef2738dSCyrill Gorcunov * Note we don't set VIRTIO_BLK_F_GEOMETRY here so the 501ef2738dSCyrill Gorcunov * node kernel will compute disk geometry by own, the 511ef2738dSCyrill Gorcunov * same applies to VIRTIO_BLK_F_BLK_SIZE 521ef2738dSCyrill Gorcunov */ 5303110ff3SAsias He .host_features = (1UL << VIRTIO_BLK_F_SEG_MAX), 54c435b91dSPekka Enberg }; 55fbc2fbf9SPekka Enberg 56416b2c2dSAsias He static bool virtio_blk_pci_io_device_specific_in(void *data, unsigned long offset, int size, uint32_t count) 5740ce993fSPekka Enberg { 58802e07a0SAsias He uint8_t *config_space = (uint8_t *) &blk_device.blk_config; 5940ce993fSPekka Enberg 6040ce993fSPekka Enberg if (size != 1 || count != 1) 6140ce993fSPekka Enberg return false; 6240ce993fSPekka Enberg 6340ce993fSPekka Enberg ioport__write8(data, config_space[offset - VIRTIO_PCI_CONFIG_NOMSI]); 6440ce993fSPekka Enberg 6540ce993fSPekka Enberg return true; 6640ce993fSPekka Enberg } 6740ce993fSPekka Enberg 68416b2c2dSAsias He static bool virtio_blk_pci_io_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count) 69fbc2fbf9SPekka Enberg { 70fbc2fbf9SPekka Enberg unsigned long offset; 71*0528c2a7SPekka Enberg bool ret = true; 72*0528c2a7SPekka Enberg 73*0528c2a7SPekka Enberg if (pthread_mutex_lock(&blk_device.mutex) < 0) 74*0528c2a7SPekka Enberg die("pthread_mutex_lock"); 75fbc2fbf9SPekka Enberg 76d1888318SCyrill Gorcunov offset = port - IOPORT_VIRTIO_BLK; 77fbc2fbf9SPekka Enberg 78fbc2fbf9SPekka Enberg switch (offset) { 79fbc2fbf9SPekka Enberg case VIRTIO_PCI_HOST_FEATURES: 80802e07a0SAsias He ioport__write32(data, blk_device.host_features); 81fbc2fbf9SPekka Enberg break; 82fbc2fbf9SPekka Enberg case VIRTIO_PCI_GUEST_FEATURES: 83*0528c2a7SPekka Enberg ret = false; 84*0528c2a7SPekka Enberg goto out_unlock; 85fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_PFN: 8645e47970SAsias He ioport__write32(data, blk_device.vqs[blk_device.queue_selector].pfn); 878b1ff07eSPekka Enberg break; 88fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_NUM: 8910eca11dSPekka Enberg ioport__write16(data, VIRTIO_BLK_QUEUE_SIZE); 908b1ff07eSPekka Enberg break; 91fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_SEL: 92fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_NOTIFY: 93*0528c2a7SPekka Enberg ret = false; 94*0528c2a7SPekka Enberg goto out_unlock; 95fbc2fbf9SPekka Enberg case VIRTIO_PCI_STATUS: 96802e07a0SAsias He ioport__write8(data, blk_device.status); 97fbc2fbf9SPekka Enberg break; 98fbc2fbf9SPekka Enberg case VIRTIO_PCI_ISR: 998b1ff07eSPekka Enberg ioport__write8(data, 0x1); 1008b1ff07eSPekka Enberg kvm__irq_line(self, VIRTIO_BLK_IRQ, 0); 1017e61688eSPekka Enberg break; 102fbc2fbf9SPekka Enberg case VIRTIO_MSI_CONFIG_VECTOR: 103802e07a0SAsias He ioport__write16(data, blk_device.config_vector); 10440ce993fSPekka Enberg break; 105fbc2fbf9SPekka Enberg default: 106*0528c2a7SPekka Enberg ret = virtio_blk_pci_io_device_specific_in(data, offset, size, count); 107*0528c2a7SPekka Enberg goto out_unlock; 108fbc2fbf9SPekka Enberg }; 109fbc2fbf9SPekka Enberg 110*0528c2a7SPekka Enberg out_unlock: 111*0528c2a7SPekka Enberg if (pthread_mutex_unlock(&blk_device.mutex) < 0) 112*0528c2a7SPekka Enberg die("pthread_mutex_unlock"); 113*0528c2a7SPekka Enberg 114*0528c2a7SPekka Enberg return ret; 115fbc2fbf9SPekka Enberg } 116fbc2fbf9SPekka Enberg 11745e47970SAsias He static bool virtio_blk_do_io_request(struct kvm *self, struct virt_queue *queue) 1184155ba8cSPekka Enberg { 11945e47970SAsias He struct iovec iov[VIRTIO_BLK_QUEUE_SIZE]; 1204155ba8cSPekka Enberg struct virtio_blk_outhdr *req; 12145e47970SAsias He uint32_t block_len, block_cnt; 12245e47970SAsias He uint16_t out, in, head; 1234155ba8cSPekka Enberg uint8_t *status; 124f0952e64SPekka Enberg bool io_error; 125258dd093SPekka Enberg void *block; 126f0952e64SPekka Enberg int err, i; 127f0952e64SPekka Enberg 128f0952e64SPekka Enberg io_error = false; 1294155ba8cSPekka Enberg 13045e47970SAsias He head = virt_queue__get_iov(queue, iov, &out, &in, self); 1314155ba8cSPekka Enberg 13245e47970SAsias He /* head */ 13345e47970SAsias He req = iov[0].iov_base; 13403110ff3SAsias He 1354155ba8cSPekka Enberg /* block */ 13603110ff3SAsias He block_cnt = 0; 13703110ff3SAsias He 13845e47970SAsias He for (i = 1; i < out + in - 1; i++) { 13945e47970SAsias He block = iov[i].iov_base; 14045e47970SAsias He block_len = iov[i].iov_len; 1414155ba8cSPekka Enberg 142258dd093SPekka Enberg switch (req->type) { 14303110ff3SAsias He case VIRTIO_BLK_T_IN: 144258dd093SPekka Enberg err = disk_image__read_sector(self->disk_image, req->sector, block, block_len); 145258dd093SPekka Enberg break; 14603110ff3SAsias He case VIRTIO_BLK_T_OUT: 147258dd093SPekka Enberg err = disk_image__write_sector(self->disk_image, req->sector, block, block_len); 148258dd093SPekka Enberg break; 149258dd093SPekka Enberg default: 1504155ba8cSPekka Enberg warning("request type %d", req->type); 151f0952e64SPekka Enberg io_error = true; 152a2c8c696SAsias He } 153a2c8c696SAsias He 15403110ff3SAsias He req->sector += block_len >> SECTOR_SHIFT; 15503110ff3SAsias He block_cnt += block_len; 15603110ff3SAsias He } 15703110ff3SAsias He 15845e47970SAsias He /* status */ 15945e47970SAsias He status = iov[out + in - 1].iov_base; 160f0952e64SPekka Enberg *status = io_error ? VIRTIO_BLK_S_IOERR : VIRTIO_BLK_S_OK; 16103110ff3SAsias He 16245e47970SAsias He virt_queue__set_used_elem(queue, head, block_cnt); 1634155ba8cSPekka Enberg 1644155ba8cSPekka Enberg return true; 1654155ba8cSPekka Enberg } 1664155ba8cSPekka Enberg 16745e47970SAsias He static void virtio_blk_handle_callback(struct kvm *self, uint16_t queue_index) 16845e47970SAsias He { 1690ea58e5bSPekka Enberg struct virt_queue *vq = &blk_device.vqs[queue_index]; 17045e47970SAsias He 1710ea58e5bSPekka Enberg while (virt_queue__available(vq)) 17245e47970SAsias He virtio_blk_do_io_request(self, vq); 1730ea58e5bSPekka Enberg 17445e47970SAsias He kvm__irq_line(self, VIRTIO_BLK_IRQ, 1); 17545e47970SAsias He 17645e47970SAsias He } 177*0528c2a7SPekka Enberg 178416b2c2dSAsias He static bool virtio_blk_pci_io_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count) 179fbc2fbf9SPekka Enberg { 180fbc2fbf9SPekka Enberg unsigned long offset; 181*0528c2a7SPekka Enberg bool ret = true; 182*0528c2a7SPekka Enberg 183*0528c2a7SPekka Enberg if (pthread_mutex_lock(&blk_device.mutex) < 0) 184*0528c2a7SPekka Enberg die("pthread_mutex_lock"); 185fbc2fbf9SPekka Enberg 186d1888318SCyrill Gorcunov offset = port - IOPORT_VIRTIO_BLK; 187fbc2fbf9SPekka Enberg 188fbc2fbf9SPekka Enberg switch (offset) { 189fbc2fbf9SPekka Enberg case VIRTIO_PCI_GUEST_FEATURES: 190802e07a0SAsias He blk_device.guest_features = ioport__read32(data); 191fbc2fbf9SPekka Enberg break; 19210eca11dSPekka Enberg case VIRTIO_PCI_QUEUE_PFN: { 19310eca11dSPekka Enberg struct virt_queue *queue; 19410eca11dSPekka Enberg void *p; 19510eca11dSPekka Enberg 19645e47970SAsias He queue = &blk_device.vqs[blk_device.queue_selector]; 19710eca11dSPekka Enberg 19810eca11dSPekka Enberg queue->pfn = ioport__read32(data); 19910eca11dSPekka Enberg 20010eca11dSPekka Enberg p = guest_flat_to_host(self, queue->pfn << 12); 20110eca11dSPekka Enberg 20210eca11dSPekka Enberg vring_init(&queue->vring, VIRTIO_BLK_QUEUE_SIZE, p, 4096); 20310eca11dSPekka Enberg 2047e61688eSPekka Enberg break; 20510eca11dSPekka Enberg } 206fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_SEL: 207802e07a0SAsias He blk_device.queue_selector = ioport__read16(data); 2087e61688eSPekka Enberg break; 20910eca11dSPekka Enberg case VIRTIO_PCI_QUEUE_NOTIFY: { 21010eca11dSPekka Enberg uint16_t queue_index; 21110eca11dSPekka Enberg queue_index = ioport__read16(data); 21245e47970SAsias He virtio_blk_handle_callback(self, queue_index); 2137e61688eSPekka Enberg break; 21410eca11dSPekka Enberg } 215fbc2fbf9SPekka Enberg case VIRTIO_PCI_STATUS: 216802e07a0SAsias He blk_device.status = ioport__read8(data); 217fbc2fbf9SPekka Enberg break; 218fbc2fbf9SPekka Enberg case VIRTIO_MSI_CONFIG_VECTOR: 219802e07a0SAsias He blk_device.config_vector = VIRTIO_MSI_NO_VECTOR; 22040ce993fSPekka Enberg break; 221fbc2fbf9SPekka Enberg case VIRTIO_MSI_QUEUE_VECTOR: 22240ce993fSPekka Enberg break; 223fbc2fbf9SPekka Enberg default: 224*0528c2a7SPekka Enberg ret = false; 225*0528c2a7SPekka Enberg goto out_unlock; 226fbc2fbf9SPekka Enberg }; 227fbc2fbf9SPekka Enberg 228*0528c2a7SPekka Enberg out_unlock: 229*0528c2a7SPekka Enberg if (pthread_mutex_unlock(&blk_device.mutex) < 0) 230*0528c2a7SPekka Enberg die("pthread_mutex_unlock"); 231*0528c2a7SPekka Enberg 232*0528c2a7SPekka Enberg return ret; 233fbc2fbf9SPekka Enberg } 234fbc2fbf9SPekka Enberg 235416b2c2dSAsias He static struct ioport_operations virtio_blk_io_ops = { 236416b2c2dSAsias He .io_in = virtio_blk_pci_io_in, 237416b2c2dSAsias He .io_out = virtio_blk_pci_io_out, 238fbc2fbf9SPekka Enberg }; 239fbc2fbf9SPekka Enberg 240b30d05adSPekka Enberg #define PCI_VENDOR_ID_REDHAT_QUMRANET 0x1af4 241b30d05adSPekka Enberg #define PCI_DEVICE_ID_VIRTIO_BLK 0x1001 242b30d05adSPekka Enberg #define PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET 0x1af4 243b30d05adSPekka Enberg #define PCI_SUBSYSTEM_ID_VIRTIO_BLK 0x0002 244b30d05adSPekka Enberg 245416b2c2dSAsias He static struct pci_device_header virtio_blk_pci_device = { 246b30d05adSPekka Enberg .vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET, 247b30d05adSPekka Enberg .device_id = PCI_DEVICE_ID_VIRTIO_BLK, 248b30d05adSPekka Enberg .header_type = PCI_HEADER_TYPE_NORMAL, 249b30d05adSPekka Enberg .revision_id = 0, 250b30d05adSPekka Enberg .class = 0x010000, 251b30d05adSPekka Enberg .subsys_vendor_id = PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET, 252b30d05adSPekka Enberg .subsys_id = PCI_SUBSYSTEM_ID_VIRTIO_BLK, 253d1888318SCyrill Gorcunov .bar[0] = IOPORT_VIRTIO_BLK | PCI_BASE_ADDRESS_SPACE_IO, 254dc53a427SPekka Enberg .irq_pin = 1, 2558b1ff07eSPekka Enberg .irq_line = VIRTIO_BLK_IRQ, 256b30d05adSPekka Enberg }; 257b30d05adSPekka Enberg 258f05bbe8dSAsias He #define PCI_VIRTIO_BLK_DEVNUM 1 259f05bbe8dSAsias He 260416b2c2dSAsias He void virtio_blk__init(struct kvm *self) 261b30d05adSPekka Enberg { 2621f848897SPekka Enberg if (!self->disk_image) 2631f848897SPekka Enberg return; 2641f848897SPekka Enberg 265802e07a0SAsias He blk_device.blk_config.capacity = self->disk_image->size / SECTOR_SIZE; 266ca7c891bSCyrill Gorcunov 267416b2c2dSAsias He pci__register(&virtio_blk_pci_device, PCI_VIRTIO_BLK_DEVNUM); 268b30d05adSPekka Enberg 269416b2c2dSAsias He ioport__register(IOPORT_VIRTIO_BLK, &virtio_blk_io_ops, IOPORT_VIRTIO_BLK_SIZE); 270b30d05adSPekka Enberg } 271