xref: /kvmtool/virtio/console.c (revision 867b15ccd7dae9ba7a174f97d4fe76e90a79d957)
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