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 { 32d3476f7dSSasha Levin struct mutex 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; 3897bb5a9dSJonathan Austin int vq_ready; 3997bb5a9dSJonathan Austin 40df0c7f57SSasha Levin struct thread_pool__job jobs[VIRTIO_CONSOLE_NUM_QUEUES]; 419bd8ac5cSAsias He }; 429bd8ac5cSAsias He 43a643306eSSasha Levin static struct con_dev cdev = { 44d3476f7dSSasha Levin .mutex = MUTEX_INITIALIZER, 4597bb5a9dSJonathan Austin .vq_ready = 0, 469bd8ac5cSAsias He }; 479bd8ac5cSAsias He 48312c62d1SSasha Levin static int compat_id = -1; 49312c62d1SSasha Levin 509bd8ac5cSAsias He /* 519bd8ac5cSAsias He * Interrupts are injected for hvc0 only. 529bd8ac5cSAsias He */ 5343835ac9SSasha Levin static void virtio_console__inject_interrupt_callback(struct kvm *kvm, void *param) 549bd8ac5cSAsias He { 559bd8ac5cSAsias He struct iovec iov[VIRTIO_CONSOLE_QUEUE_SIZE]; 569bd8ac5cSAsias He struct virt_queue *vq; 573fdf659dSSasha Levin u16 out, in; 583fdf659dSSasha Levin u16 head; 599bd8ac5cSAsias He int len; 609bd8ac5cSAsias He 61a643306eSSasha Levin mutex_lock(&cdev.mutex); 627512639bSAsias He 63b6638c22SSasha Levin vq = param; 649bd8ac5cSAsias He 652651ea58SSasha Levin if (term_readable(0) && virt_queue__available(vq)) { 6643835ac9SSasha Levin head = virt_queue__get_iov(vq, iov, &out, &in, kvm); 674346fd8fSSasha Levin len = term_getc_iov(kvm, iov, in, 0); 689bd8ac5cSAsias He virt_queue__set_used_elem(vq, head, len); 6902eca50cSAsias He cdev.vdev.ops->signal_vq(kvm, &cdev.vdev, vq - cdev.vqs); 709bd8ac5cSAsias He } 717512639bSAsias He 72a643306eSSasha Levin mutex_unlock(&cdev.mutex); 739bd8ac5cSAsias He } 749bd8ac5cSAsias He 7543835ac9SSasha Levin void virtio_console__inject_interrupt(struct kvm *kvm) 76b6638c22SSasha Levin { 7766454cc2SJean-Philippe Brucker if (kvm->cfg.active_console != CONSOLE_VIRTIO) 7866454cc2SJean-Philippe Brucker return; 7966454cc2SJean-Philippe Brucker 8066454cc2SJean-Philippe Brucker mutex_lock(&cdev.mutex); 8166454cc2SJean-Philippe Brucker if (cdev.vq_ready) 8266454cc2SJean-Philippe Brucker thread_pool__do_job(&cdev.jobs[VIRTIO_CONSOLE_RX_QUEUE]); 8366454cc2SJean-Philippe Brucker mutex_unlock(&cdev.mutex); 84b6638c22SSasha Levin } 85b6638c22SSasha Levin 8643835ac9SSasha Levin static void virtio_console_handle_callback(struct kvm *kvm, void *param) 879bd8ac5cSAsias He { 889bd8ac5cSAsias He struct iovec iov[VIRTIO_CONSOLE_QUEUE_SIZE]; 899bd8ac5cSAsias He struct virt_queue *vq; 903fdf659dSSasha Levin u16 out, in; 913fdf659dSSasha Levin u16 head; 923fdf659dSSasha Levin u32 len; 939bd8ac5cSAsias He 94b6638c22SSasha Levin vq = param; 959bd8ac5cSAsias He 9620f0438aSAsias He /* 9720f0438aSAsias He * The current Linux implementation polls for the buffer 9820f0438aSAsias He * to be used, rather than waiting for an interrupt. 9920f0438aSAsias He * So there is no need to inject an interrupt for the tx path. 10020f0438aSAsias He */ 10120f0438aSAsias He 1029bd8ac5cSAsias He while (virt_queue__available(vq)) { 10343835ac9SSasha Levin head = virt_queue__get_iov(vq, iov, &out, &in, kvm); 1042651ea58SSasha Levin len = term_putc_iov(iov, out, 0); 1059bd8ac5cSAsias He virt_queue__set_used_elem(vq, head, len); 1069bd8ac5cSAsias He } 1079bd8ac5cSAsias He 1089bd8ac5cSAsias He } 1099bd8ac5cSAsias He 110c5ae742bSSasha Levin static u8 *get_config(struct kvm *kvm, void *dev) 1119bd8ac5cSAsias He { 112c85b4b4dSSasha Levin struct con_dev *cdev = dev; 1139bd8ac5cSAsias He 114c5ae742bSSasha Levin return ((u8 *)(&cdev->config)); 115c85b4b4dSSasha Levin } 116c85b4b4dSSasha Levin 117e4730284SMartin Radev static size_t get_config_size(struct kvm *kvm, void *dev) 118e4730284SMartin Radev { 119e4730284SMartin Radev struct con_dev *cdev = dev; 120e4730284SMartin Radev 121e4730284SMartin Radev return sizeof(cdev->config); 122e4730284SMartin Radev } 123e4730284SMartin Radev 124c85b4b4dSSasha Levin static u32 get_host_features(struct kvm *kvm, void *dev) 125c85b4b4dSSasha Levin { 126c85b4b4dSSasha Levin return 0; 127c85b4b4dSSasha Levin } 128c85b4b4dSSasha Levin 129c85b4b4dSSasha Levin static void set_guest_features(struct kvm *kvm, void *dev, u32 features) 130c85b4b4dSSasha Levin { 131c85b4b4dSSasha Levin } 132c85b4b4dSSasha Levin 13395242e44SJean-Philippe Brucker static void notify_status(struct kvm *kvm, void *dev, u32 status) 13495242e44SJean-Philippe Brucker { 135*867b15ccSJean-Philippe Brucker struct con_dev *cdev = dev; 136*867b15ccSJean-Philippe Brucker struct virtio_console_config *conf = &cdev->config; 137*867b15ccSJean-Philippe Brucker 138*867b15ccSJean-Philippe Brucker if (!(status & VIRTIO__STATUS_CONFIG)) 139*867b15ccSJean-Philippe Brucker return; 140*867b15ccSJean-Philippe Brucker 141*867b15ccSJean-Philippe Brucker conf->cols = virtio_host_to_guest_u16(&cdev->vdev, 80); 142*867b15ccSJean-Philippe Brucker conf->rows = virtio_host_to_guest_u16(&cdev->vdev, 24); 143*867b15ccSJean-Philippe Brucker conf->max_nr_ports = virtio_host_to_guest_u32(&cdev->vdev, 1); 14495242e44SJean-Philippe Brucker } 14595242e44SJean-Philippe Brucker 146609ee906SJean-Philippe Brucker static int init_vq(struct kvm *kvm, void *dev, u32 vq) 147c85b4b4dSSasha Levin { 1489bd8ac5cSAsias He struct virt_queue *queue; 1499bd8ac5cSAsias He 150a2857479SCyrill Gorcunov BUG_ON(vq >= VIRTIO_CONSOLE_NUM_QUEUES); 1519bd8ac5cSAsias He 152312c62d1SSasha Levin compat__remove_message(compat_id); 153e59b1ed0SSasha Levin 154c85b4b4dSSasha Levin queue = &cdev.vqs[vq]; 1559bd8ac5cSAsias He 156609ee906SJean-Philippe Brucker virtio_init_device_vq(kvm, &cdev.vdev, queue, VIRTIO_CONSOLE_QUEUE_SIZE); 1579bd8ac5cSAsias He 15897bb5a9dSJonathan Austin if (vq == VIRTIO_CONSOLE_TX_QUEUE) { 159c85b4b4dSSasha Levin thread_pool__init_job(&cdev.jobs[vq], kvm, virtio_console_handle_callback, queue); 16097bb5a9dSJonathan Austin } else if (vq == VIRTIO_CONSOLE_RX_QUEUE) { 161c85b4b4dSSasha Levin thread_pool__init_job(&cdev.jobs[vq], kvm, virtio_console__inject_interrupt_callback, queue); 16297bb5a9dSJonathan Austin /* Tell the waiting poll thread that we're ready to go */ 16397bb5a9dSJonathan Austin mutex_lock(&cdev.mutex); 16497bb5a9dSJonathan Austin cdev.vq_ready = 1; 16597bb5a9dSJonathan Austin mutex_unlock(&cdev.mutex); 16697bb5a9dSJonathan Austin } 167b6638c22SSasha Levin 168c85b4b4dSSasha Levin return 0; 1699bd8ac5cSAsias He } 1709bd8ac5cSAsias He 17166454cc2SJean-Philippe Brucker static void exit_vq(struct kvm *kvm, void *dev, u32 vq) 17266454cc2SJean-Philippe Brucker { 17366454cc2SJean-Philippe Brucker if (vq == VIRTIO_CONSOLE_RX_QUEUE) { 17466454cc2SJean-Philippe Brucker mutex_lock(&cdev.mutex); 17566454cc2SJean-Philippe Brucker cdev.vq_ready = 0; 17666454cc2SJean-Philippe Brucker mutex_unlock(&cdev.mutex); 17766454cc2SJean-Philippe Brucker thread_pool__cancel_job(&cdev.jobs[vq]); 17866454cc2SJean-Philippe Brucker } else if (vq == VIRTIO_CONSOLE_TX_QUEUE) { 17966454cc2SJean-Philippe Brucker thread_pool__cancel_job(&cdev.jobs[vq]); 18066454cc2SJean-Philippe Brucker } 18166454cc2SJean-Philippe Brucker } 18266454cc2SJean-Philippe Brucker 183c85b4b4dSSasha Levin static int notify_vq(struct kvm *kvm, void *dev, u32 vq) 184c85b4b4dSSasha Levin { 185c85b4b4dSSasha Levin struct con_dev *cdev = dev; 186c85b4b4dSSasha Levin 187c85b4b4dSSasha Levin thread_pool__do_job(&cdev->jobs[vq]); 188c85b4b4dSSasha Levin 189c85b4b4dSSasha Levin return 0; 190c85b4b4dSSasha Levin } 191c85b4b4dSSasha Levin 19253fbb17bSJean-Philippe Brucker static struct virt_queue *get_vq(struct kvm *kvm, void *dev, u32 vq) 193c85b4b4dSSasha Levin { 194c85b4b4dSSasha Levin struct con_dev *cdev = dev; 195c85b4b4dSSasha Levin 19653fbb17bSJean-Philippe Brucker return &cdev->vqs[vq]; 197c85b4b4dSSasha Levin } 198c85b4b4dSSasha Levin 199c85b4b4dSSasha Levin static int get_size_vq(struct kvm *kvm, void *dev, u32 vq) 200c85b4b4dSSasha Levin { 201c85b4b4dSSasha Levin return VIRTIO_CONSOLE_QUEUE_SIZE; 202c85b4b4dSSasha Levin } 2039bd8ac5cSAsias He 2047aba29c1SWill Deacon static int set_size_vq(struct kvm *kvm, void *dev, u32 vq, int size) 2057aba29c1SWill Deacon { 2067aba29c1SWill Deacon /* FIXME: dynamic */ 2077aba29c1SWill Deacon return size; 2087aba29c1SWill Deacon } 2097aba29c1SWill Deacon 21031e0eaccSMartin Radev static unsigned int get_vq_count(struct kvm *kvm, void *dev) 211b98ac591SJean-Philippe Brucker { 212b98ac591SJean-Philippe Brucker return VIRTIO_CONSOLE_NUM_QUEUES; 213b98ac591SJean-Philippe Brucker } 214b98ac591SJean-Philippe Brucker 21515542babSAndre Przywara static struct virtio_ops con_dev_virtio_ops = { 216c85b4b4dSSasha Levin .get_config = get_config, 217e4730284SMartin Radev .get_config_size = get_config_size, 218c85b4b4dSSasha Levin .get_host_features = get_host_features, 219c85b4b4dSSasha Levin .set_guest_features = set_guest_features, 220b98ac591SJean-Philippe Brucker .get_vq_count = get_vq_count, 221c85b4b4dSSasha Levin .init_vq = init_vq, 22266454cc2SJean-Philippe Brucker .exit_vq = exit_vq, 22395242e44SJean-Philippe Brucker .notify_status = notify_status, 224c85b4b4dSSasha Levin .notify_vq = notify_vq, 22553fbb17bSJean-Philippe Brucker .get_vq = get_vq, 226c85b4b4dSSasha Levin .get_size_vq = get_size_vq, 2277aba29c1SWill Deacon .set_size_vq = set_size_vq, 228c85b4b4dSSasha Levin }; 229e59b1ed0SSasha Levin 230a3fa3f86SSasha Levin int virtio_console__init(struct kvm *kvm) 2311c47ce69SSasha Levin { 232db927775SAlexandru Elisei int r; 233db927775SAlexandru Elisei 234a3fa3f86SSasha Levin if (kvm->cfg.active_console != CONSOLE_VIRTIO) 235a3fa3f86SSasha Levin return 0; 236a3fa3f86SSasha Levin 237db927775SAlexandru Elisei r = virtio_init(kvm, &cdev, &cdev.vdev, &con_dev_virtio_ops, 238d97dadecSWill Deacon VIRTIO_DEFAULT_TRANS(kvm), PCI_DEVICE_ID_VIRTIO_CONSOLE, 239ae06ce71SWill Deacon VIRTIO_ID_CONSOLE, PCI_CLASS_CONSOLE); 240db927775SAlexandru Elisei if (r < 0) 241db927775SAlexandru Elisei return r; 242db927775SAlexandru Elisei 243d278197dSAsias He if (compat_id == -1) 24452f34d2cSAsias He compat_id = virtio_compat_add_message("virtio-console", "CONFIG_VIRTIO_CONSOLE"); 245a3fa3f86SSasha Levin 246a3fa3f86SSasha Levin return 0; 247a3fa3f86SSasha Levin } 24849a8afd1SSasha Levin virtio_dev_init(virtio_console__init); 249a3fa3f86SSasha Levin 250a3fa3f86SSasha Levin int virtio_console__exit(struct kvm *kvm) 251a3fa3f86SSasha Levin { 252a3fa3f86SSasha Levin return 0; 2539bd8ac5cSAsias He } 25449a8afd1SSasha Levin virtio_dev_exit(virtio_console__exit); 255