1b30d05adSPekka Enberg #include "kvm/blk-virtio.h" 2b30d05adSPekka Enberg 3c435b91dSPekka Enberg #include "kvm/virtio_blk.h" 4984b7ae0SPekka Enberg #include "kvm/virtio_pci.h" 5b30d05adSPekka Enberg #include "kvm/ioport.h" 68b1ff07eSPekka Enberg #include "kvm/kvm.h" 7b30d05adSPekka Enberg #include "kvm/pci.h" 8b30d05adSPekka Enberg 98b1ff07eSPekka Enberg #define VIRTIO_BLK_IRQ 14 10984b7ae0SPekka Enberg 11fbc2fbf9SPekka Enberg struct device { 12c435b91dSPekka Enberg uint32_t host_features; 13fbc2fbf9SPekka Enberg uint32_t guest_features; 14fbc2fbf9SPekka Enberg uint8_t status; 15fbc2fbf9SPekka Enberg }; 16fbc2fbf9SPekka Enberg 17c435b91dSPekka Enberg static struct device device = { 1898790f28SPekka Enberg .host_features = (1UL << VIRTIO_BLK_F_GEOMETRY) 19c435b91dSPekka Enberg | (1UL << VIRTIO_BLK_F_TOPOLOGY) 20c435b91dSPekka Enberg | (1UL << VIRTIO_BLK_F_BLK_SIZE), 21c435b91dSPekka Enberg }; 22fbc2fbf9SPekka Enberg 23fbc2fbf9SPekka Enberg static bool blk_virtio_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count) 24fbc2fbf9SPekka Enberg { 25fbc2fbf9SPekka Enberg unsigned long offset; 26fbc2fbf9SPekka Enberg 27fbc2fbf9SPekka Enberg offset = port - IOPORT_VIRTIO; 28fbc2fbf9SPekka Enberg 298b1ff07eSPekka Enberg /* XXX: Let virtio block device handle this */ 308b1ff07eSPekka Enberg if (offset >= VIRTIO_PCI_CONFIG_NOMSI) 318b1ff07eSPekka Enberg return true; 328b1ff07eSPekka Enberg 33fbc2fbf9SPekka Enberg switch (offset) { 34fbc2fbf9SPekka Enberg case VIRTIO_PCI_HOST_FEATURES: 35c435b91dSPekka Enberg ioport__write32(data, device.host_features); 36fbc2fbf9SPekka Enberg break; 37fbc2fbf9SPekka Enberg case VIRTIO_PCI_GUEST_FEATURES: 388b1ff07eSPekka Enberg return false; 39fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_PFN: 408b1ff07eSPekka Enberg ioport__write32(data, 0x00); 418b1ff07eSPekka Enberg break; 42fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_NUM: 438b1ff07eSPekka Enberg ioport__write16(data, 0x10); 448b1ff07eSPekka Enberg break; 45fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_SEL: 46fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_NOTIFY: 47fbc2fbf9SPekka Enberg return false; 48fbc2fbf9SPekka Enberg case VIRTIO_PCI_STATUS: 49fbc2fbf9SPekka Enberg ioport__write8(data, device.status); 50fbc2fbf9SPekka Enberg break; 51fbc2fbf9SPekka Enberg case VIRTIO_PCI_ISR: 528b1ff07eSPekka Enberg ioport__write8(data, 0x1); 538b1ff07eSPekka Enberg kvm__irq_line(self, VIRTIO_BLK_IRQ, 0); 547e61688eSPekka Enberg break; 55fbc2fbf9SPekka Enberg case VIRTIO_MSI_CONFIG_VECTOR: 56fbc2fbf9SPekka Enberg default: 57fbc2fbf9SPekka Enberg return false; 58fbc2fbf9SPekka Enberg }; 59fbc2fbf9SPekka Enberg 60fbc2fbf9SPekka Enberg return true; 61fbc2fbf9SPekka Enberg } 62fbc2fbf9SPekka Enberg 63fbc2fbf9SPekka Enberg static bool blk_virtio_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count) 64fbc2fbf9SPekka Enberg { 65fbc2fbf9SPekka Enberg unsigned long offset; 66fbc2fbf9SPekka Enberg 67fbc2fbf9SPekka Enberg offset = port - IOPORT_VIRTIO; 68fbc2fbf9SPekka Enberg 698b1ff07eSPekka Enberg /* XXX: Let virtio block device handle this */ 708b1ff07eSPekka Enberg if (offset >= VIRTIO_PCI_CONFIG_NOMSI) 718b1ff07eSPekka Enberg return true; 728b1ff07eSPekka Enberg 73fbc2fbf9SPekka Enberg switch (offset) { 74fbc2fbf9SPekka Enberg case VIRTIO_PCI_GUEST_FEATURES: 75fbc2fbf9SPekka Enberg device.guest_features = ioport__read32(data); 76fbc2fbf9SPekka Enberg break; 77fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_PFN: 787e61688eSPekka Enberg break; 79fbc2fbf9SPekka Enberg case VIRTIO_PCI_QUEUE_SEL: 807e61688eSPekka Enberg break; 817e61688eSPekka Enberg case VIRTIO_PCI_QUEUE_NOTIFY: 828b1ff07eSPekka Enberg kvm__irq_line(self, VIRTIO_BLK_IRQ, 1); 837e61688eSPekka Enberg break; 84fbc2fbf9SPekka Enberg case VIRTIO_PCI_STATUS: 85fbc2fbf9SPekka Enberg device.status = ioport__read8(data); 86fbc2fbf9SPekka Enberg break; 87fbc2fbf9SPekka Enberg case VIRTIO_MSI_CONFIG_VECTOR: 88fbc2fbf9SPekka Enberg case VIRTIO_MSI_QUEUE_VECTOR: 89fbc2fbf9SPekka Enberg default: 90fbc2fbf9SPekka Enberg return false; 91fbc2fbf9SPekka Enberg }; 92fbc2fbf9SPekka Enberg 93fbc2fbf9SPekka Enberg return true; 94fbc2fbf9SPekka Enberg } 95fbc2fbf9SPekka Enberg 96fbc2fbf9SPekka Enberg static struct ioport_operations blk_virtio_io_ops = { 97fbc2fbf9SPekka Enberg .io_in = blk_virtio_in, 98fbc2fbf9SPekka Enberg .io_out = blk_virtio_out, 99fbc2fbf9SPekka Enberg }; 100fbc2fbf9SPekka Enberg 101b30d05adSPekka Enberg #define PCI_VENDOR_ID_REDHAT_QUMRANET 0x1af4 102b30d05adSPekka Enberg #define PCI_DEVICE_ID_VIRTIO_BLK 0x1001 103b30d05adSPekka Enberg #define PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET 0x1af4 104b30d05adSPekka Enberg #define PCI_SUBSYSTEM_ID_VIRTIO_BLK 0x0002 105b30d05adSPekka Enberg 106fbc2fbf9SPekka Enberg static struct pci_device_header blk_virtio_pci_device = { 107b30d05adSPekka Enberg .vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET, 108b30d05adSPekka Enberg .device_id = PCI_DEVICE_ID_VIRTIO_BLK, 109b30d05adSPekka Enberg .header_type = PCI_HEADER_TYPE_NORMAL, 110b30d05adSPekka Enberg .revision_id = 0, 111b30d05adSPekka Enberg .class = 0x010000, 112b30d05adSPekka Enberg .subsys_vendor_id = PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET, 113b30d05adSPekka Enberg .subsys_id = PCI_SUBSYSTEM_ID_VIRTIO_BLK, 114b30d05adSPekka Enberg .bar[0] = IOPORT_VIRTIO | PCI_BASE_ADDRESS_SPACE_IO, 115*dc53a427SPekka Enberg /* XXX: Is this IRQ setup OK? */ 116*dc53a427SPekka Enberg .irq_pin = 1, 1178b1ff07eSPekka Enberg .irq_line = VIRTIO_BLK_IRQ, 118b30d05adSPekka Enberg }; 119b30d05adSPekka Enberg 120b30d05adSPekka Enberg void blk_virtio__init(void) 121b30d05adSPekka Enberg { 122fbc2fbf9SPekka Enberg pci__register(&blk_virtio_pci_device, 1); 123b30d05adSPekka Enberg 1248b1ff07eSPekka Enberg ioport__register(IOPORT_VIRTIO, &blk_virtio_io_ops, 256); 125b30d05adSPekka Enberg } 126