1416b2c2dSAsias He #include "kvm/virtio-blk.h" 2b30d05adSPekka Enberg 3416b2c2dSAsias He #include "kvm/virtio-pci.h" 431638bcaSCyrill Gorcunov #include "kvm/virtio-pci-dev.h" 52449f6e3SSasha Levin #include "kvm/irq.h" 65a24a9f2SPekka Enberg #include "kvm/disk-image.h" 739d6af07SAsias He #include "kvm/virtio.h" 8b30d05adSPekka Enberg #include "kvm/ioport.h" 94ef0f4d6SPekka Enberg #include "kvm/mutex.h" 10fe99fd4eSPekka Enberg #include "kvm/util.h" 118b1ff07eSPekka Enberg #include "kvm/kvm.h" 12b30d05adSPekka Enberg #include "kvm/pci.h" 13fb0957f2SSasha Levin #include "kvm/threadpool.h" 14b30d05adSPekka Enberg 1520c64ecaSPekka Enberg #include <linux/virtio_ring.h> 1620c64ecaSPekka Enberg #include <linux/virtio_blk.h> 170528c2a7SPekka Enberg 183fdf659dSSasha Levin #include <linux/types.h> 190528c2a7SPekka Enberg #include <pthread.h> 204155ba8cSPekka Enberg 214749e795SSasha Levin #define VIRTIO_BLK_MAX_DEV 4 2210eca11dSPekka Enberg #define NUM_VIRT_QUEUES 1 2310eca11dSPekka Enberg 2403110ff3SAsias He #define VIRTIO_BLK_QUEUE_SIZE 128 2510eca11dSPekka Enberg 26fe2a70d1SSasha Levin struct blk_dev_job { 274749e795SSasha Levin struct virt_queue *vq; 28fe2a70d1SSasha Levin struct blk_dev *bdev; 294749e795SSasha Levin void *job_id; 304749e795SSasha Levin }; 314749e795SSasha Levin 32fe2a70d1SSasha Levin struct blk_dev { 330528c2a7SPekka Enberg pthread_mutex_t mutex; 340528c2a7SPekka Enberg 3540ce993fSPekka Enberg struct virtio_blk_config blk_config; 3638605e1cSSasha Levin struct disk_image *disk; 373fdf659dSSasha Levin u32 host_features; 383fdf659dSSasha Levin u32 guest_features; 393fdf659dSSasha Levin u16 config_vector; 403fdf659dSSasha Levin u8 status; 414749e795SSasha Levin u8 idx; 4247bf1d0fSPekka Enberg 4347bf1d0fSPekka Enberg /* virtio queue */ 443fdf659dSSasha Levin u16 queue_selector; 4510eca11dSPekka Enberg 4645e47970SAsias He struct virt_queue vqs[NUM_VIRT_QUEUES]; 47fe2a70d1SSasha Levin struct blk_dev_job jobs[NUM_VIRT_QUEUES]; 48*ef1f02f2SSasha Levin struct pci_device_header pci_hdr; 49fbc2fbf9SPekka Enberg }; 50fbc2fbf9SPekka Enberg 51fe2a70d1SSasha Levin static struct blk_dev *bdevs[VIRTIO_BLK_MAX_DEV]; 5240ce993fSPekka Enberg 53407475bfSPekka Enberg static bool virtio_blk_dev_in(struct blk_dev *bdev, void *data, unsigned long offset, int size, u32 count) 5440ce993fSPekka Enberg { 55fe2a70d1SSasha Levin u8 *config_space = (u8 *) &bdev->blk_config; 5640ce993fSPekka Enberg 5740ce993fSPekka Enberg if (size != 1 || count != 1) 5840ce993fSPekka Enberg return false; 5940ce993fSPekka Enberg 6040ce993fSPekka Enberg ioport__write8(data, config_space[offset - VIRTIO_PCI_CONFIG_NOMSI]); 6140ce993fSPekka Enberg 6240ce993fSPekka Enberg return true; 6340ce993fSPekka Enberg } 6440ce993fSPekka Enberg 654749e795SSasha Levin /* Translate port into device id + offset in that device addr space */ 66407475bfSPekka Enberg static void virtio_blk_port2dev(u16 port, u16 base, u16 size, u16 *dev_idx, u16 *offset) 674749e795SSasha Levin { 684749e795SSasha Levin *dev_idx = (port - base) / size; 694749e795SSasha Levin *offset = port - (base + *dev_idx * size); 704749e795SSasha Levin } 71407475bfSPekka Enberg 723fdf659dSSasha Levin static bool virtio_blk_pci_io_in(struct kvm *self, u16 port, void *data, int size, u32 count) 73fbc2fbf9SPekka Enberg { 74407475bfSPekka Enberg struct blk_dev *bdev; 754749e795SSasha Levin u16 offset, dev_idx; 760528c2a7SPekka Enberg bool ret = true; 770528c2a7SPekka Enberg 78407475bfSPekka Enberg virtio_blk_port2dev(port, IOPORT_VIRTIO_BLK, IOPORT_VIRTIO_BLK_SIZE, &dev_idx, &offset); 79fbc2fbf9SPekka Enberg 80fe2a70d1SSasha Levin bdev = bdevs[dev_idx]; 814749e795SSasha Levin 82fe2a70d1SSasha Levin mutex_lock(&bdev->mutex); 83fbc2fbf9SPekka Enberg 84fbc2fbf9SPekka Enberg switch (offset) { 85fbc2fbf9SPekka Enberg case VIRTIO_PCI_HOST_FEATURES: 86fe2a70d1SSasha Levin ioport__write32(data, bdev->host_features); 87fbc2fbf9SPekka Enberg break; 88fbc2fbf9SPekka Enberg case VIRTIO_PCI_GUEST_FEATURES: 890528c2a7SPekka Enberg ret = false; 909ee67e60SAsias He break; 91fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_PFN: 92fe2a70d1SSasha Levin ioport__write32(data, bdev->vqs[bdev->queue_selector].pfn); 938b1ff07eSPekka Enberg break; 94fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_NUM: 9510eca11dSPekka Enberg ioport__write16(data, VIRTIO_BLK_QUEUE_SIZE); 968b1ff07eSPekka Enberg break; 97fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_SEL: 98fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_NOTIFY: 990528c2a7SPekka Enberg ret = false; 1009ee67e60SAsias He break; 101fbc2fbf9SPekka Enberg case VIRTIO_PCI_STATUS: 102fe2a70d1SSasha Levin ioport__write8(data, bdev->status); 103fbc2fbf9SPekka Enberg break; 104fbc2fbf9SPekka Enberg case VIRTIO_PCI_ISR: 1058b1ff07eSPekka Enberg ioport__write8(data, 0x1); 106*ef1f02f2SSasha Levin kvm__irq_line(self, bdev->pci_hdr.irq_line, 0); 1077e61688eSPekka Enberg break; 108fbc2fbf9SPekka Enberg case VIRTIO_MSI_CONFIG_VECTOR: 109fe2a70d1SSasha Levin ioport__write16(data, bdev->config_vector); 11040ce993fSPekka Enberg break; 111fbc2fbf9SPekka Enberg default: 112407475bfSPekka Enberg ret = virtio_blk_dev_in(bdev, data, offset, size, count); 113407475bfSPekka Enberg break; 114fbc2fbf9SPekka Enberg }; 115fbc2fbf9SPekka Enberg 116fe2a70d1SSasha Levin mutex_unlock(&bdev->mutex); 1170528c2a7SPekka Enberg 1180528c2a7SPekka Enberg return ret; 119fbc2fbf9SPekka Enberg } 120fbc2fbf9SPekka Enberg 1214749e795SSasha Levin static bool virtio_blk_do_io_request(struct kvm *self, 122fe2a70d1SSasha Levin struct blk_dev *bdev, 1234749e795SSasha Levin struct virt_queue *queue) 1244155ba8cSPekka Enberg { 12545e47970SAsias He struct iovec iov[VIRTIO_BLK_QUEUE_SIZE]; 1264155ba8cSPekka Enberg struct virtio_blk_outhdr *req; 12770b53f25SSasha Levin ssize_t block_cnt = -1; 1283fdf659dSSasha Levin u16 out, in, head; 1293fdf659dSSasha Levin u8 *status; 1304155ba8cSPekka Enberg 13145e47970SAsias He head = virt_queue__get_iov(queue, iov, &out, &in, self); 1324155ba8cSPekka Enberg 13345e47970SAsias He /* head */ 13445e47970SAsias He req = iov[0].iov_base; 13503110ff3SAsias He 136258dd093SPekka Enberg switch (req->type) { 13703110ff3SAsias He case VIRTIO_BLK_T_IN: 138407475bfSPekka Enberg block_cnt = disk_image__read_sector_iov(bdev->disk, req->sector, iov + 1, in + out - 2); 139258dd093SPekka Enberg break; 14003110ff3SAsias He case VIRTIO_BLK_T_OUT: 141407475bfSPekka Enberg block_cnt = disk_image__write_sector_iov(bdev->disk, req->sector, iov + 1, in + out - 2); 142258dd093SPekka Enberg break; 143258dd093SPekka Enberg default: 1444155ba8cSPekka Enberg warning("request type %d", req->type); 14570b53f25SSasha Levin block_cnt = -1; 146407475bfSPekka Enberg break; 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 158fb0957f2SSasha Levin static void virtio_blk_do_io(struct kvm *kvm, void *param) 15945e47970SAsias He { 160fe2a70d1SSasha Levin struct blk_dev_job *job = param; 161407475bfSPekka Enberg struct virt_queue *vq; 162407475bfSPekka Enberg struct blk_dev *bdev; 163407475bfSPekka Enberg 164407475bfSPekka Enberg vq = job->vq; 165407475bfSPekka Enberg bdev = job->bdev; 16645e47970SAsias He 1670ea58e5bSPekka Enberg while (virt_queue__available(vq)) 168fe2a70d1SSasha Levin virtio_blk_do_io_request(kvm, bdev, vq); 1690ea58e5bSPekka Enberg 170*ef1f02f2SSasha Levin kvm__irq_line(kvm, bdev->pci_hdr.irq_line, 1); 1714baf6f73SSasha Levin } 1720528c2a7SPekka Enberg 1733fdf659dSSasha Levin static bool virtio_blk_pci_io_out(struct kvm *self, u16 port, void *data, int size, u32 count) 174fbc2fbf9SPekka Enberg { 175407475bfSPekka Enberg struct blk_dev *bdev; 1764749e795SSasha Levin u16 offset, dev_idx; 1770528c2a7SPekka Enberg bool ret = true; 1780528c2a7SPekka Enberg 179407475bfSPekka Enberg virtio_blk_port2dev(port, IOPORT_VIRTIO_BLK, IOPORT_VIRTIO_BLK_SIZE, &dev_idx, &offset); 180fbc2fbf9SPekka Enberg 181fe2a70d1SSasha Levin bdev = bdevs[dev_idx]; 1824749e795SSasha Levin 183fe2a70d1SSasha Levin mutex_lock(&bdev->mutex); 184fbc2fbf9SPekka Enberg 185fbc2fbf9SPekka Enberg switch (offset) { 186fbc2fbf9SPekka Enberg case VIRTIO_PCI_GUEST_FEATURES: 187fe2a70d1SSasha Levin bdev->guest_features = ioport__read32(data); 188fbc2fbf9SPekka Enberg break; 18910eca11dSPekka Enberg case VIRTIO_PCI_QUEUE_PFN: { 19010eca11dSPekka Enberg struct virt_queue *queue; 191fe2a70d1SSasha Levin struct blk_dev_job *job; 19210eca11dSPekka Enberg void *p; 19310eca11dSPekka Enberg 194fe2a70d1SSasha Levin job = &bdev->jobs[bdev->queue_selector]; 19510eca11dSPekka Enberg 196fe2a70d1SSasha Levin queue = &bdev->vqs[bdev->queue_selector]; 19710eca11dSPekka Enberg queue->pfn = ioport__read32(data); 19810eca11dSPekka Enberg p = guest_flat_to_host(self, queue->pfn << 12); 19910eca11dSPekka Enberg 20010eca11dSPekka Enberg vring_init(&queue->vring, VIRTIO_BLK_QUEUE_SIZE, p, 4096); 20110eca11dSPekka Enberg 202fe2a70d1SSasha Levin *job = (struct blk_dev_job) { 2034749e795SSasha Levin .vq = queue, 204fe2a70d1SSasha Levin .bdev = bdev, 2054749e795SSasha Levin }; 2064749e795SSasha Levin 2074749e795SSasha Levin job->job_id = thread_pool__add_job(self, virtio_blk_do_io, job); 208fb0957f2SSasha Levin 2097e61688eSPekka Enberg break; 21010eca11dSPekka Enberg } 211fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_SEL: 212fe2a70d1SSasha Levin bdev->queue_selector = ioport__read16(data); 2137e61688eSPekka Enberg break; 21410eca11dSPekka Enberg case VIRTIO_PCI_QUEUE_NOTIFY: { 2153fdf659dSSasha Levin u16 queue_index; 216407475bfSPekka Enberg 21710eca11dSPekka Enberg queue_index = ioport__read16(data); 218fe2a70d1SSasha Levin thread_pool__do_job(bdev->jobs[queue_index].job_id); 219407475bfSPekka Enberg 2207e61688eSPekka Enberg break; 22110eca11dSPekka Enberg } 222fbc2fbf9SPekka Enberg case VIRTIO_PCI_STATUS: 223fe2a70d1SSasha Levin bdev->status = ioport__read8(data); 224fbc2fbf9SPekka Enberg break; 225fbc2fbf9SPekka Enberg case VIRTIO_MSI_CONFIG_VECTOR: 226fe2a70d1SSasha Levin bdev->config_vector = VIRTIO_MSI_NO_VECTOR; 22740ce993fSPekka Enberg break; 228fbc2fbf9SPekka Enberg case VIRTIO_MSI_QUEUE_VECTOR: 22940ce993fSPekka Enberg break; 230fbc2fbf9SPekka Enberg default: 2310528c2a7SPekka Enberg ret = false; 232407475bfSPekka Enberg break; 233fbc2fbf9SPekka Enberg }; 234fbc2fbf9SPekka Enberg 235fe2a70d1SSasha Levin mutex_unlock(&bdev->mutex); 2360528c2a7SPekka Enberg 2370528c2a7SPekka Enberg return ret; 238fbc2fbf9SPekka Enberg } 239fbc2fbf9SPekka Enberg 240416b2c2dSAsias He static struct ioport_operations virtio_blk_io_ops = { 241416b2c2dSAsias He .io_in = virtio_blk_pci_io_in, 242416b2c2dSAsias He .io_out = virtio_blk_pci_io_out, 243fbc2fbf9SPekka Enberg }; 244fbc2fbf9SPekka Enberg 2454749e795SSasha Levin static int virtio_blk_find_empty_dev(void) 2464749e795SSasha Levin { 2474749e795SSasha Levin int i; 2484749e795SSasha Levin 2494749e795SSasha Levin for (i = 0; i < VIRTIO_BLK_MAX_DEV; i++) { 250fe2a70d1SSasha Levin if (bdevs[i] == NULL) 2514749e795SSasha Levin return i; 2524749e795SSasha Levin } 2534749e795SSasha Levin 2544749e795SSasha Levin return -1; 2554749e795SSasha Levin } 2564749e795SSasha Levin 2574749e795SSasha Levin void virtio_blk__init(struct kvm *self, struct disk_image *disk) 2584749e795SSasha Levin { 2594749e795SSasha Levin u16 blk_dev_base_addr; 2602449f6e3SSasha Levin u8 dev, pin, line; 261fe2a70d1SSasha Levin struct blk_dev *bdev; 262407475bfSPekka Enberg int new_dev_idx; 2634749e795SSasha Levin 2644749e795SSasha Levin if (!disk) 2654749e795SSasha Levin return; 2664749e795SSasha Levin 2674749e795SSasha Levin new_dev_idx = virtio_blk_find_empty_dev(); 2684749e795SSasha Levin if (new_dev_idx < 0) 2694749e795SSasha Levin die("Could not find an empty block device slot"); 2704749e795SSasha Levin 271fe2a70d1SSasha Levin bdevs[new_dev_idx] = calloc(1, sizeof(struct blk_dev)); 272fe2a70d1SSasha Levin if (bdevs[new_dev_idx] == NULL) 273fe2a70d1SSasha Levin die("Failed allocating bdev"); 2744749e795SSasha Levin 275fe2a70d1SSasha Levin bdev = bdevs[new_dev_idx]; 276fe2a70d1SSasha Levin 2774749e795SSasha Levin blk_dev_base_addr = IOPORT_VIRTIO_BLK + new_dev_idx * IOPORT_VIRTIO_BLK_SIZE; 2784749e795SSasha Levin 279fe2a70d1SSasha Levin *bdev = (struct blk_dev) { 2804749e795SSasha Levin .mutex = PTHREAD_MUTEX_INITIALIZER, 2814749e795SSasha Levin .disk = disk, 2824749e795SSasha Levin .idx = new_dev_idx, 2834749e795SSasha Levin .blk_config = (struct virtio_blk_config) { 2844749e795SSasha Levin .capacity = disk->size / SECTOR_SIZE, 2854749e795SSasha Levin }, 286*ef1f02f2SSasha Levin .pci_hdr = (struct pci_device_header) { 287b30d05adSPekka Enberg .vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET, 288b30d05adSPekka Enberg .device_id = PCI_DEVICE_ID_VIRTIO_BLK, 289b30d05adSPekka Enberg .header_type = PCI_HEADER_TYPE_NORMAL, 290b30d05adSPekka Enberg .revision_id = 0, 291b30d05adSPekka Enberg .class = 0x010000, 292b30d05adSPekka Enberg .subsys_vendor_id = PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET, 293b30d05adSPekka Enberg .subsys_id = PCI_SUBSYSTEM_ID_VIRTIO_BLK, 2944749e795SSasha Levin .bar[0] = blk_dev_base_addr | PCI_BASE_ADDRESS_SPACE_IO, 2954749e795SSasha Levin }, 296b30d05adSPekka Enberg }; 297b30d05adSPekka Enberg 2982449f6e3SSasha Levin if (irq__register_device(PCI_DEVICE_ID_VIRTIO_BLK, &dev, &pin, &line) < 0) 2992449f6e3SSasha Levin return; 3002449f6e3SSasha Levin 301*ef1f02f2SSasha Levin bdev->pci_hdr.irq_pin = pin; 302*ef1f02f2SSasha Levin bdev->pci_hdr.irq_line = line; 3032449f6e3SSasha Levin 304*ef1f02f2SSasha Levin pci__register(&bdev->pci_hdr, dev); 305f05bbe8dSAsias He 3064749e795SSasha Levin ioport__register(blk_dev_base_addr, &virtio_blk_io_ops, IOPORT_VIRTIO_BLK_SIZE); 307b30d05adSPekka Enberg } 308