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;
3797bb5a9dSJonathan Austin int vq_ready;
3897bb5a9dSJonathan Austin
39df0c7f57SSasha Levin struct thread_pool__job jobs[VIRTIO_CONSOLE_NUM_QUEUES];
409bd8ac5cSAsias He };
419bd8ac5cSAsias He
42a643306eSSasha Levin static struct con_dev cdev = {
43d3476f7dSSasha Levin .mutex = MUTEX_INITIALIZER,
4497bb5a9dSJonathan Austin .vq_ready = 0,
459bd8ac5cSAsias He };
469bd8ac5cSAsias He
47312c62d1SSasha Levin static int compat_id = -1;
48312c62d1SSasha Levin
499bd8ac5cSAsias He /*
509bd8ac5cSAsias He * Interrupts are injected for hvc0 only.
519bd8ac5cSAsias He */
virtio_console__inject_interrupt_callback(struct kvm * kvm,void * param)5243835ac9SSasha Levin static void virtio_console__inject_interrupt_callback(struct kvm *kvm, void *param)
539bd8ac5cSAsias He {
549bd8ac5cSAsias He struct iovec iov[VIRTIO_CONSOLE_QUEUE_SIZE];
559bd8ac5cSAsias He struct virt_queue *vq;
563fdf659dSSasha Levin u16 out, in;
573fdf659dSSasha Levin u16 head;
589bd8ac5cSAsias He int len;
599bd8ac5cSAsias He
60a643306eSSasha Levin mutex_lock(&cdev.mutex);
617512639bSAsias He
62b6638c22SSasha Levin vq = param;
639bd8ac5cSAsias He
642651ea58SSasha Levin if (term_readable(0) && virt_queue__available(vq)) {
6543835ac9SSasha Levin head = virt_queue__get_iov(vq, iov, &out, &in, kvm);
664346fd8fSSasha Levin len = term_getc_iov(kvm, iov, in, 0);
679bd8ac5cSAsias He virt_queue__set_used_elem(vq, head, len);
6802eca50cSAsias He cdev.vdev.ops->signal_vq(kvm, &cdev.vdev, vq - cdev.vqs);
699bd8ac5cSAsias He }
707512639bSAsias He
71a643306eSSasha Levin mutex_unlock(&cdev.mutex);
729bd8ac5cSAsias He }
739bd8ac5cSAsias He
virtio_console__inject_interrupt(struct kvm * kvm)7443835ac9SSasha Levin void virtio_console__inject_interrupt(struct kvm *kvm)
75b6638c22SSasha Levin {
7666454cc2SJean-Philippe Brucker if (kvm->cfg.active_console != CONSOLE_VIRTIO)
7766454cc2SJean-Philippe Brucker return;
7866454cc2SJean-Philippe Brucker
7966454cc2SJean-Philippe Brucker mutex_lock(&cdev.mutex);
8066454cc2SJean-Philippe Brucker if (cdev.vq_ready)
8166454cc2SJean-Philippe Brucker thread_pool__do_job(&cdev.jobs[VIRTIO_CONSOLE_RX_QUEUE]);
8266454cc2SJean-Philippe Brucker mutex_unlock(&cdev.mutex);
83b6638c22SSasha Levin }
84b6638c22SSasha Levin
virtio_console_handle_callback(struct kvm * kvm,void * param)8543835ac9SSasha Levin static void virtio_console_handle_callback(struct kvm *kvm, void *param)
869bd8ac5cSAsias He {
879bd8ac5cSAsias He struct iovec iov[VIRTIO_CONSOLE_QUEUE_SIZE];
889bd8ac5cSAsias He struct virt_queue *vq;
893fdf659dSSasha Levin u16 out, in;
903fdf659dSSasha Levin u16 head;
913fdf659dSSasha Levin u32 len;
929bd8ac5cSAsias He
93b6638c22SSasha Levin vq = param;
949bd8ac5cSAsias He
9520f0438aSAsias He /*
9620f0438aSAsias He * The current Linux implementation polls for the buffer
9720f0438aSAsias He * to be used, rather than waiting for an interrupt.
9820f0438aSAsias He * So there is no need to inject an interrupt for the tx path.
9920f0438aSAsias He */
10020f0438aSAsias He
1019bd8ac5cSAsias He while (virt_queue__available(vq)) {
10243835ac9SSasha Levin head = virt_queue__get_iov(vq, iov, &out, &in, kvm);
1032651ea58SSasha Levin len = term_putc_iov(iov, out, 0);
1049bd8ac5cSAsias He virt_queue__set_used_elem(vq, head, len);
1059bd8ac5cSAsias He }
1069bd8ac5cSAsias He
1079bd8ac5cSAsias He }
1089bd8ac5cSAsias He
get_config(struct kvm * kvm,void * dev)109c5ae742bSSasha Levin static u8 *get_config(struct kvm *kvm, void *dev)
1109bd8ac5cSAsias He {
111c85b4b4dSSasha Levin struct con_dev *cdev = dev;
1129bd8ac5cSAsias He
113c5ae742bSSasha Levin return ((u8 *)(&cdev->config));
114c85b4b4dSSasha Levin }
115c85b4b4dSSasha Levin
get_config_size(struct kvm * kvm,void * dev)116e4730284SMartin Radev static size_t get_config_size(struct kvm *kvm, void *dev)
117e4730284SMartin Radev {
118e4730284SMartin Radev struct con_dev *cdev = dev;
119e4730284SMartin Radev
120e4730284SMartin Radev return sizeof(cdev->config);
121e4730284SMartin Radev }
122e4730284SMartin Radev
get_host_features(struct kvm * kvm,void * dev)1233c8f82b8SJean-Philippe Brucker static u64 get_host_features(struct kvm *kvm, void *dev)
124c85b4b4dSSasha Levin {
125e74b56e1SJean-Philippe Brucker return 1 << VIRTIO_F_ANY_LAYOUT;
126c85b4b4dSSasha Levin }
127c85b4b4dSSasha Levin
notify_status(struct kvm * kvm,void * dev,u32 status)12895242e44SJean-Philippe Brucker static void notify_status(struct kvm *kvm, void *dev, u32 status)
12995242e44SJean-Philippe Brucker {
130867b15ccSJean-Philippe Brucker struct con_dev *cdev = dev;
131867b15ccSJean-Philippe Brucker struct virtio_console_config *conf = &cdev->config;
132867b15ccSJean-Philippe Brucker
133867b15ccSJean-Philippe Brucker if (!(status & VIRTIO__STATUS_CONFIG))
134867b15ccSJean-Philippe Brucker return;
135867b15ccSJean-Philippe Brucker
136b17552eeSAndre Przywara conf->cols = virtio_host_to_guest_u16(cdev->vdev.endian, 80);
137b17552eeSAndre Przywara conf->rows = virtio_host_to_guest_u16(cdev->vdev.endian, 24);
138b17552eeSAndre Przywara conf->max_nr_ports = virtio_host_to_guest_u32(cdev->vdev.endian, 1);
13995242e44SJean-Philippe Brucker }
14095242e44SJean-Philippe Brucker
init_vq(struct kvm * kvm,void * dev,u32 vq)141609ee906SJean-Philippe Brucker static int init_vq(struct kvm *kvm, void *dev, u32 vq)
142c85b4b4dSSasha Levin {
1439bd8ac5cSAsias He struct virt_queue *queue;
1449bd8ac5cSAsias He
145a2857479SCyrill Gorcunov BUG_ON(vq >= VIRTIO_CONSOLE_NUM_QUEUES);
1469bd8ac5cSAsias He
147312c62d1SSasha Levin compat__remove_message(compat_id);
148e59b1ed0SSasha Levin
149c85b4b4dSSasha Levin queue = &cdev.vqs[vq];
1509bd8ac5cSAsias He
151609ee906SJean-Philippe Brucker virtio_init_device_vq(kvm, &cdev.vdev, queue, VIRTIO_CONSOLE_QUEUE_SIZE);
1529bd8ac5cSAsias He
15397bb5a9dSJonathan Austin if (vq == VIRTIO_CONSOLE_TX_QUEUE) {
154c85b4b4dSSasha Levin thread_pool__init_job(&cdev.jobs[vq], kvm, virtio_console_handle_callback, queue);
15597bb5a9dSJonathan Austin } else if (vq == VIRTIO_CONSOLE_RX_QUEUE) {
156c85b4b4dSSasha Levin thread_pool__init_job(&cdev.jobs[vq], kvm, virtio_console__inject_interrupt_callback, queue);
15797bb5a9dSJonathan Austin /* Tell the waiting poll thread that we're ready to go */
15897bb5a9dSJonathan Austin mutex_lock(&cdev.mutex);
15997bb5a9dSJonathan Austin cdev.vq_ready = 1;
16097bb5a9dSJonathan Austin mutex_unlock(&cdev.mutex);
16197bb5a9dSJonathan Austin }
162b6638c22SSasha Levin
163c85b4b4dSSasha Levin return 0;
1649bd8ac5cSAsias He }
1659bd8ac5cSAsias He
exit_vq(struct kvm * kvm,void * dev,u32 vq)16666454cc2SJean-Philippe Brucker static void exit_vq(struct kvm *kvm, void *dev, u32 vq)
16766454cc2SJean-Philippe Brucker {
16866454cc2SJean-Philippe Brucker if (vq == VIRTIO_CONSOLE_RX_QUEUE) {
16966454cc2SJean-Philippe Brucker mutex_lock(&cdev.mutex);
17066454cc2SJean-Philippe Brucker cdev.vq_ready = 0;
17166454cc2SJean-Philippe Brucker mutex_unlock(&cdev.mutex);
17266454cc2SJean-Philippe Brucker thread_pool__cancel_job(&cdev.jobs[vq]);
17366454cc2SJean-Philippe Brucker } else if (vq == VIRTIO_CONSOLE_TX_QUEUE) {
17466454cc2SJean-Philippe Brucker thread_pool__cancel_job(&cdev.jobs[vq]);
17566454cc2SJean-Philippe Brucker }
17666454cc2SJean-Philippe Brucker }
17766454cc2SJean-Philippe Brucker
notify_vq(struct kvm * kvm,void * dev,u32 vq)178c85b4b4dSSasha Levin static int notify_vq(struct kvm *kvm, void *dev, u32 vq)
179c85b4b4dSSasha Levin {
180c85b4b4dSSasha Levin struct con_dev *cdev = dev;
181c85b4b4dSSasha Levin
182c85b4b4dSSasha Levin thread_pool__do_job(&cdev->jobs[vq]);
183c85b4b4dSSasha Levin
184c85b4b4dSSasha Levin return 0;
185c85b4b4dSSasha Levin }
186c85b4b4dSSasha Levin
get_vq(struct kvm * kvm,void * dev,u32 vq)18753fbb17bSJean-Philippe Brucker static struct virt_queue *get_vq(struct kvm *kvm, void *dev, u32 vq)
188c85b4b4dSSasha Levin {
189c85b4b4dSSasha Levin struct con_dev *cdev = dev;
190c85b4b4dSSasha Levin
19153fbb17bSJean-Philippe Brucker return &cdev->vqs[vq];
192c85b4b4dSSasha Levin }
193c85b4b4dSSasha Levin
get_size_vq(struct kvm * kvm,void * dev,u32 vq)194c85b4b4dSSasha Levin static int get_size_vq(struct kvm *kvm, void *dev, u32 vq)
195c85b4b4dSSasha Levin {
196c85b4b4dSSasha Levin return VIRTIO_CONSOLE_QUEUE_SIZE;
197c85b4b4dSSasha Levin }
1989bd8ac5cSAsias He
set_size_vq(struct kvm * kvm,void * dev,u32 vq,int size)1997aba29c1SWill Deacon static int set_size_vq(struct kvm *kvm, void *dev, u32 vq, int size)
2007aba29c1SWill Deacon {
2017aba29c1SWill Deacon /* FIXME: dynamic */
2027aba29c1SWill Deacon return size;
2037aba29c1SWill Deacon }
2047aba29c1SWill Deacon
get_vq_count(struct kvm * kvm,void * dev)20531e0eaccSMartin Radev static unsigned int get_vq_count(struct kvm *kvm, void *dev)
206b98ac591SJean-Philippe Brucker {
207b98ac591SJean-Philippe Brucker return VIRTIO_CONSOLE_NUM_QUEUES;
208b98ac591SJean-Philippe Brucker }
209b98ac591SJean-Philippe Brucker
21015542babSAndre Przywara static struct virtio_ops con_dev_virtio_ops = {
211c85b4b4dSSasha Levin .get_config = get_config,
212e4730284SMartin Radev .get_config_size = get_config_size,
213c85b4b4dSSasha Levin .get_host_features = get_host_features,
214b98ac591SJean-Philippe Brucker .get_vq_count = get_vq_count,
215c85b4b4dSSasha Levin .init_vq = init_vq,
21666454cc2SJean-Philippe Brucker .exit_vq = exit_vq,
21795242e44SJean-Philippe Brucker .notify_status = notify_status,
218c85b4b4dSSasha Levin .notify_vq = notify_vq,
21953fbb17bSJean-Philippe Brucker .get_vq = get_vq,
220c85b4b4dSSasha Levin .get_size_vq = get_size_vq,
2217aba29c1SWill Deacon .set_size_vq = set_size_vq,
222c85b4b4dSSasha Levin };
223e59b1ed0SSasha Levin
virtio_console__init(struct kvm * kvm)224a3fa3f86SSasha Levin int virtio_console__init(struct kvm *kvm)
2251c47ce69SSasha Levin {
226db927775SAlexandru Elisei int r;
227db927775SAlexandru Elisei
228a3fa3f86SSasha Levin if (kvm->cfg.active_console != CONSOLE_VIRTIO)
229a3fa3f86SSasha Levin return 0;
230a3fa3f86SSasha Levin
231db927775SAlexandru Elisei r = virtio_init(kvm, &cdev, &cdev.vdev, &con_dev_virtio_ops,
2329b46ebc5SRajnesh Kanwal kvm->cfg.virtio_transport, PCI_DEVICE_ID_VIRTIO_CONSOLE,
233ae06ce71SWill Deacon VIRTIO_ID_CONSOLE, PCI_CLASS_CONSOLE);
234db927775SAlexandru Elisei if (r < 0)
235db927775SAlexandru Elisei return r;
236db927775SAlexandru Elisei
237d278197dSAsias He if (compat_id == -1)
23852f34d2cSAsias He compat_id = virtio_compat_add_message("virtio-console", "CONFIG_VIRTIO_CONSOLE");
239a3fa3f86SSasha Levin
240a3fa3f86SSasha Levin return 0;
241a3fa3f86SSasha Levin }
24249a8afd1SSasha Levin virtio_dev_init(virtio_console__init);
243a3fa3f86SSasha Levin
virtio_console__exit(struct kvm * kvm)244a3fa3f86SSasha Levin int virtio_console__exit(struct kvm *kvm)
245a3fa3f86SSasha Levin {
246*74af1456SEduardo Bart virtio_exit(kvm, &cdev.vdev);
247*74af1456SEduardo Bart
248a3fa3f86SSasha Levin return 0;
2499bd8ac5cSAsias He }
25049a8afd1SSasha Levin virtio_dev_exit(virtio_console__exit);
251