xref: /kvmtool/virtio/console.c (revision a3fa3f86b8d03b9fc8e308cb70d2b6cd35e506d8)
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 
652651ea58SSasha Levin 	if (kvm->cfg.active_console != CONSOLE_VIRTIO)
662651ea58SSasha Levin 		return;
672651ea58SSasha Levin 
68a643306eSSasha Levin 	mutex_lock(&cdev.mutex);
697512639bSAsias He 
70b6638c22SSasha Levin 	vq = param;
719bd8ac5cSAsias He 
722651ea58SSasha Levin 	if (term_readable(0) && virt_queue__available(vq)) {
7343835ac9SSasha Levin 		head = virt_queue__get_iov(vq, iov, &out, &in, kvm);
742651ea58SSasha Levin 		len = term_getc_iov(iov, in, 0);
759bd8ac5cSAsias He 		virt_queue__set_used_elem(vq, head, len);
7602eca50cSAsias He 		cdev.vdev.ops->signal_vq(kvm, &cdev.vdev, vq - cdev.vqs);
779bd8ac5cSAsias He 	}
787512639bSAsias He 
79a643306eSSasha Levin 	mutex_unlock(&cdev.mutex);
809bd8ac5cSAsias He }
819bd8ac5cSAsias He 
8243835ac9SSasha Levin void virtio_console__inject_interrupt(struct kvm *kvm)
83b6638c22SSasha Levin {
84df0c7f57SSasha Levin 	thread_pool__do_job(&cdev.jobs[VIRTIO_CONSOLE_RX_QUEUE]);
85b6638c22SSasha Levin }
86b6638c22SSasha Levin 
8743835ac9SSasha Levin static void virtio_console_handle_callback(struct kvm *kvm, void *param)
889bd8ac5cSAsias He {
899bd8ac5cSAsias He 	struct iovec iov[VIRTIO_CONSOLE_QUEUE_SIZE];
909bd8ac5cSAsias He 	struct virt_queue *vq;
913fdf659dSSasha Levin 	u16 out, in;
923fdf659dSSasha Levin 	u16 head;
933fdf659dSSasha Levin 	u32 len;
949bd8ac5cSAsias He 
95b6638c22SSasha Levin 	vq = param;
969bd8ac5cSAsias He 
9720f0438aSAsias He 	/*
9820f0438aSAsias He 	 * The current Linux implementation polls for the buffer
9920f0438aSAsias He 	 * to be used, rather than waiting for an interrupt.
10020f0438aSAsias He 	 * So there is no need to inject an interrupt for the tx path.
10120f0438aSAsias He 	 */
10220f0438aSAsias He 
1039bd8ac5cSAsias He 	while (virt_queue__available(vq)) {
10443835ac9SSasha Levin 		head = virt_queue__get_iov(vq, iov, &out, &in, kvm);
1052651ea58SSasha Levin 		if (kvm->cfg.active_console == CONSOLE_VIRTIO)
1062651ea58SSasha Levin 			len = term_putc_iov(iov, out, 0);
1072651ea58SSasha Levin 		else
1082651ea58SSasha Levin 			len = 0;
1099bd8ac5cSAsias He 		virt_queue__set_used_elem(vq, head, len);
1109bd8ac5cSAsias He 	}
1119bd8ac5cSAsias He 
1129bd8ac5cSAsias He }
1139bd8ac5cSAsias He 
114c5ae742bSSasha Levin static u8 *get_config(struct kvm *kvm, void *dev)
1159bd8ac5cSAsias He {
116c85b4b4dSSasha Levin 	struct con_dev *cdev = dev;
1179bd8ac5cSAsias He 
118c5ae742bSSasha Levin 	return ((u8 *)(&cdev->config));
119c85b4b4dSSasha Levin }
120c85b4b4dSSasha Levin 
121c85b4b4dSSasha Levin static u32 get_host_features(struct kvm *kvm, void *dev)
122c85b4b4dSSasha Levin {
123c85b4b4dSSasha Levin 	return 0;
124c85b4b4dSSasha Levin }
125c85b4b4dSSasha Levin 
126c85b4b4dSSasha Levin static void set_guest_features(struct kvm *kvm, void *dev, u32 features)
127c85b4b4dSSasha Levin {
128c85b4b4dSSasha Levin 	/* Unused */
129c85b4b4dSSasha Levin }
130c85b4b4dSSasha Levin 
131c85b4b4dSSasha Levin static int init_vq(struct kvm *kvm, void *dev, u32 vq, u32 pfn)
132c85b4b4dSSasha Levin {
1339bd8ac5cSAsias He 	struct virt_queue *queue;
1349bd8ac5cSAsias He 	void *p;
1359bd8ac5cSAsias He 
136a2857479SCyrill Gorcunov 	BUG_ON(vq >= VIRTIO_CONSOLE_NUM_QUEUES);
1379bd8ac5cSAsias He 
138312c62d1SSasha Levin 	compat__remove_message(compat_id);
139e59b1ed0SSasha Levin 
140c85b4b4dSSasha Levin 	queue		= &cdev.vqs[vq];
141c85b4b4dSSasha Levin 	queue->pfn	= pfn;
14243835ac9SSasha Levin 	p		= guest_pfn_to_host(kvm, queue->pfn);
1439bd8ac5cSAsias He 
144b8f43678SSasha Levin 	vring_init(&queue->vring, VIRTIO_CONSOLE_QUEUE_SIZE, p, VIRTIO_PCI_VRING_ALIGN);
1459bd8ac5cSAsias He 
146c85b4b4dSSasha Levin 	if (vq == VIRTIO_CONSOLE_TX_QUEUE)
147c85b4b4dSSasha Levin 		thread_pool__init_job(&cdev.jobs[vq], kvm, virtio_console_handle_callback, queue);
148c85b4b4dSSasha Levin 	else if (vq == VIRTIO_CONSOLE_RX_QUEUE)
149c85b4b4dSSasha Levin 		thread_pool__init_job(&cdev.jobs[vq], kvm, virtio_console__inject_interrupt_callback, queue);
150b6638c22SSasha Levin 
151c85b4b4dSSasha Levin 	return 0;
1529bd8ac5cSAsias He }
1539bd8ac5cSAsias He 
154c85b4b4dSSasha Levin static int notify_vq(struct kvm *kvm, void *dev, u32 vq)
155c85b4b4dSSasha Levin {
156c85b4b4dSSasha Levin 	struct con_dev *cdev = dev;
157c85b4b4dSSasha Levin 
158c85b4b4dSSasha Levin 	thread_pool__do_job(&cdev->jobs[vq]);
159c85b4b4dSSasha Levin 
160c85b4b4dSSasha Levin 	return 0;
161c85b4b4dSSasha Levin }
162c85b4b4dSSasha Levin 
163c85b4b4dSSasha Levin static int get_pfn_vq(struct kvm *kvm, void *dev, u32 vq)
164c85b4b4dSSasha Levin {
165c85b4b4dSSasha Levin 	struct con_dev *cdev = dev;
166c85b4b4dSSasha Levin 
167c85b4b4dSSasha Levin 	return cdev->vqs[vq].pfn;
168c85b4b4dSSasha Levin }
169c85b4b4dSSasha Levin 
170c85b4b4dSSasha Levin static int get_size_vq(struct kvm *kvm, void *dev, u32 vq)
171c85b4b4dSSasha Levin {
172c85b4b4dSSasha Levin 	return VIRTIO_CONSOLE_QUEUE_SIZE;
173c85b4b4dSSasha Levin }
1749bd8ac5cSAsias He 
1751c47ce69SSasha Levin static struct virtio_ops con_dev_virtio_ops = (struct virtio_ops) {
176c85b4b4dSSasha Levin 	.get_config		= get_config,
177c85b4b4dSSasha Levin 	.get_host_features	= get_host_features,
178c85b4b4dSSasha Levin 	.set_guest_features	= set_guest_features,
179c85b4b4dSSasha Levin 	.init_vq		= init_vq,
180c85b4b4dSSasha Levin 	.notify_vq		= notify_vq,
181c85b4b4dSSasha Levin 	.get_pfn_vq		= get_pfn_vq,
182c85b4b4dSSasha Levin 	.get_size_vq		= get_size_vq,
183c85b4b4dSSasha Levin };
184e59b1ed0SSasha Levin 
185*a3fa3f86SSasha Levin int virtio_console__init(struct kvm *kvm)
1861c47ce69SSasha Levin {
187*a3fa3f86SSasha Levin 	if (kvm->cfg.active_console != CONSOLE_VIRTIO)
188*a3fa3f86SSasha Levin 		return 0;
189*a3fa3f86SSasha Levin 
19002eca50cSAsias He 	virtio_init(kvm, &cdev, &cdev.vdev, &con_dev_virtio_ops,
19102eca50cSAsias He 		    VIRTIO_PCI, PCI_DEVICE_ID_VIRTIO_CONSOLE, VIRTIO_ID_CONSOLE, PCI_CLASS_CONSOLE);
192d278197dSAsias He 	if (compat_id == -1)
19352f34d2cSAsias He 		compat_id = virtio_compat_add_message("virtio-console", "CONFIG_VIRTIO_CONSOLE");
194*a3fa3f86SSasha Levin 
195*a3fa3f86SSasha Levin 	return 0;
196*a3fa3f86SSasha Levin }
197*a3fa3f86SSasha Levin 
198*a3fa3f86SSasha Levin int virtio_console__exit(struct kvm *kvm)
199*a3fa3f86SSasha Levin {
200*a3fa3f86SSasha Levin 	return 0;
2019bd8ac5cSAsias He }
202