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" 12b30d05adSPekka Enberg 1320c64ecaSPekka Enberg #include <linux/virtio_ring.h> 1420c64ecaSPekka Enberg #include <linux/virtio_blk.h> 150528c2a7SPekka Enberg 164155ba8cSPekka Enberg #include <inttypes.h> 170528c2a7SPekka Enberg #include <pthread.h> 184155ba8cSPekka Enberg 198b1ff07eSPekka Enberg #define VIRTIO_BLK_IRQ 14 20984b7ae0SPekka Enberg 2110eca11dSPekka Enberg #define NUM_VIRT_QUEUES 1 2210eca11dSPekka Enberg 2303110ff3SAsias He #define VIRTIO_BLK_QUEUE_SIZE 128 2410eca11dSPekka Enberg 25802e07a0SAsias He struct blk_device { 260528c2a7SPekka Enberg pthread_mutex_t mutex; 270528c2a7SPekka Enberg 2840ce993fSPekka Enberg struct virtio_blk_config blk_config; 29c435b91dSPekka Enberg uint32_t host_features; 30fbc2fbf9SPekka Enberg uint32_t guest_features; 3140ce993fSPekka Enberg uint16_t config_vector; 32fbc2fbf9SPekka Enberg uint8_t status; 33*4baf6f73SSasha Levin pthread_t io_thread; 34*4baf6f73SSasha Levin pthread_mutex_t io_mutex; 35*4baf6f73SSasha Levin pthread_cond_t io_cond; 3647bf1d0fSPekka Enberg 3747bf1d0fSPekka Enberg /* virtio queue */ 3847bf1d0fSPekka Enberg uint16_t queue_selector; 39*4baf6f73SSasha Levin uint64_t virtio_blk_queue_set_flags; 4010eca11dSPekka Enberg 4145e47970SAsias He struct virt_queue vqs[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), 59*4baf6f73SSasha Levin 60*4baf6f73SSasha Levin .io_mutex = PTHREAD_MUTEX_INITIALIZER, 61*4baf6f73SSasha Levin .io_cond = PTHREAD_COND_INITIALIZER 62c435b91dSPekka Enberg }; 63fbc2fbf9SPekka Enberg 64416b2c2dSAsias He static bool virtio_blk_pci_io_device_specific_in(void *data, unsigned long offset, int size, uint32_t count) 6540ce993fSPekka Enberg { 66802e07a0SAsias He uint8_t *config_space = (uint8_t *) &blk_device.blk_config; 6740ce993fSPekka Enberg 6840ce993fSPekka Enberg if (size != 1 || count != 1) 6940ce993fSPekka Enberg return false; 7040ce993fSPekka Enberg 7140ce993fSPekka Enberg ioport__write8(data, config_space[offset - VIRTIO_PCI_CONFIG_NOMSI]); 7240ce993fSPekka Enberg 7340ce993fSPekka Enberg return true; 7440ce993fSPekka Enberg } 7540ce993fSPekka Enberg 76416b2c2dSAsias He static bool virtio_blk_pci_io_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count) 77fbc2fbf9SPekka Enberg { 78fbc2fbf9SPekka Enberg unsigned long offset; 790528c2a7SPekka Enberg bool ret = true; 800528c2a7SPekka Enberg 814ef0f4d6SPekka Enberg mutex_lock(&blk_device.mutex); 82fbc2fbf9SPekka Enberg 83d1888318SCyrill Gorcunov offset = port - IOPORT_VIRTIO_BLK; 84fbc2fbf9SPekka Enberg 85fbc2fbf9SPekka Enberg switch (offset) { 86fbc2fbf9SPekka Enberg case VIRTIO_PCI_HOST_FEATURES: 87802e07a0SAsias He ioport__write32(data, blk_device.host_features); 88fbc2fbf9SPekka Enberg break; 89fbc2fbf9SPekka Enberg case VIRTIO_PCI_GUEST_FEATURES: 900528c2a7SPekka Enberg ret = false; 919ee67e60SAsias He break; 92fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_PFN: 9345e47970SAsias He ioport__write32(data, blk_device.vqs[blk_device.queue_selector].pfn); 948b1ff07eSPekka Enberg break; 95fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_NUM: 9610eca11dSPekka Enberg ioport__write16(data, VIRTIO_BLK_QUEUE_SIZE); 978b1ff07eSPekka Enberg break; 98fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_SEL: 99fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_NOTIFY: 1000528c2a7SPekka Enberg ret = false; 1019ee67e60SAsias He break; 102fbc2fbf9SPekka Enberg case VIRTIO_PCI_STATUS: 103802e07a0SAsias He ioport__write8(data, blk_device.status); 104fbc2fbf9SPekka Enberg break; 105fbc2fbf9SPekka Enberg case VIRTIO_PCI_ISR: 1068b1ff07eSPekka Enberg ioport__write8(data, 0x1); 1078b1ff07eSPekka Enberg kvm__irq_line(self, VIRTIO_BLK_IRQ, 0); 1087e61688eSPekka Enberg break; 109fbc2fbf9SPekka Enberg case VIRTIO_MSI_CONFIG_VECTOR: 110802e07a0SAsias He ioport__write16(data, blk_device.config_vector); 11140ce993fSPekka Enberg break; 112fbc2fbf9SPekka Enberg default: 1130528c2a7SPekka Enberg ret = virtio_blk_pci_io_device_specific_in(data, offset, size, count); 114fbc2fbf9SPekka Enberg }; 115fbc2fbf9SPekka Enberg 1164ef0f4d6SPekka Enberg mutex_unlock(&blk_device.mutex); 1170528c2a7SPekka Enberg 1180528c2a7SPekka Enberg return ret; 119fbc2fbf9SPekka Enberg } 120fbc2fbf9SPekka Enberg 12145e47970SAsias He static bool virtio_blk_do_io_request(struct kvm *self, struct virt_queue *queue) 1224155ba8cSPekka Enberg { 12345e47970SAsias He struct iovec iov[VIRTIO_BLK_QUEUE_SIZE]; 1244155ba8cSPekka Enberg struct virtio_blk_outhdr *req; 12570b53f25SSasha Levin ssize_t block_cnt = -1; 12645e47970SAsias He uint16_t out, in, head; 1274155ba8cSPekka Enberg uint8_t *status; 1284155ba8cSPekka Enberg 12945e47970SAsias He head = virt_queue__get_iov(queue, iov, &out, &in, self); 1304155ba8cSPekka Enberg 13145e47970SAsias He /* head */ 13245e47970SAsias He req = iov[0].iov_base; 13303110ff3SAsias He 134258dd093SPekka Enberg switch (req->type) { 13503110ff3SAsias He case VIRTIO_BLK_T_IN: 1362d103098SSasha Levin block_cnt = disk_image__read_sector_iov(self->disk_image, req->sector, iov + 1, in + out - 2); 13770b53f25SSasha Levin 138258dd093SPekka Enberg break; 13903110ff3SAsias He case VIRTIO_BLK_T_OUT: 1402d103098SSasha Levin block_cnt = disk_image__write_sector_iov(self->disk_image, req->sector, iov + 1, in + out - 2); 14170b53f25SSasha Levin 142258dd093SPekka Enberg break; 14370b53f25SSasha Levin 144258dd093SPekka Enberg default: 1454155ba8cSPekka Enberg warning("request type %d", req->type); 14670b53f25SSasha Levin block_cnt = -1; 14703110ff3SAsias He } 14803110ff3SAsias He 14945e47970SAsias He /* status */ 15045e47970SAsias He status = iov[out + in - 1].iov_base; 15170b53f25SSasha Levin *status = (block_cnt < 0) ? VIRTIO_BLK_S_IOERR : VIRTIO_BLK_S_OK; 15203110ff3SAsias He 15345e47970SAsias He virt_queue__set_used_elem(queue, head, block_cnt); 1544155ba8cSPekka Enberg 1554155ba8cSPekka Enberg return true; 1564155ba8cSPekka Enberg } 1574155ba8cSPekka Enberg 158*4baf6f73SSasha Levin 159*4baf6f73SSasha Levin 160*4baf6f73SSasha Levin static int virtio_blk_get_selected_queue(struct blk_device *dev) 16145e47970SAsias He { 162*4baf6f73SSasha Levin int i; 163*4baf6f73SSasha Levin 164*4baf6f73SSasha Levin for (i = 0 ; i < NUM_VIRT_QUEUES ; i++) { 165*4baf6f73SSasha Levin if (dev->virtio_blk_queue_set_flags & (1 << i)) { 166*4baf6f73SSasha Levin dev->virtio_blk_queue_set_flags &= ~(1 << i); 167*4baf6f73SSasha Levin return i; 168*4baf6f73SSasha Levin } 169*4baf6f73SSasha Levin } 170*4baf6f73SSasha Levin 171*4baf6f73SSasha Levin return -1; 172*4baf6f73SSasha Levin } 173*4baf6f73SSasha Levin 174*4baf6f73SSasha Levin static void virtio_blk_do_io(struct kvm *kvm, struct blk_device *dev) 175*4baf6f73SSasha Levin { 176*4baf6f73SSasha Levin for (;;) { 177*4baf6f73SSasha Levin struct virt_queue *vq; 178*4baf6f73SSasha Levin int queue_index; 179*4baf6f73SSasha Levin 180*4baf6f73SSasha Levin mutex_lock(&dev->io_mutex); 181*4baf6f73SSasha Levin queue_index = virtio_blk_get_selected_queue(dev); 182*4baf6f73SSasha Levin mutex_unlock(&dev->io_mutex); 183*4baf6f73SSasha Levin 184*4baf6f73SSasha Levin if (queue_index < 0) 185*4baf6f73SSasha Levin break; 186*4baf6f73SSasha Levin 187*4baf6f73SSasha Levin vq = &dev->vqs[queue_index]; 18845e47970SAsias He 1890ea58e5bSPekka Enberg while (virt_queue__available(vq)) 190*4baf6f73SSasha Levin virtio_blk_do_io_request(kvm, vq); 1910ea58e5bSPekka Enberg 192*4baf6f73SSasha Levin kvm__irq_line(kvm, VIRTIO_BLK_IRQ, 1); 193*4baf6f73SSasha Levin } 194*4baf6f73SSasha Levin } 19545e47970SAsias He 196*4baf6f73SSasha Levin static void *virtio_blk_io_thread(void *ptr) 197*4baf6f73SSasha Levin { 198*4baf6f73SSasha Levin struct kvm *self = ptr; 199*4baf6f73SSasha Levin 200*4baf6f73SSasha Levin for (;;) { 201*4baf6f73SSasha Levin int ret; 202*4baf6f73SSasha Levin 203*4baf6f73SSasha Levin mutex_lock(&blk_device.io_mutex); 204*4baf6f73SSasha Levin ret = pthread_cond_wait(&blk_device.io_cond, &blk_device.io_mutex); 205*4baf6f73SSasha Levin mutex_unlock(&blk_device.io_mutex); 206*4baf6f73SSasha Levin 207*4baf6f73SSasha Levin if (ret != 0) 208*4baf6f73SSasha Levin break; 209*4baf6f73SSasha Levin 210*4baf6f73SSasha Levin virtio_blk_do_io(self, &blk_device); 211*4baf6f73SSasha Levin } 212*4baf6f73SSasha Levin 213*4baf6f73SSasha Levin return NULL; 214*4baf6f73SSasha Levin } 215*4baf6f73SSasha Levin 216*4baf6f73SSasha Levin static void virtio_blk_handle_callback(struct blk_device *dev, uint16_t queue_index) 217*4baf6f73SSasha Levin { 218*4baf6f73SSasha Levin mutex_lock(&dev->io_mutex); 219*4baf6f73SSasha Levin 220*4baf6f73SSasha Levin dev->virtio_blk_queue_set_flags |= (1 << queue_index); 221*4baf6f73SSasha Levin 222*4baf6f73SSasha Levin mutex_unlock(&dev->io_mutex); 223*4baf6f73SSasha Levin 224*4baf6f73SSasha Levin pthread_cond_signal(&dev->io_cond); 22545e47970SAsias He } 2260528c2a7SPekka Enberg 227416b2c2dSAsias He static bool virtio_blk_pci_io_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count) 228fbc2fbf9SPekka Enberg { 229fbc2fbf9SPekka Enberg unsigned long offset; 2300528c2a7SPekka Enberg bool ret = true; 2310528c2a7SPekka Enberg 2324ef0f4d6SPekka Enberg mutex_lock(&blk_device.mutex); 233fbc2fbf9SPekka Enberg 234d1888318SCyrill Gorcunov offset = port - IOPORT_VIRTIO_BLK; 235fbc2fbf9SPekka Enberg 236fbc2fbf9SPekka Enberg switch (offset) { 237fbc2fbf9SPekka Enberg case VIRTIO_PCI_GUEST_FEATURES: 238802e07a0SAsias He blk_device.guest_features = ioport__read32(data); 239fbc2fbf9SPekka Enberg break; 24010eca11dSPekka Enberg case VIRTIO_PCI_QUEUE_PFN: { 24110eca11dSPekka Enberg struct virt_queue *queue; 24210eca11dSPekka Enberg void *p; 24310eca11dSPekka Enberg 24445e47970SAsias He queue = &blk_device.vqs[blk_device.queue_selector]; 24510eca11dSPekka Enberg 24610eca11dSPekka Enberg queue->pfn = ioport__read32(data); 24710eca11dSPekka Enberg 24810eca11dSPekka Enberg p = guest_flat_to_host(self, queue->pfn << 12); 24910eca11dSPekka Enberg 25010eca11dSPekka Enberg vring_init(&queue->vring, VIRTIO_BLK_QUEUE_SIZE, p, 4096); 25110eca11dSPekka Enberg 2527e61688eSPekka Enberg break; 25310eca11dSPekka Enberg } 254fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_SEL: 255802e07a0SAsias He blk_device.queue_selector = ioport__read16(data); 2567e61688eSPekka Enberg break; 25710eca11dSPekka Enberg case VIRTIO_PCI_QUEUE_NOTIFY: { 25810eca11dSPekka Enberg uint16_t queue_index; 25910eca11dSPekka Enberg queue_index = ioport__read16(data); 260*4baf6f73SSasha Levin virtio_blk_handle_callback(&blk_device, queue_index); 2617e61688eSPekka Enberg break; 26210eca11dSPekka Enberg } 263fbc2fbf9SPekka Enberg case VIRTIO_PCI_STATUS: 264802e07a0SAsias He blk_device.status = ioport__read8(data); 265fbc2fbf9SPekka Enberg break; 266fbc2fbf9SPekka Enberg case VIRTIO_MSI_CONFIG_VECTOR: 267802e07a0SAsias He blk_device.config_vector = VIRTIO_MSI_NO_VECTOR; 26840ce993fSPekka Enberg break; 269fbc2fbf9SPekka Enberg case VIRTIO_MSI_QUEUE_VECTOR: 27040ce993fSPekka Enberg break; 271fbc2fbf9SPekka Enberg default: 2720528c2a7SPekka Enberg ret = false; 273fbc2fbf9SPekka Enberg }; 274fbc2fbf9SPekka Enberg 2754ef0f4d6SPekka Enberg mutex_unlock(&blk_device.mutex); 2760528c2a7SPekka Enberg 2770528c2a7SPekka Enberg return ret; 278fbc2fbf9SPekka Enberg } 279fbc2fbf9SPekka Enberg 280416b2c2dSAsias He static struct ioport_operations virtio_blk_io_ops = { 281416b2c2dSAsias He .io_in = virtio_blk_pci_io_in, 282416b2c2dSAsias He .io_out = virtio_blk_pci_io_out, 283fbc2fbf9SPekka Enberg }; 284fbc2fbf9SPekka Enberg 285b30d05adSPekka Enberg #define PCI_VENDOR_ID_REDHAT_QUMRANET 0x1af4 286b30d05adSPekka Enberg #define PCI_DEVICE_ID_VIRTIO_BLK 0x1001 287b30d05adSPekka Enberg #define PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET 0x1af4 288b30d05adSPekka Enberg #define PCI_SUBSYSTEM_ID_VIRTIO_BLK 0x0002 289b30d05adSPekka Enberg 290416b2c2dSAsias He static struct pci_device_header virtio_blk_pci_device = { 291b30d05adSPekka Enberg .vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET, 292b30d05adSPekka Enberg .device_id = PCI_DEVICE_ID_VIRTIO_BLK, 293b30d05adSPekka Enberg .header_type = PCI_HEADER_TYPE_NORMAL, 294b30d05adSPekka Enberg .revision_id = 0, 295b30d05adSPekka Enberg .class = 0x010000, 296b30d05adSPekka Enberg .subsys_vendor_id = PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET, 297b30d05adSPekka Enberg .subsys_id = PCI_SUBSYSTEM_ID_VIRTIO_BLK, 298d1888318SCyrill Gorcunov .bar[0] = IOPORT_VIRTIO_BLK | PCI_BASE_ADDRESS_SPACE_IO, 299dc53a427SPekka Enberg .irq_pin = 1, 3008b1ff07eSPekka Enberg .irq_line = VIRTIO_BLK_IRQ, 301b30d05adSPekka Enberg }; 302b30d05adSPekka Enberg 303f05bbe8dSAsias He #define PCI_VIRTIO_BLK_DEVNUM 1 304f05bbe8dSAsias He 305416b2c2dSAsias He void virtio_blk__init(struct kvm *self) 306b30d05adSPekka Enberg { 3071f848897SPekka Enberg if (!self->disk_image) 3081f848897SPekka Enberg return; 3091f848897SPekka Enberg 310*4baf6f73SSasha Levin pthread_create(&blk_device.io_thread, NULL, virtio_blk_io_thread, self); 311*4baf6f73SSasha Levin 312802e07a0SAsias He blk_device.blk_config.capacity = self->disk_image->size / SECTOR_SIZE; 313ca7c891bSCyrill Gorcunov 314416b2c2dSAsias He pci__register(&virtio_blk_pci_device, PCI_VIRTIO_BLK_DEVNUM); 315b30d05adSPekka Enberg 316416b2c2dSAsias He ioport__register(IOPORT_VIRTIO_BLK, &virtio_blk_io_ops, IOPORT_VIRTIO_BLK_SIZE); 317b30d05adSPekka Enberg } 318