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" 12fb0957f2SSasha 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 20*4749e795SSasha Levin #define VIRTIO_BLK_IRQ 9 21bc0363c8SCyrill Gorcunov #define VIRTIO_BLK_PIN 1 22*4749e795SSasha Levin #define VIRTIO_BLK_MAX_DEV 4 2310eca11dSPekka Enberg #define NUM_VIRT_QUEUES 1 2410eca11dSPekka Enberg 2503110ff3SAsias He #define VIRTIO_BLK_QUEUE_SIZE 128 2610eca11dSPekka Enberg 27*4749e795SSasha Levin struct blk_device_job { 28*4749e795SSasha Levin struct virt_queue *vq; 29*4749e795SSasha Levin struct blk_device *blk_device; 30*4749e795SSasha Levin void *job_id; 31*4749e795SSasha Levin }; 32*4749e795SSasha Levin 33802e07a0SAsias He struct blk_device { 340528c2a7SPekka Enberg pthread_mutex_t mutex; 350528c2a7SPekka Enberg 3640ce993fSPekka Enberg struct virtio_blk_config blk_config; 3738605e1cSSasha Levin struct disk_image *disk; 38c435b91dSPekka Enberg uint32_t host_features; 39fbc2fbf9SPekka Enberg uint32_t guest_features; 4040ce993fSPekka Enberg uint16_t config_vector; 41fbc2fbf9SPekka Enberg uint8_t status; 42*4749e795SSasha Levin u8 idx; 4347bf1d0fSPekka Enberg 4447bf1d0fSPekka Enberg /* virtio queue */ 4547bf1d0fSPekka Enberg uint16_t queue_selector; 4610eca11dSPekka Enberg 4745e47970SAsias He struct virt_queue vqs[NUM_VIRT_QUEUES]; 48*4749e795SSasha Levin struct blk_device_job jobs[NUM_VIRT_QUEUES]; 49*4749e795SSasha Levin struct pci_device_header pci_device; 50fbc2fbf9SPekka Enberg }; 51fbc2fbf9SPekka Enberg 52*4749e795SSasha Levin static struct blk_device *blk_devices[VIRTIO_BLK_MAX_DEV]; 5340ce993fSPekka Enberg 54*4749e795SSasha Levin static bool virtio_blk_pci_io_device_specific_in(struct blk_device *blk_device, 55*4749e795SSasha Levin void *data, 56*4749e795SSasha Levin unsigned long offset, 57*4749e795SSasha Levin int size, 58*4749e795SSasha Levin uint32_t count) 5940ce993fSPekka Enberg { 60*4749e795SSasha Levin uint8_t *config_space = (uint8_t *) &blk_device->blk_config; 6140ce993fSPekka Enberg 6240ce993fSPekka Enberg if (size != 1 || count != 1) 6340ce993fSPekka Enberg return false; 6440ce993fSPekka Enberg 6540ce993fSPekka Enberg ioport__write8(data, config_space[offset - VIRTIO_PCI_CONFIG_NOMSI]); 6640ce993fSPekka Enberg 6740ce993fSPekka Enberg return true; 6840ce993fSPekka Enberg } 6940ce993fSPekka Enberg 70*4749e795SSasha Levin /* Translate port into device id + offset in that device addr space */ 71*4749e795SSasha Levin static void virtio_blk_port2dev(u16 port, 72*4749e795SSasha Levin u16 base, 73*4749e795SSasha Levin u16 size, 74*4749e795SSasha Levin u16 *dev_idx, 75*4749e795SSasha Levin u16 *offset) 76*4749e795SSasha Levin { 77*4749e795SSasha Levin *dev_idx = (port - base) / size; 78*4749e795SSasha Levin *offset = port - (base + *dev_idx * size); 79*4749e795SSasha Levin } 80416b2c2dSAsias He static bool virtio_blk_pci_io_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count) 81fbc2fbf9SPekka Enberg { 82*4749e795SSasha Levin u16 offset, dev_idx; 830528c2a7SPekka Enberg bool ret = true; 84*4749e795SSasha Levin struct blk_device *blk_device; 850528c2a7SPekka Enberg 86*4749e795SSasha Levin virtio_blk_port2dev(port, IOPORT_VIRTIO_BLK, IOPORT_VIRTIO_BLK_SIZE, 87*4749e795SSasha Levin &dev_idx, &offset); 88fbc2fbf9SPekka Enberg 89*4749e795SSasha Levin blk_device = blk_devices[dev_idx]; 90*4749e795SSasha Levin 91*4749e795SSasha Levin mutex_lock(&blk_device->mutex); 92fbc2fbf9SPekka Enberg 93fbc2fbf9SPekka Enberg switch (offset) { 94fbc2fbf9SPekka Enberg case VIRTIO_PCI_HOST_FEATURES: 95*4749e795SSasha Levin ioport__write32(data, blk_device->host_features); 96fbc2fbf9SPekka Enberg break; 97fbc2fbf9SPekka Enberg case VIRTIO_PCI_GUEST_FEATURES: 980528c2a7SPekka Enberg ret = false; 999ee67e60SAsias He break; 100fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_PFN: 101*4749e795SSasha Levin ioport__write32(data, blk_device->vqs[blk_device->queue_selector].pfn); 1028b1ff07eSPekka Enberg break; 103fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_NUM: 10410eca11dSPekka Enberg ioport__write16(data, VIRTIO_BLK_QUEUE_SIZE); 1058b1ff07eSPekka Enberg break; 106fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_SEL: 107fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_NOTIFY: 1080528c2a7SPekka Enberg ret = false; 1099ee67e60SAsias He break; 110fbc2fbf9SPekka Enberg case VIRTIO_PCI_STATUS: 111*4749e795SSasha Levin ioport__write8(data, blk_device->status); 112fbc2fbf9SPekka Enberg break; 113fbc2fbf9SPekka Enberg case VIRTIO_PCI_ISR: 1148b1ff07eSPekka Enberg ioport__write8(data, 0x1); 115*4749e795SSasha Levin kvm__irq_line(self, VIRTIO_BLK_IRQ + blk_device->idx, 0); 1167e61688eSPekka Enberg break; 117fbc2fbf9SPekka Enberg case VIRTIO_MSI_CONFIG_VECTOR: 118*4749e795SSasha Levin ioport__write16(data, blk_device->config_vector); 11940ce993fSPekka Enberg break; 120fbc2fbf9SPekka Enberg default: 121*4749e795SSasha Levin ret = virtio_blk_pci_io_device_specific_in(blk_device, data, offset, size, count); 122fbc2fbf9SPekka Enberg }; 123fbc2fbf9SPekka Enberg 124*4749e795SSasha Levin mutex_unlock(&blk_device->mutex); 1250528c2a7SPekka Enberg 1260528c2a7SPekka Enberg return ret; 127fbc2fbf9SPekka Enberg } 128fbc2fbf9SPekka Enberg 129*4749e795SSasha Levin static bool virtio_blk_do_io_request(struct kvm *self, 130*4749e795SSasha Levin struct blk_device *blk_device, 131*4749e795SSasha Levin struct virt_queue *queue) 1324155ba8cSPekka Enberg { 13345e47970SAsias He struct iovec iov[VIRTIO_BLK_QUEUE_SIZE]; 1344155ba8cSPekka Enberg struct virtio_blk_outhdr *req; 13570b53f25SSasha Levin ssize_t block_cnt = -1; 13645e47970SAsias He uint16_t out, in, head; 1374155ba8cSPekka Enberg uint8_t *status; 1384155ba8cSPekka Enberg 13945e47970SAsias He head = virt_queue__get_iov(queue, iov, &out, &in, self); 1404155ba8cSPekka Enberg 14145e47970SAsias He /* head */ 14245e47970SAsias He req = iov[0].iov_base; 14303110ff3SAsias He 144258dd093SPekka Enberg switch (req->type) { 14503110ff3SAsias He case VIRTIO_BLK_T_IN: 146*4749e795SSasha Levin block_cnt = disk_image__read_sector_iov(blk_device->disk, req->sector, iov + 1, in + out - 2); 14770b53f25SSasha Levin 148258dd093SPekka Enberg break; 14903110ff3SAsias He case VIRTIO_BLK_T_OUT: 150*4749e795SSasha Levin block_cnt = disk_image__write_sector_iov(blk_device->disk, req->sector, iov + 1, in + out - 2); 15170b53f25SSasha Levin 152258dd093SPekka Enberg break; 15370b53f25SSasha Levin 154258dd093SPekka Enberg default: 1554155ba8cSPekka Enberg warning("request type %d", req->type); 15670b53f25SSasha Levin block_cnt = -1; 15703110ff3SAsias He } 15803110ff3SAsias He 15945e47970SAsias He /* status */ 16045e47970SAsias He status = iov[out + in - 1].iov_base; 16170b53f25SSasha Levin *status = (block_cnt < 0) ? VIRTIO_BLK_S_IOERR : VIRTIO_BLK_S_OK; 16203110ff3SAsias He 16345e47970SAsias He virt_queue__set_used_elem(queue, head, block_cnt); 1644155ba8cSPekka Enberg 1654155ba8cSPekka Enberg return true; 1664155ba8cSPekka Enberg } 1674155ba8cSPekka Enberg 168fb0957f2SSasha Levin static void virtio_blk_do_io(struct kvm *kvm, void *param) 16945e47970SAsias He { 170*4749e795SSasha Levin struct blk_device_job *job = param; 171*4749e795SSasha Levin struct virt_queue *vq = job->vq; 172*4749e795SSasha Levin struct blk_device *blk_device = job->blk_device; 17345e47970SAsias He 1740ea58e5bSPekka Enberg while (virt_queue__available(vq)) 175*4749e795SSasha Levin virtio_blk_do_io_request(kvm, blk_device, vq); 1760ea58e5bSPekka Enberg 177*4749e795SSasha Levin kvm__irq_line(kvm, VIRTIO_BLK_IRQ + blk_device->idx, 1); 1784baf6f73SSasha Levin } 1790528c2a7SPekka Enberg 180416b2c2dSAsias He static bool virtio_blk_pci_io_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count) 181fbc2fbf9SPekka Enberg { 182*4749e795SSasha Levin u16 offset, dev_idx; 1830528c2a7SPekka Enberg bool ret = true; 184*4749e795SSasha Levin struct blk_device *blk_device; 1850528c2a7SPekka Enberg 186*4749e795SSasha Levin virtio_blk_port2dev(port, IOPORT_VIRTIO_BLK, IOPORT_VIRTIO_BLK_SIZE, 187*4749e795SSasha Levin &dev_idx, &offset); 188fbc2fbf9SPekka Enberg 189*4749e795SSasha Levin blk_device = blk_devices[dev_idx]; 190*4749e795SSasha Levin 191*4749e795SSasha Levin mutex_lock(&blk_device->mutex); 192fbc2fbf9SPekka Enberg 193fbc2fbf9SPekka Enberg switch (offset) { 194fbc2fbf9SPekka Enberg case VIRTIO_PCI_GUEST_FEATURES: 195*4749e795SSasha Levin blk_device->guest_features = ioport__read32(data); 196fbc2fbf9SPekka Enberg break; 19710eca11dSPekka Enberg case VIRTIO_PCI_QUEUE_PFN: { 19810eca11dSPekka Enberg struct virt_queue *queue; 199*4749e795SSasha Levin struct blk_device_job *job; 20010eca11dSPekka Enberg void *p; 20110eca11dSPekka Enberg 202*4749e795SSasha Levin job = &blk_device->jobs[blk_device->queue_selector]; 20310eca11dSPekka Enberg 204*4749e795SSasha Levin queue = &blk_device->vqs[blk_device->queue_selector]; 20510eca11dSPekka Enberg queue->pfn = ioport__read32(data); 20610eca11dSPekka Enberg p = guest_flat_to_host(self, queue->pfn << 12); 20710eca11dSPekka Enberg 20810eca11dSPekka Enberg vring_init(&queue->vring, VIRTIO_BLK_QUEUE_SIZE, p, 4096); 20910eca11dSPekka Enberg 210*4749e795SSasha Levin *job = (struct blk_device_job) { 211*4749e795SSasha Levin .vq = queue, 212*4749e795SSasha Levin .blk_device = blk_device, 213*4749e795SSasha Levin }; 214*4749e795SSasha Levin 215*4749e795SSasha Levin job->job_id = thread_pool__add_job(self, virtio_blk_do_io, job); 216fb0957f2SSasha Levin 2177e61688eSPekka Enberg break; 21810eca11dSPekka Enberg } 219fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_SEL: 220*4749e795SSasha Levin blk_device->queue_selector = ioport__read16(data); 2217e61688eSPekka Enberg break; 22210eca11dSPekka Enberg case VIRTIO_PCI_QUEUE_NOTIFY: { 22310eca11dSPekka Enberg uint16_t queue_index; 22410eca11dSPekka Enberg queue_index = ioport__read16(data); 225*4749e795SSasha Levin thread_pool__do_job(blk_device->jobs[queue_index].job_id); 2267e61688eSPekka Enberg break; 22710eca11dSPekka Enberg } 228fbc2fbf9SPekka Enberg case VIRTIO_PCI_STATUS: 229*4749e795SSasha Levin blk_device->status = ioport__read8(data); 230fbc2fbf9SPekka Enberg break; 231fbc2fbf9SPekka Enberg case VIRTIO_MSI_CONFIG_VECTOR: 232*4749e795SSasha Levin blk_device->config_vector = VIRTIO_MSI_NO_VECTOR; 23340ce993fSPekka Enberg break; 234fbc2fbf9SPekka Enberg case VIRTIO_MSI_QUEUE_VECTOR: 23540ce993fSPekka Enberg break; 236fbc2fbf9SPekka Enberg default: 2370528c2a7SPekka Enberg ret = false; 238fbc2fbf9SPekka Enberg }; 239fbc2fbf9SPekka Enberg 240*4749e795SSasha Levin mutex_unlock(&blk_device->mutex); 2410528c2a7SPekka Enberg 2420528c2a7SPekka Enberg return ret; 243fbc2fbf9SPekka Enberg } 244fbc2fbf9SPekka Enberg 245416b2c2dSAsias He static struct ioport_operations virtio_blk_io_ops = { 246416b2c2dSAsias He .io_in = virtio_blk_pci_io_in, 247416b2c2dSAsias He .io_out = virtio_blk_pci_io_out, 248fbc2fbf9SPekka Enberg }; 249fbc2fbf9SPekka Enberg 250b30d05adSPekka Enberg #define PCI_VENDOR_ID_REDHAT_QUMRANET 0x1af4 251b30d05adSPekka Enberg #define PCI_DEVICE_ID_VIRTIO_BLK 0x1001 252b30d05adSPekka Enberg #define PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET 0x1af4 253b30d05adSPekka Enberg #define PCI_SUBSYSTEM_ID_VIRTIO_BLK 0x0002 254*4749e795SSasha Levin #define PCI_VIRTIO_BLK_DEVNUM 10 255b30d05adSPekka Enberg 256*4749e795SSasha Levin static int virtio_blk_find_empty_dev(void) 257*4749e795SSasha Levin { 258*4749e795SSasha Levin int i; 259*4749e795SSasha Levin 260*4749e795SSasha Levin for (i = 0; i < VIRTIO_BLK_MAX_DEV; i++) { 261*4749e795SSasha Levin if (blk_devices[i] == NULL) 262*4749e795SSasha Levin return i; 263*4749e795SSasha Levin } 264*4749e795SSasha Levin 265*4749e795SSasha Levin return -1; 266*4749e795SSasha Levin } 267*4749e795SSasha Levin 268*4749e795SSasha Levin void virtio_blk__init(struct kvm *self, struct disk_image *disk) 269*4749e795SSasha Levin { 270*4749e795SSasha Levin int new_dev_idx; 271*4749e795SSasha Levin u16 blk_dev_base_addr; 272*4749e795SSasha Levin struct blk_device *blk_device; 273*4749e795SSasha Levin 274*4749e795SSasha Levin if (!disk) 275*4749e795SSasha Levin return; 276*4749e795SSasha Levin 277*4749e795SSasha Levin new_dev_idx = virtio_blk_find_empty_dev(); 278*4749e795SSasha Levin if (new_dev_idx < 0) 279*4749e795SSasha Levin die("Could not find an empty block device slot"); 280*4749e795SSasha Levin 281*4749e795SSasha Levin blk_devices[new_dev_idx] = calloc(1, sizeof(struct blk_device)); 282*4749e795SSasha Levin if (blk_devices[new_dev_idx] == NULL) 283*4749e795SSasha Levin die("Failed allocating blk_device"); 284*4749e795SSasha Levin 285*4749e795SSasha Levin blk_device = blk_devices[new_dev_idx]; 286*4749e795SSasha Levin blk_dev_base_addr = IOPORT_VIRTIO_BLK + new_dev_idx * IOPORT_VIRTIO_BLK_SIZE; 287*4749e795SSasha Levin 288*4749e795SSasha Levin *blk_device = (struct blk_device) { 289*4749e795SSasha Levin .mutex = PTHREAD_MUTEX_INITIALIZER, 290*4749e795SSasha Levin .disk = disk, 291*4749e795SSasha Levin .idx = new_dev_idx, 292*4749e795SSasha Levin .blk_config = (struct virtio_blk_config) { 293*4749e795SSasha Levin .capacity = disk->size / SECTOR_SIZE, 294*4749e795SSasha Levin }, 295*4749e795SSasha Levin .pci_device = (struct pci_device_header) { 296b30d05adSPekka Enberg .vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET, 297b30d05adSPekka Enberg .device_id = PCI_DEVICE_ID_VIRTIO_BLK, 298b30d05adSPekka Enberg .header_type = PCI_HEADER_TYPE_NORMAL, 299b30d05adSPekka Enberg .revision_id = 0, 300b30d05adSPekka Enberg .class = 0x010000, 301b30d05adSPekka Enberg .subsys_vendor_id = PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET, 302b30d05adSPekka Enberg .subsys_id = PCI_SUBSYSTEM_ID_VIRTIO_BLK, 303*4749e795SSasha Levin .bar[0] = blk_dev_base_addr | PCI_BASE_ADDRESS_SPACE_IO, 304bc0363c8SCyrill Gorcunov .irq_pin = VIRTIO_BLK_PIN, 305*4749e795SSasha Levin .irq_line = VIRTIO_BLK_IRQ + new_dev_idx, 306*4749e795SSasha Levin }, 307b30d05adSPekka Enberg }; 308b30d05adSPekka Enberg 309*4749e795SSasha Levin pci__register(&blk_device->pci_device, PCI_VIRTIO_BLK_DEVNUM + new_dev_idx); 310f05bbe8dSAsias He 311*4749e795SSasha Levin ioport__register(blk_dev_base_addr, &virtio_blk_io_ops, IOPORT_VIRTIO_BLK_SIZE); 312b30d05adSPekka Enberg } 313