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" 13e59b1ed0SSasha 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 <unistd.h> 249bd8ac5cSAsias He #include <fcntl.h> 259bd8ac5cSAsias He 269bd8ac5cSAsias He #define VIRTIO_CONSOLE_QUEUE_SIZE 128 279bd8ac5cSAsias He #define VIRTIO_CONSOLE_NUM_QUEUES 2 289bd8ac5cSAsias He #define VIRTIO_CONSOLE_RX_QUEUE 0 299bd8ac5cSAsias He #define VIRTIO_CONSOLE_TX_QUEUE 1 309bd8ac5cSAsias He 31a643306eSSasha Levin struct con_dev { 327512639bSAsias He pthread_mutex_t mutex; 337512639bSAsias He 3402eca50cSAsias He struct virtio_device vdev; 359bd8ac5cSAsias He struct virt_queue vqs[VIRTIO_CONSOLE_NUM_QUEUES]; 36c85b4b4dSSasha Levin struct virtio_console_config config; 37c85b4b4dSSasha Levin u32 features; 38b6638c22SSasha Levin 39df0c7f57SSasha Levin struct thread_pool__job jobs[VIRTIO_CONSOLE_NUM_QUEUES]; 409bd8ac5cSAsias He }; 419bd8ac5cSAsias He 42a643306eSSasha Levin static struct con_dev cdev = { 437512639bSAsias He .mutex = PTHREAD_MUTEX_INITIALIZER, 447512639bSAsias He 45c85b4b4dSSasha Levin .config = { 469bd8ac5cSAsias He .cols = 80, 479bd8ac5cSAsias He .rows = 24, 489bd8ac5cSAsias He .max_nr_ports = 1, 499bd8ac5cSAsias He }, 509bd8ac5cSAsias He }; 519bd8ac5cSAsias He 52312c62d1SSasha Levin static int compat_id = -1; 53312c62d1SSasha Levin 549bd8ac5cSAsias He /* 559bd8ac5cSAsias He * Interrupts are injected for hvc0 only. 569bd8ac5cSAsias He */ 5743835ac9SSasha Levin static void virtio_console__inject_interrupt_callback(struct kvm *kvm, void *param) 589bd8ac5cSAsias He { 599bd8ac5cSAsias He struct iovec iov[VIRTIO_CONSOLE_QUEUE_SIZE]; 609bd8ac5cSAsias He struct virt_queue *vq; 613fdf659dSSasha Levin u16 out, in; 623fdf659dSSasha Levin u16 head; 639bd8ac5cSAsias He int len; 649bd8ac5cSAsias He 65a643306eSSasha Levin mutex_lock(&cdev.mutex); 667512639bSAsias He 67b6638c22SSasha Levin vq = param; 689bd8ac5cSAsias He 691add4b76SSasha Levin if (term_readable(CONSOLE_VIRTIO, 0) && virt_queue__available(vq)) { 7043835ac9SSasha Levin head = virt_queue__get_iov(vq, iov, &out, &in, kvm); 711add4b76SSasha Levin len = term_getc_iov(CONSOLE_VIRTIO, iov, in, 0); 729bd8ac5cSAsias He virt_queue__set_used_elem(vq, head, len); 7302eca50cSAsias He cdev.vdev.ops->signal_vq(kvm, &cdev.vdev, vq - cdev.vqs); 749bd8ac5cSAsias He } 757512639bSAsias He 76a643306eSSasha Levin mutex_unlock(&cdev.mutex); 779bd8ac5cSAsias He } 789bd8ac5cSAsias He 7943835ac9SSasha Levin void virtio_console__inject_interrupt(struct kvm *kvm) 80b6638c22SSasha Levin { 81df0c7f57SSasha Levin thread_pool__do_job(&cdev.jobs[VIRTIO_CONSOLE_RX_QUEUE]); 82b6638c22SSasha Levin } 83b6638c22SSasha Levin 8443835ac9SSasha Levin static void virtio_console_handle_callback(struct kvm *kvm, void *param) 859bd8ac5cSAsias He { 869bd8ac5cSAsias He struct iovec iov[VIRTIO_CONSOLE_QUEUE_SIZE]; 879bd8ac5cSAsias He struct virt_queue *vq; 883fdf659dSSasha Levin u16 out, in; 893fdf659dSSasha Levin u16 head; 903fdf659dSSasha Levin u32 len; 919bd8ac5cSAsias He 92b6638c22SSasha Levin vq = param; 939bd8ac5cSAsias He 9420f0438aSAsias He /* 9520f0438aSAsias He * The current Linux implementation polls for the buffer 9620f0438aSAsias He * to be used, rather than waiting for an interrupt. 9720f0438aSAsias He * So there is no need to inject an interrupt for the tx path. 9820f0438aSAsias He */ 9920f0438aSAsias He 1009bd8ac5cSAsias He while (virt_queue__available(vq)) { 10143835ac9SSasha Levin head = virt_queue__get_iov(vq, iov, &out, &in, kvm); 1021add4b76SSasha Levin len = term_putc_iov(CONSOLE_VIRTIO, iov, out, 0); 1039bd8ac5cSAsias He virt_queue__set_used_elem(vq, head, len); 1049bd8ac5cSAsias He } 1059bd8ac5cSAsias He 1069bd8ac5cSAsias He } 1079bd8ac5cSAsias He 108*c5ae742bSSasha Levin static u8 *get_config(struct kvm *kvm, void *dev) 1099bd8ac5cSAsias He { 110c85b4b4dSSasha Levin struct con_dev *cdev = dev; 1119bd8ac5cSAsias He 112*c5ae742bSSasha Levin return ((u8 *)(&cdev->config)); 113c85b4b4dSSasha Levin } 114c85b4b4dSSasha Levin 115c85b4b4dSSasha Levin static u32 get_host_features(struct kvm *kvm, void *dev) 116c85b4b4dSSasha Levin { 117c85b4b4dSSasha Levin return 0; 118c85b4b4dSSasha Levin } 119c85b4b4dSSasha Levin 120c85b4b4dSSasha Levin static void set_guest_features(struct kvm *kvm, void *dev, u32 features) 121c85b4b4dSSasha Levin { 122c85b4b4dSSasha Levin /* Unused */ 123c85b4b4dSSasha Levin } 124c85b4b4dSSasha Levin 125c85b4b4dSSasha Levin static int init_vq(struct kvm *kvm, void *dev, u32 vq, u32 pfn) 126c85b4b4dSSasha Levin { 1279bd8ac5cSAsias He struct virt_queue *queue; 1289bd8ac5cSAsias He void *p; 1299bd8ac5cSAsias He 130a2857479SCyrill Gorcunov BUG_ON(vq >= VIRTIO_CONSOLE_NUM_QUEUES); 1319bd8ac5cSAsias He 132312c62d1SSasha Levin compat__remove_message(compat_id); 133e59b1ed0SSasha Levin 134c85b4b4dSSasha Levin queue = &cdev.vqs[vq]; 135c85b4b4dSSasha Levin queue->pfn = pfn; 13643835ac9SSasha Levin p = guest_pfn_to_host(kvm, queue->pfn); 1379bd8ac5cSAsias He 138b8f43678SSasha Levin vring_init(&queue->vring, VIRTIO_CONSOLE_QUEUE_SIZE, p, VIRTIO_PCI_VRING_ALIGN); 1399bd8ac5cSAsias He 140c85b4b4dSSasha Levin if (vq == VIRTIO_CONSOLE_TX_QUEUE) 141c85b4b4dSSasha Levin thread_pool__init_job(&cdev.jobs[vq], kvm, virtio_console_handle_callback, queue); 142c85b4b4dSSasha Levin else if (vq == VIRTIO_CONSOLE_RX_QUEUE) 143c85b4b4dSSasha Levin thread_pool__init_job(&cdev.jobs[vq], kvm, virtio_console__inject_interrupt_callback, queue); 144b6638c22SSasha Levin 145c85b4b4dSSasha Levin return 0; 1469bd8ac5cSAsias He } 1479bd8ac5cSAsias He 148c85b4b4dSSasha Levin static int notify_vq(struct kvm *kvm, void *dev, u32 vq) 149c85b4b4dSSasha Levin { 150c85b4b4dSSasha Levin struct con_dev *cdev = dev; 151c85b4b4dSSasha Levin 152c85b4b4dSSasha Levin thread_pool__do_job(&cdev->jobs[vq]); 153c85b4b4dSSasha Levin 154c85b4b4dSSasha Levin return 0; 155c85b4b4dSSasha Levin } 156c85b4b4dSSasha Levin 157c85b4b4dSSasha Levin static int get_pfn_vq(struct kvm *kvm, void *dev, u32 vq) 158c85b4b4dSSasha Levin { 159c85b4b4dSSasha Levin struct con_dev *cdev = dev; 160c85b4b4dSSasha Levin 161c85b4b4dSSasha Levin return cdev->vqs[vq].pfn; 162c85b4b4dSSasha Levin } 163c85b4b4dSSasha Levin 164c85b4b4dSSasha Levin static int get_size_vq(struct kvm *kvm, void *dev, u32 vq) 165c85b4b4dSSasha Levin { 166c85b4b4dSSasha Levin return VIRTIO_CONSOLE_QUEUE_SIZE; 167c85b4b4dSSasha Levin } 1689bd8ac5cSAsias He 1691c47ce69SSasha Levin static struct virtio_ops con_dev_virtio_ops = (struct virtio_ops) { 170c85b4b4dSSasha Levin .get_config = get_config, 171c85b4b4dSSasha Levin .get_host_features = get_host_features, 172c85b4b4dSSasha Levin .set_guest_features = set_guest_features, 173c85b4b4dSSasha Levin .init_vq = init_vq, 174c85b4b4dSSasha Levin .notify_vq = notify_vq, 175c85b4b4dSSasha Levin .get_pfn_vq = get_pfn_vq, 176c85b4b4dSSasha Levin .get_size_vq = get_size_vq, 177c85b4b4dSSasha Levin }; 178e59b1ed0SSasha Levin 1791c47ce69SSasha Levin void virtio_console__init(struct kvm *kvm) 1801c47ce69SSasha Levin { 18102eca50cSAsias He virtio_init(kvm, &cdev, &cdev.vdev, &con_dev_virtio_ops, 18202eca50cSAsias He VIRTIO_PCI, PCI_DEVICE_ID_VIRTIO_CONSOLE, VIRTIO_ID_CONSOLE, PCI_CLASS_CONSOLE); 183d278197dSAsias He if (compat_id == -1) 18452f34d2cSAsias He compat_id = virtio_compat_add_message("virtio-console", "CONFIG_VIRTIO_CONSOLE"); 1859bd8ac5cSAsias He } 186