1416b2c2dSAsias He #include "kvm/virtio-console.h" 231638bcaSCyrill Gorcunov #include "kvm/virtio-pci-dev.h" 39bd8ac5cSAsias He #include "kvm/disk-image.h" 439d6af07SAsias He #include "kvm/virtio.h" 59bd8ac5cSAsias He #include "kvm/ioport.h" 69bd8ac5cSAsias He #include "kvm/util.h" 79bd8ac5cSAsias He #include "kvm/term.h" 87512639bSAsias He #include "kvm/mutex.h" 99bd8ac5cSAsias He #include "kvm/kvm.h" 109bd8ac5cSAsias He #include "kvm/pci.h" 11b6638c22SSasha Levin #include "kvm/threadpool.h" 122449f6e3SSasha Levin #include "kvm/irq.h" 13*e59b1ed0SSasha Levin #include "kvm/guest_compat.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 322449f6e3SSasha Levin static struct pci_device_header virtio_console_pci_device = { 332449f6e3SSasha Levin .vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET, 342449f6e3SSasha Levin .device_id = PCI_DEVICE_ID_VIRTIO_CONSOLE, 352449f6e3SSasha Levin .header_type = PCI_HEADER_TYPE_NORMAL, 362449f6e3SSasha Levin .revision_id = 0, 372449f6e3SSasha Levin .class = 0x078000, 382449f6e3SSasha Levin .subsys_vendor_id = PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET, 390a7ab0c6SSasha Levin .subsys_id = VIRTIO_ID_CONSOLE, 402449f6e3SSasha Levin }; 412449f6e3SSasha Levin 42a643306eSSasha Levin struct con_dev { 437512639bSAsias He pthread_mutex_t mutex; 447512639bSAsias He 459bd8ac5cSAsias He struct virt_queue vqs[VIRTIO_CONSOLE_NUM_QUEUES]; 469bd8ac5cSAsias He struct virtio_console_config console_config; 473fdf659dSSasha Levin u32 host_features; 483fdf659dSSasha Levin u32 guest_features; 493fdf659dSSasha Levin u16 config_vector; 503fdf659dSSasha Levin u8 status; 510ba84a57SAsias He u8 isr; 523fdf659dSSasha Levin u16 queue_selector; 534b385ac4SSasha Levin u16 base_addr; 54*e59b1ed0SSasha Levin int compat_id; 55b6638c22SSasha Levin 56df0c7f57SSasha Levin struct thread_pool__job jobs[VIRTIO_CONSOLE_NUM_QUEUES]; 579bd8ac5cSAsias He }; 589bd8ac5cSAsias He 59a643306eSSasha Levin static struct con_dev cdev = { 607512639bSAsias He .mutex = PTHREAD_MUTEX_INITIALIZER, 617512639bSAsias He 629bd8ac5cSAsias He .console_config = { 639bd8ac5cSAsias He .cols = 80, 649bd8ac5cSAsias He .rows = 24, 659bd8ac5cSAsias He .max_nr_ports = 1, 669bd8ac5cSAsias He }, 679bd8ac5cSAsias He 689bd8ac5cSAsias He .host_features = 0, 699bd8ac5cSAsias He }; 709bd8ac5cSAsias He 719bd8ac5cSAsias He /* 729bd8ac5cSAsias He * Interrupts are injected for hvc0 only. 739bd8ac5cSAsias He */ 7443835ac9SSasha Levin static void virtio_console__inject_interrupt_callback(struct kvm *kvm, void *param) 759bd8ac5cSAsias He { 769bd8ac5cSAsias He struct iovec iov[VIRTIO_CONSOLE_QUEUE_SIZE]; 779bd8ac5cSAsias He struct virt_queue *vq; 783fdf659dSSasha Levin u16 out, in; 793fdf659dSSasha Levin u16 head; 809bd8ac5cSAsias He int len; 819bd8ac5cSAsias He 82a643306eSSasha Levin mutex_lock(&cdev.mutex); 837512639bSAsias He 84b6638c22SSasha Levin vq = param; 859bd8ac5cSAsias He 869bd8ac5cSAsias He if (term_readable(CONSOLE_VIRTIO) && virt_queue__available(vq)) { 8743835ac9SSasha Levin head = virt_queue__get_iov(vq, iov, &out, &in, kvm); 889bd8ac5cSAsias He len = term_getc_iov(CONSOLE_VIRTIO, iov, in); 899bd8ac5cSAsias He virt_queue__set_used_elem(vq, head, len); 9043835ac9SSasha Levin virt_queue__trigger_irq(vq, virtio_console_pci_device.irq_line, &cdev.isr, kvm); 919bd8ac5cSAsias He } 927512639bSAsias He 93a643306eSSasha Levin mutex_unlock(&cdev.mutex); 949bd8ac5cSAsias He } 959bd8ac5cSAsias He 9643835ac9SSasha Levin void virtio_console__inject_interrupt(struct kvm *kvm) 97b6638c22SSasha Levin { 98df0c7f57SSasha Levin thread_pool__do_job(&cdev.jobs[VIRTIO_CONSOLE_RX_QUEUE]); 99b6638c22SSasha Levin } 100b6638c22SSasha Levin 101c9f6a037SXiao Guangrong static bool virtio_console_pci_io_device_specific_in(void *data, unsigned long offset, int size) 1029bd8ac5cSAsias He { 103a643306eSSasha Levin u8 *config_space = (u8 *) &cdev.console_config; 1049bd8ac5cSAsias He 105c9f6a037SXiao Guangrong if (size != 1) 1069bd8ac5cSAsias He return false; 1079bd8ac5cSAsias He 108b8f43678SSasha Levin if ((offset - VIRTIO_MSI_CONFIG_VECTOR) > sizeof(struct virtio_console_config)) 1094542f276SCyrill Gorcunov pr_error("config offset is too big: %li", offset - VIRTIO_MSI_CONFIG_VECTOR); 1109bd8ac5cSAsias He 111b8f43678SSasha Levin ioport__write8(data, config_space[offset - VIRTIO_MSI_CONFIG_VECTOR]); 1129bd8ac5cSAsias He 1139bd8ac5cSAsias He return true; 1149bd8ac5cSAsias He } 1159bd8ac5cSAsias He 116c9f6a037SXiao Guangrong static bool virtio_console_pci_io_in(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size) 1179bd8ac5cSAsias He { 1184b385ac4SSasha Levin unsigned long offset = port - cdev.base_addr; 1197512639bSAsias He bool ret = true; 1207512639bSAsias He 121a643306eSSasha Levin mutex_lock(&cdev.mutex); 1229bd8ac5cSAsias He 1239bd8ac5cSAsias He switch (offset) { 1249bd8ac5cSAsias He case VIRTIO_PCI_HOST_FEATURES: 125a643306eSSasha Levin ioport__write32(data, cdev.host_features); 1269bd8ac5cSAsias He break; 1279bd8ac5cSAsias He case VIRTIO_PCI_GUEST_FEATURES: 1287512639bSAsias He ret = false; 1297512639bSAsias He break; 1309bd8ac5cSAsias He case VIRTIO_PCI_QUEUE_PFN: 131a643306eSSasha Levin ioport__write32(data, cdev.vqs[cdev.queue_selector].pfn); 1329bd8ac5cSAsias He break; 1339bd8ac5cSAsias He case VIRTIO_PCI_QUEUE_NUM: 1349bd8ac5cSAsias He ioport__write16(data, VIRTIO_CONSOLE_QUEUE_SIZE); 1359bd8ac5cSAsias He break; 1369bd8ac5cSAsias He case VIRTIO_PCI_QUEUE_SEL: 1379bd8ac5cSAsias He case VIRTIO_PCI_QUEUE_NOTIFY: 1387512639bSAsias He ret = false; 1397512639bSAsias He break; 1409bd8ac5cSAsias He case VIRTIO_PCI_STATUS: 141a643306eSSasha Levin ioport__write8(data, cdev.status); 1429bd8ac5cSAsias He break; 1439bd8ac5cSAsias He case VIRTIO_PCI_ISR: 1440ba84a57SAsias He ioport__write8(data, cdev.isr); 14543835ac9SSasha Levin kvm__irq_line(kvm, virtio_console_pci_device.irq_line, VIRTIO_IRQ_LOW); 1460ba84a57SAsias He cdev.isr = VIRTIO_IRQ_LOW; 1479bd8ac5cSAsias He break; 1489bd8ac5cSAsias He case VIRTIO_MSI_CONFIG_VECTOR: 149a643306eSSasha Levin ioport__write16(data, cdev.config_vector); 1509bd8ac5cSAsias He break; 1519bd8ac5cSAsias He default: 152c9f6a037SXiao Guangrong ret = virtio_console_pci_io_device_specific_in(data, offset, size); 1539bd8ac5cSAsias He }; 1549bd8ac5cSAsias He 155a643306eSSasha Levin mutex_unlock(&cdev.mutex); 1567512639bSAsias He 1577512639bSAsias He return ret; 1589bd8ac5cSAsias He } 1599bd8ac5cSAsias He 16043835ac9SSasha Levin static void virtio_console_handle_callback(struct kvm *kvm, void *param) 1619bd8ac5cSAsias He { 1629bd8ac5cSAsias He struct iovec iov[VIRTIO_CONSOLE_QUEUE_SIZE]; 1639bd8ac5cSAsias He struct virt_queue *vq; 1643fdf659dSSasha Levin u16 out, in; 1653fdf659dSSasha Levin u16 head; 1663fdf659dSSasha Levin u32 len; 1679bd8ac5cSAsias He 168b6638c22SSasha Levin vq = param; 1699bd8ac5cSAsias He 17020f0438aSAsias He /* 17120f0438aSAsias He * The current Linux implementation polls for the buffer 17220f0438aSAsias He * to be used, rather than waiting for an interrupt. 17320f0438aSAsias He * So there is no need to inject an interrupt for the tx path. 17420f0438aSAsias He */ 17520f0438aSAsias He 1769bd8ac5cSAsias He while (virt_queue__available(vq)) { 17743835ac9SSasha Levin head = virt_queue__get_iov(vq, iov, &out, &in, kvm); 1789bd8ac5cSAsias He len = term_putc_iov(CONSOLE_VIRTIO, iov, out); 1799bd8ac5cSAsias He virt_queue__set_used_elem(vq, head, len); 1809bd8ac5cSAsias He } 1819bd8ac5cSAsias He 1829bd8ac5cSAsias He } 1839bd8ac5cSAsias He 184c9f6a037SXiao Guangrong static bool virtio_console_pci_io_out(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size) 1859bd8ac5cSAsias He { 1864b385ac4SSasha Levin unsigned long offset = port - cdev.base_addr; 1877512639bSAsias He bool ret = true; 1889bd8ac5cSAsias He 189a643306eSSasha Levin mutex_lock(&cdev.mutex); 190e147d838SAsias He 1919bd8ac5cSAsias He switch (offset) { 1929bd8ac5cSAsias He case VIRTIO_PCI_GUEST_FEATURES: 193a643306eSSasha Levin cdev.guest_features = ioport__read32(data); 1949bd8ac5cSAsias He break; 1959bd8ac5cSAsias He case VIRTIO_PCI_QUEUE_PFN: { 1969bd8ac5cSAsias He struct virt_queue *queue; 1979bd8ac5cSAsias He void *p; 1989bd8ac5cSAsias He 199a643306eSSasha Levin assert(cdev.queue_selector < VIRTIO_CONSOLE_NUM_QUEUES); 2009bd8ac5cSAsias He 201*e59b1ed0SSasha Levin compat__remove_message(cdev.compat_id); 202*e59b1ed0SSasha Levin 203a643306eSSasha Levin queue = &cdev.vqs[cdev.queue_selector]; 2049bd8ac5cSAsias He queue->pfn = ioport__read32(data); 20543835ac9SSasha Levin p = guest_pfn_to_host(kvm, queue->pfn); 2069bd8ac5cSAsias He 207b8f43678SSasha Levin vring_init(&queue->vring, VIRTIO_CONSOLE_QUEUE_SIZE, p, VIRTIO_PCI_VRING_ALIGN); 2089bd8ac5cSAsias He 209a643306eSSasha Levin if (cdev.queue_selector == VIRTIO_CONSOLE_TX_QUEUE) 210df0c7f57SSasha Levin thread_pool__init_job(&cdev.jobs[cdev.queue_selector], kvm, virtio_console_handle_callback, queue); 211a643306eSSasha Levin else if (cdev.queue_selector == VIRTIO_CONSOLE_RX_QUEUE) 212df0c7f57SSasha Levin thread_pool__init_job(&cdev.jobs[cdev.queue_selector], kvm, virtio_console__inject_interrupt_callback, queue); 213b6638c22SSasha Levin 2149bd8ac5cSAsias He break; 2159bd8ac5cSAsias He } 2169bd8ac5cSAsias He case VIRTIO_PCI_QUEUE_SEL: 217a643306eSSasha Levin cdev.queue_selector = ioport__read16(data); 2189bd8ac5cSAsias He break; 2199bd8ac5cSAsias He case VIRTIO_PCI_QUEUE_NOTIFY: { 220a643306eSSasha Levin u16 queue_index = ioport__read16(data); 221df0c7f57SSasha Levin thread_pool__do_job(&cdev.jobs[queue_index]); 2229bd8ac5cSAsias He break; 2239bd8ac5cSAsias He } 2249bd8ac5cSAsias He case VIRTIO_PCI_STATUS: 225a643306eSSasha Levin cdev.status = ioport__read8(data); 2269bd8ac5cSAsias He break; 2279bd8ac5cSAsias He case VIRTIO_MSI_CONFIG_VECTOR: 228a643306eSSasha Levin cdev.config_vector = VIRTIO_MSI_NO_VECTOR; 2299bd8ac5cSAsias He break; 2309bd8ac5cSAsias He case VIRTIO_MSI_QUEUE_VECTOR: 2319bd8ac5cSAsias He break; 2329bd8ac5cSAsias He default: 2337512639bSAsias He ret = false; 234407475bfSPekka Enberg break; 2359bd8ac5cSAsias He }; 2369bd8ac5cSAsias He 237a643306eSSasha Levin mutex_unlock(&cdev.mutex); 238407475bfSPekka Enberg 2397512639bSAsias He return ret; 2409bd8ac5cSAsias He } 2419bd8ac5cSAsias He 2429bd8ac5cSAsias He static struct ioport_operations virtio_console_io_ops = { 2439bd8ac5cSAsias He .io_in = virtio_console_pci_io_in, 2449bd8ac5cSAsias He .io_out = virtio_console_pci_io_out, 2459bd8ac5cSAsias He }; 2469bd8ac5cSAsias He 24743835ac9SSasha Levin void virtio_console__init(struct kvm *kvm) 2489bd8ac5cSAsias He { 2492449f6e3SSasha Levin u8 dev, line, pin; 2504b385ac4SSasha Levin u16 console_base_addr; 2512449f6e3SSasha Levin 2520a7ab0c6SSasha Levin if (irq__register_device(VIRTIO_ID_CONSOLE, &dev, &pin, &line) < 0) 2532449f6e3SSasha Levin return; 2542449f6e3SSasha Levin 2552449f6e3SSasha Levin virtio_console_pci_device.irq_pin = pin; 2562449f6e3SSasha Levin virtio_console_pci_device.irq_line = line; 2574b385ac4SSasha Levin console_base_addr = ioport__register(IOPORT_EMPTY, &virtio_console_io_ops, IOPORT_SIZE, NULL); 2584b385ac4SSasha Levin virtio_console_pci_device.bar[0] = console_base_addr | PCI_BASE_ADDRESS_SPACE_IO; 2594b385ac4SSasha Levin cdev.base_addr = console_base_addr; 2602449f6e3SSasha Levin pci__register(&virtio_console_pci_device, dev); 261*e59b1ed0SSasha Levin 262*e59b1ed0SSasha Levin cdev.compat_id = compat__add_message("virtio-console device was not detected", 263*e59b1ed0SSasha Levin "While you have requested a virtio-console device, " 264*e59b1ed0SSasha Levin "the guest kernel didn't seem to detect it.\n" 265*e59b1ed0SSasha Levin "Please make sure that the kernel was compiled" 266*e59b1ed0SSasha Levin "with CONFIG_VIRTIO_CONSOLE."); 2679bd8ac5cSAsias He } 268