1416b2c2dSAsias He #include "kvm/virtio-console.h" 2416b2c2dSAsias He #include "kvm/virtio-pci.h" 331638bcaSCyrill Gorcunov #include "kvm/virtio-pci-dev.h" 49bd8ac5cSAsias He #include "kvm/disk-image.h" 539d6af07SAsias He #include "kvm/virtio.h" 69bd8ac5cSAsias He #include "kvm/ioport.h" 79bd8ac5cSAsias He #include "kvm/util.h" 89bd8ac5cSAsias He #include "kvm/term.h" 97512639bSAsias He #include "kvm/mutex.h" 109bd8ac5cSAsias He #include "kvm/kvm.h" 119bd8ac5cSAsias He #include "kvm/pci.h" 12b6638c22SSasha Levin #include "kvm/threadpool.h" 13*2449f6e3SSasha Levin #include "kvm/irq.h" 149bd8ac5cSAsias He 159bd8ac5cSAsias He #include <linux/virtio_console.h> 169bd8ac5cSAsias He #include <linux/virtio_ring.h> 179bd8ac5cSAsias He #include <linux/virtio_blk.h> 189bd8ac5cSAsias He 199bd8ac5cSAsias He #include <sys/uio.h> 209bd8ac5cSAsias He #include <sys/types.h> 219bd8ac5cSAsias He #include <sys/stat.h> 229bd8ac5cSAsias He #include <termios.h> 239bd8ac5cSAsias He #include <assert.h> 249bd8ac5cSAsias He #include <unistd.h> 259bd8ac5cSAsias He #include <fcntl.h> 269bd8ac5cSAsias He 279bd8ac5cSAsias He #define VIRTIO_CONSOLE_QUEUE_SIZE 128 289bd8ac5cSAsias He #define VIRTIO_CONSOLE_NUM_QUEUES 2 299bd8ac5cSAsias He #define VIRTIO_CONSOLE_RX_QUEUE 0 309bd8ac5cSAsias He #define VIRTIO_CONSOLE_TX_QUEUE 1 319bd8ac5cSAsias He 32*2449f6e3SSasha Levin static struct pci_device_header virtio_console_pci_device = { 33*2449f6e3SSasha Levin .vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET, 34*2449f6e3SSasha Levin .device_id = PCI_DEVICE_ID_VIRTIO_CONSOLE, 35*2449f6e3SSasha Levin .header_type = PCI_HEADER_TYPE_NORMAL, 36*2449f6e3SSasha Levin .revision_id = 0, 37*2449f6e3SSasha Levin .class = 0x078000, 38*2449f6e3SSasha Levin .subsys_vendor_id = PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET, 39*2449f6e3SSasha Levin .subsys_id = PCI_SUBSYSTEM_ID_VIRTIO_CONSOLE, 40*2449f6e3SSasha Levin .bar[0] = IOPORT_VIRTIO_CONSOLE | PCI_BASE_ADDRESS_SPACE_IO, 41*2449f6e3SSasha Levin }; 42*2449f6e3SSasha Levin 43a643306eSSasha Levin struct con_dev { 447512639bSAsias He pthread_mutex_t mutex; 457512639bSAsias He 469bd8ac5cSAsias He struct virt_queue vqs[VIRTIO_CONSOLE_NUM_QUEUES]; 479bd8ac5cSAsias He struct virtio_console_config console_config; 483fdf659dSSasha Levin u32 host_features; 493fdf659dSSasha Levin u32 guest_features; 503fdf659dSSasha Levin u16 config_vector; 513fdf659dSSasha Levin u8 status; 523fdf659dSSasha Levin u16 queue_selector; 53b6638c22SSasha Levin 54b6638c22SSasha Levin void *jobs[VIRTIO_CONSOLE_NUM_QUEUES]; 559bd8ac5cSAsias He }; 569bd8ac5cSAsias He 57a643306eSSasha Levin static struct con_dev cdev = { 587512639bSAsias He .mutex = PTHREAD_MUTEX_INITIALIZER, 597512639bSAsias He 609bd8ac5cSAsias He .console_config = { 619bd8ac5cSAsias He .cols = 80, 629bd8ac5cSAsias He .rows = 24, 639bd8ac5cSAsias He .max_nr_ports = 1, 649bd8ac5cSAsias He }, 659bd8ac5cSAsias He 669bd8ac5cSAsias He .host_features = 0, 679bd8ac5cSAsias He }; 689bd8ac5cSAsias He 699bd8ac5cSAsias He /* 709bd8ac5cSAsias He * Interrupts are injected for hvc0 only. 719bd8ac5cSAsias He */ 72b6638c22SSasha Levin static void virtio_console__inject_interrupt_callback(struct kvm *self, void *param) 739bd8ac5cSAsias He { 749bd8ac5cSAsias He struct iovec iov[VIRTIO_CONSOLE_QUEUE_SIZE]; 759bd8ac5cSAsias He struct virt_queue *vq; 763fdf659dSSasha Levin u16 out, in; 773fdf659dSSasha Levin u16 head; 789bd8ac5cSAsias He int len; 799bd8ac5cSAsias He 80a643306eSSasha Levin mutex_lock(&cdev.mutex); 817512639bSAsias He 82b6638c22SSasha Levin vq = param; 839bd8ac5cSAsias He 849bd8ac5cSAsias He if (term_readable(CONSOLE_VIRTIO) && virt_queue__available(vq)) { 859bd8ac5cSAsias He head = virt_queue__get_iov(vq, iov, &out, &in, self); 869bd8ac5cSAsias He len = term_getc_iov(CONSOLE_VIRTIO, iov, in); 879bd8ac5cSAsias He virt_queue__set_used_elem(vq, head, len); 88*2449f6e3SSasha Levin kvm__irq_line(self, virtio_console_pci_device.irq_line, 1); 899bd8ac5cSAsias He } 907512639bSAsias He 91a643306eSSasha Levin mutex_unlock(&cdev.mutex); 929bd8ac5cSAsias He } 939bd8ac5cSAsias He 94b6638c22SSasha Levin void virtio_console__inject_interrupt(struct kvm *self) 95b6638c22SSasha Levin { 96a643306eSSasha Levin thread_pool__do_job(cdev.jobs[VIRTIO_CONSOLE_RX_QUEUE]); 97b6638c22SSasha Levin } 98b6638c22SSasha Levin 993fdf659dSSasha Levin static bool virtio_console_pci_io_device_specific_in(void *data, unsigned long offset, int size, u32 count) 1009bd8ac5cSAsias He { 101a643306eSSasha Levin u8 *config_space = (u8 *) &cdev.console_config; 1029bd8ac5cSAsias He 1039bd8ac5cSAsias He if (size != 1 || count != 1) 1049bd8ac5cSAsias He return false; 1059bd8ac5cSAsias He 1069bd8ac5cSAsias He if ((offset - VIRTIO_PCI_CONFIG_NOMSI) > sizeof(struct virtio_console_config)) 1079bd8ac5cSAsias He error("config offset is too big: %li", offset - VIRTIO_PCI_CONFIG_NOMSI); 1089bd8ac5cSAsias He 1099bd8ac5cSAsias He ioport__write8(data, config_space[offset - VIRTIO_PCI_CONFIG_NOMSI]); 1109bd8ac5cSAsias He 1119bd8ac5cSAsias He return true; 1129bd8ac5cSAsias He } 1139bd8ac5cSAsias He 1143fdf659dSSasha Levin static bool virtio_console_pci_io_in(struct kvm *self, u16 port, void *data, int size, u32 count) 1159bd8ac5cSAsias He { 1169bd8ac5cSAsias He unsigned long offset = port - IOPORT_VIRTIO_CONSOLE; 1177512639bSAsias He bool ret = true; 1187512639bSAsias He 119a643306eSSasha Levin mutex_lock(&cdev.mutex); 1209bd8ac5cSAsias He 1219bd8ac5cSAsias He switch (offset) { 1229bd8ac5cSAsias He case VIRTIO_PCI_HOST_FEATURES: 123a643306eSSasha Levin ioport__write32(data, cdev.host_features); 1249bd8ac5cSAsias He break; 1259bd8ac5cSAsias He case VIRTIO_PCI_GUEST_FEATURES: 1267512639bSAsias He ret = false; 1277512639bSAsias He break; 1289bd8ac5cSAsias He case VIRTIO_PCI_QUEUE_PFN: 129a643306eSSasha Levin ioport__write32(data, cdev.vqs[cdev.queue_selector].pfn); 1309bd8ac5cSAsias He break; 1319bd8ac5cSAsias He case VIRTIO_PCI_QUEUE_NUM: 1329bd8ac5cSAsias He ioport__write16(data, VIRTIO_CONSOLE_QUEUE_SIZE); 1339bd8ac5cSAsias He break; 1349bd8ac5cSAsias He case VIRTIO_PCI_QUEUE_SEL: 1359bd8ac5cSAsias He case VIRTIO_PCI_QUEUE_NOTIFY: 1367512639bSAsias He ret = false; 1377512639bSAsias He break; 1389bd8ac5cSAsias He case VIRTIO_PCI_STATUS: 139a643306eSSasha Levin ioport__write8(data, cdev.status); 1409bd8ac5cSAsias He break; 1419bd8ac5cSAsias He case VIRTIO_PCI_ISR: 1429bd8ac5cSAsias He ioport__write8(data, 0x1); 143*2449f6e3SSasha Levin kvm__irq_line(self, virtio_console_pci_device.irq_line, 0); 1449bd8ac5cSAsias He break; 1459bd8ac5cSAsias He case VIRTIO_MSI_CONFIG_VECTOR: 146a643306eSSasha Levin ioport__write16(data, cdev.config_vector); 1479bd8ac5cSAsias He break; 1489bd8ac5cSAsias He default: 1497512639bSAsias He ret = virtio_console_pci_io_device_specific_in(data, offset, size, count); 1509bd8ac5cSAsias He }; 1519bd8ac5cSAsias He 152a643306eSSasha Levin mutex_unlock(&cdev.mutex); 1537512639bSAsias He 1547512639bSAsias He return ret; 1559bd8ac5cSAsias He } 1569bd8ac5cSAsias He 157b6638c22SSasha Levin static void virtio_console_handle_callback(struct kvm *self, void *param) 1589bd8ac5cSAsias He { 1599bd8ac5cSAsias He struct iovec iov[VIRTIO_CONSOLE_QUEUE_SIZE]; 1609bd8ac5cSAsias He struct virt_queue *vq; 1613fdf659dSSasha Levin u16 out, in; 1623fdf659dSSasha Levin u16 head; 1633fdf659dSSasha Levin u32 len; 1649bd8ac5cSAsias He 165b6638c22SSasha Levin vq = param; 1669bd8ac5cSAsias He 1679bd8ac5cSAsias He while (virt_queue__available(vq)) { 1689bd8ac5cSAsias He head = virt_queue__get_iov(vq, iov, &out, &in, self); 1699bd8ac5cSAsias He len = term_putc_iov(CONSOLE_VIRTIO, iov, out); 1709bd8ac5cSAsias He virt_queue__set_used_elem(vq, head, len); 1719bd8ac5cSAsias He } 1729bd8ac5cSAsias He 173*2449f6e3SSasha Levin kvm__irq_line(self, virtio_console_pci_device.irq_line, 1); 1749bd8ac5cSAsias He } 1759bd8ac5cSAsias He 1763fdf659dSSasha Levin static bool virtio_console_pci_io_out(struct kvm *self, u16 port, void *data, int size, u32 count) 1779bd8ac5cSAsias He { 1789bd8ac5cSAsias He unsigned long offset = port - IOPORT_VIRTIO_CONSOLE; 1797512639bSAsias He bool ret = true; 1809bd8ac5cSAsias He 181a643306eSSasha Levin mutex_lock(&cdev.mutex); 182e147d838SAsias He 1839bd8ac5cSAsias He switch (offset) { 1849bd8ac5cSAsias He case VIRTIO_PCI_GUEST_FEATURES: 185a643306eSSasha Levin cdev.guest_features = ioport__read32(data); 1869bd8ac5cSAsias He break; 1879bd8ac5cSAsias He case VIRTIO_PCI_QUEUE_PFN: { 1889bd8ac5cSAsias He struct virt_queue *queue; 1899bd8ac5cSAsias He void *p; 1909bd8ac5cSAsias He 191a643306eSSasha Levin assert(cdev.queue_selector < VIRTIO_CONSOLE_NUM_QUEUES); 1929bd8ac5cSAsias He 193a643306eSSasha Levin queue = &cdev.vqs[cdev.queue_selector]; 1949bd8ac5cSAsias He queue->pfn = ioport__read32(data); 1959bd8ac5cSAsias He p = guest_flat_to_host(self, queue->pfn << 12); 1969bd8ac5cSAsias He 1979bd8ac5cSAsias He vring_init(&queue->vring, VIRTIO_CONSOLE_QUEUE_SIZE, p, 4096); 1989bd8ac5cSAsias He 199a643306eSSasha Levin if (cdev.queue_selector == VIRTIO_CONSOLE_TX_QUEUE) 200407475bfSPekka Enberg cdev.jobs[cdev.queue_selector] = thread_pool__add_job(self, virtio_console_handle_callback, queue); 201a643306eSSasha Levin else if (cdev.queue_selector == VIRTIO_CONSOLE_RX_QUEUE) 202407475bfSPekka Enberg cdev.jobs[cdev.queue_selector] = thread_pool__add_job(self, virtio_console__inject_interrupt_callback, queue); 203b6638c22SSasha Levin 2049bd8ac5cSAsias He break; 2059bd8ac5cSAsias He } 2069bd8ac5cSAsias He case VIRTIO_PCI_QUEUE_SEL: 207a643306eSSasha Levin cdev.queue_selector = ioport__read16(data); 2089bd8ac5cSAsias He break; 2099bd8ac5cSAsias He case VIRTIO_PCI_QUEUE_NOTIFY: { 210a643306eSSasha Levin u16 queue_index = ioport__read16(data); 211a643306eSSasha Levin thread_pool__do_job(cdev.jobs[queue_index]); 2129bd8ac5cSAsias He break; 2139bd8ac5cSAsias He } 2149bd8ac5cSAsias He case VIRTIO_PCI_STATUS: 215a643306eSSasha Levin cdev.status = ioport__read8(data); 2169bd8ac5cSAsias He break; 2179bd8ac5cSAsias He case VIRTIO_MSI_CONFIG_VECTOR: 218a643306eSSasha Levin cdev.config_vector = VIRTIO_MSI_NO_VECTOR; 2199bd8ac5cSAsias He break; 2209bd8ac5cSAsias He case VIRTIO_MSI_QUEUE_VECTOR: 2219bd8ac5cSAsias He break; 2229bd8ac5cSAsias He default: 2237512639bSAsias He ret = false; 224407475bfSPekka Enberg break; 2259bd8ac5cSAsias He }; 2269bd8ac5cSAsias He 227a643306eSSasha Levin mutex_unlock(&cdev.mutex); 228407475bfSPekka Enberg 2297512639bSAsias He return ret; 2309bd8ac5cSAsias He } 2319bd8ac5cSAsias He 2329bd8ac5cSAsias He static struct ioport_operations virtio_console_io_ops = { 2339bd8ac5cSAsias He .io_in = virtio_console_pci_io_in, 2349bd8ac5cSAsias He .io_out = virtio_console_pci_io_out, 2359bd8ac5cSAsias He }; 2369bd8ac5cSAsias He 2379bd8ac5cSAsias He void virtio_console__init(struct kvm *self) 2389bd8ac5cSAsias He { 239*2449f6e3SSasha Levin u8 dev, line, pin; 240*2449f6e3SSasha Levin 241*2449f6e3SSasha Levin if (irq__register_device(PCI_DEVICE_ID_VIRTIO_CONSOLE, &dev, &pin, &line) < 0) 242*2449f6e3SSasha Levin return; 243*2449f6e3SSasha Levin 244*2449f6e3SSasha Levin virtio_console_pci_device.irq_pin = pin; 245*2449f6e3SSasha Levin virtio_console_pci_device.irq_line = line; 246*2449f6e3SSasha Levin pci__register(&virtio_console_pci_device, dev); 2479bd8ac5cSAsias He ioport__register(IOPORT_VIRTIO_CONSOLE, &virtio_console_io_ops, IOPORT_VIRTIO_CONSOLE_SIZE); 2489bd8ac5cSAsias He } 249