xref: /kvmtool/virtio/console.c (revision c85b4b4de60dc5bd4280bf277784595d734ba798)
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"
14*c85b4b4dSSasha Levin #include "kvm/virtio-pci.h"
159bd8ac5cSAsias He 
169bd8ac5cSAsias He #include <linux/virtio_console.h>
179bd8ac5cSAsias He #include <linux/virtio_ring.h>
189bd8ac5cSAsias He #include <linux/virtio_blk.h>
199bd8ac5cSAsias He 
209bd8ac5cSAsias He #include <sys/uio.h>
219bd8ac5cSAsias He #include <sys/types.h>
229bd8ac5cSAsias He #include <sys/stat.h>
239bd8ac5cSAsias He #include <termios.h>
249bd8ac5cSAsias He #include <assert.h>
259bd8ac5cSAsias He #include <unistd.h>
269bd8ac5cSAsias He #include <fcntl.h>
279bd8ac5cSAsias He 
289bd8ac5cSAsias He #define VIRTIO_CONSOLE_QUEUE_SIZE	128
299bd8ac5cSAsias He #define VIRTIO_CONSOLE_NUM_QUEUES	2
309bd8ac5cSAsias He #define VIRTIO_CONSOLE_RX_QUEUE		0
319bd8ac5cSAsias He #define VIRTIO_CONSOLE_TX_QUEUE		1
329bd8ac5cSAsias He 
33a643306eSSasha Levin struct con_dev {
347512639bSAsias He 	pthread_mutex_t			mutex;
357512639bSAsias He 
36*c85b4b4dSSasha Levin 	struct virtio_pci		vpci;
379bd8ac5cSAsias He 	struct virt_queue		vqs[VIRTIO_CONSOLE_NUM_QUEUES];
38*c85b4b4dSSasha Levin 	struct virtio_console_config	config;
39*c85b4b4dSSasha Levin 	u32				features;
40e59b1ed0SSasha Levin 	int				compat_id;
41b6638c22SSasha Levin 
42df0c7f57SSasha Levin 	struct thread_pool__job		jobs[VIRTIO_CONSOLE_NUM_QUEUES];
439bd8ac5cSAsias He };
449bd8ac5cSAsias He 
45a643306eSSasha Levin static struct con_dev cdev = {
467512639bSAsias He 	.mutex				= PTHREAD_MUTEX_INITIALIZER,
477512639bSAsias He 
48*c85b4b4dSSasha Levin 	.config = {
499bd8ac5cSAsias He 		.cols			= 80,
509bd8ac5cSAsias He 		.rows			= 24,
519bd8ac5cSAsias He 		.max_nr_ports		= 1,
529bd8ac5cSAsias He 	},
539bd8ac5cSAsias He };
549bd8ac5cSAsias He 
559bd8ac5cSAsias He /*
569bd8ac5cSAsias He  * Interrupts are injected for hvc0 only.
579bd8ac5cSAsias He  */
5843835ac9SSasha Levin static void virtio_console__inject_interrupt_callback(struct kvm *kvm, void *param)
599bd8ac5cSAsias He {
609bd8ac5cSAsias He 	struct iovec iov[VIRTIO_CONSOLE_QUEUE_SIZE];
619bd8ac5cSAsias He 	struct virt_queue *vq;
623fdf659dSSasha Levin 	u16 out, in;
633fdf659dSSasha Levin 	u16 head;
649bd8ac5cSAsias He 	int len;
659bd8ac5cSAsias He 
66a643306eSSasha Levin 	mutex_lock(&cdev.mutex);
677512639bSAsias He 
68b6638c22SSasha Levin 	vq = param;
699bd8ac5cSAsias He 
709bd8ac5cSAsias He 	if (term_readable(CONSOLE_VIRTIO) && virt_queue__available(vq)) {
7143835ac9SSasha Levin 		head = virt_queue__get_iov(vq, iov, &out, &in, kvm);
729bd8ac5cSAsias He 		len = term_getc_iov(CONSOLE_VIRTIO, iov, in);
739bd8ac5cSAsias He 		virt_queue__set_used_elem(vq, head, len);
74*c85b4b4dSSasha Levin 		virtio_pci__signal_vq(kvm, &cdev.vpci, vq - cdev.vqs);
759bd8ac5cSAsias He 	}
767512639bSAsias He 
77a643306eSSasha Levin 	mutex_unlock(&cdev.mutex);
789bd8ac5cSAsias He }
799bd8ac5cSAsias He 
8043835ac9SSasha Levin void virtio_console__inject_interrupt(struct kvm *kvm)
81b6638c22SSasha Levin {
82df0c7f57SSasha Levin 	thread_pool__do_job(&cdev.jobs[VIRTIO_CONSOLE_RX_QUEUE]);
83b6638c22SSasha Levin }
84b6638c22SSasha Levin 
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);
1039bd8ac5cSAsias He 		len = term_putc_iov(CONSOLE_VIRTIO, iov, out);
1049bd8ac5cSAsias He 		virt_queue__set_used_elem(vq, head, len);
1059bd8ac5cSAsias He 	}
1069bd8ac5cSAsias He 
1079bd8ac5cSAsias He }
1089bd8ac5cSAsias He 
109*c85b4b4dSSasha Levin static void set_config(struct kvm *kvm, void *dev, u8 data, u32 offset)
1109bd8ac5cSAsias He {
111*c85b4b4dSSasha Levin 	struct con_dev *cdev = dev;
1129bd8ac5cSAsias He 
113*c85b4b4dSSasha Levin 	((u8 *)(&cdev->config))[offset] = data;
114*c85b4b4dSSasha Levin }
115e147d838SAsias He 
116*c85b4b4dSSasha Levin static u8 get_config(struct kvm *kvm, void *dev, u32 offset)
117*c85b4b4dSSasha Levin {
118*c85b4b4dSSasha Levin 	struct con_dev *cdev = dev;
119*c85b4b4dSSasha Levin 
120*c85b4b4dSSasha Levin 	return ((u8 *)(&cdev->config))[offset];
121*c85b4b4dSSasha Levin }
122*c85b4b4dSSasha Levin 
123*c85b4b4dSSasha Levin static u32 get_host_features(struct kvm *kvm, void *dev)
124*c85b4b4dSSasha Levin {
125*c85b4b4dSSasha Levin 	return 0;
126*c85b4b4dSSasha Levin }
127*c85b4b4dSSasha Levin 
128*c85b4b4dSSasha Levin static void set_guest_features(struct kvm *kvm, void *dev, u32 features)
129*c85b4b4dSSasha Levin {
130*c85b4b4dSSasha Levin 	/* Unused */
131*c85b4b4dSSasha Levin }
132*c85b4b4dSSasha Levin 
133*c85b4b4dSSasha Levin static int init_vq(struct kvm *kvm, void *dev, u32 vq, u32 pfn)
134*c85b4b4dSSasha Levin {
1359bd8ac5cSAsias He 	struct virt_queue *queue;
1369bd8ac5cSAsias He 	void *p;
1379bd8ac5cSAsias He 
138*c85b4b4dSSasha Levin 	assert(vq < VIRTIO_CONSOLE_NUM_QUEUES);
1399bd8ac5cSAsias He 
140e59b1ed0SSasha Levin 	compat__remove_message(cdev.compat_id);
141e59b1ed0SSasha Levin 
142*c85b4b4dSSasha Levin 	queue			= &cdev.vqs[vq];
143*c85b4b4dSSasha Levin 	queue->pfn		= pfn;
14443835ac9SSasha Levin 	p			= guest_pfn_to_host(kvm, queue->pfn);
1459bd8ac5cSAsias He 
146b8f43678SSasha Levin 	vring_init(&queue->vring, VIRTIO_CONSOLE_QUEUE_SIZE, p, VIRTIO_PCI_VRING_ALIGN);
1479bd8ac5cSAsias He 
148*c85b4b4dSSasha Levin 	if (vq == VIRTIO_CONSOLE_TX_QUEUE)
149*c85b4b4dSSasha Levin 		thread_pool__init_job(&cdev.jobs[vq], kvm, virtio_console_handle_callback, queue);
150*c85b4b4dSSasha Levin 	else if (vq == VIRTIO_CONSOLE_RX_QUEUE)
151*c85b4b4dSSasha Levin 		thread_pool__init_job(&cdev.jobs[vq], kvm, virtio_console__inject_interrupt_callback, queue);
152b6638c22SSasha Levin 
153*c85b4b4dSSasha Levin 	return 0;
1549bd8ac5cSAsias He }
1559bd8ac5cSAsias He 
156*c85b4b4dSSasha Levin static int notify_vq(struct kvm *kvm, void *dev, u32 vq)
157*c85b4b4dSSasha Levin {
158*c85b4b4dSSasha Levin 	struct con_dev *cdev = dev;
159*c85b4b4dSSasha Levin 
160*c85b4b4dSSasha Levin 	thread_pool__do_job(&cdev->jobs[vq]);
161*c85b4b4dSSasha Levin 
162*c85b4b4dSSasha Levin 	return 0;
163*c85b4b4dSSasha Levin }
164*c85b4b4dSSasha Levin 
165*c85b4b4dSSasha Levin static int get_pfn_vq(struct kvm *kvm, void *dev, u32 vq)
166*c85b4b4dSSasha Levin {
167*c85b4b4dSSasha Levin 	struct con_dev *cdev = dev;
168*c85b4b4dSSasha Levin 
169*c85b4b4dSSasha Levin 	return cdev->vqs[vq].pfn;
170*c85b4b4dSSasha Levin }
171*c85b4b4dSSasha Levin 
172*c85b4b4dSSasha Levin static int get_size_vq(struct kvm *kvm, void *dev, u32 vq)
173*c85b4b4dSSasha Levin {
174*c85b4b4dSSasha Levin 	return VIRTIO_CONSOLE_QUEUE_SIZE;
175*c85b4b4dSSasha Levin }
1769bd8ac5cSAsias He 
17743835ac9SSasha Levin void virtio_console__init(struct kvm *kvm)
1789bd8ac5cSAsias He {
179*c85b4b4dSSasha Levin 	virtio_pci__init(kvm, &cdev.vpci, &cdev, PCI_DEVICE_ID_VIRTIO_CONSOLE, VIRTIO_ID_CONSOLE);
180*c85b4b4dSSasha Levin 	cdev.vpci.ops = (struct virtio_pci_ops) {
181*c85b4b4dSSasha Levin 		.set_config		= set_config,
182*c85b4b4dSSasha Levin 		.get_config		= get_config,
183*c85b4b4dSSasha Levin 		.get_host_features	= get_host_features,
184*c85b4b4dSSasha Levin 		.set_guest_features	= set_guest_features,
185*c85b4b4dSSasha Levin 		.init_vq		= init_vq,
186*c85b4b4dSSasha Levin 		.notify_vq		= notify_vq,
187*c85b4b4dSSasha Levin 		.get_pfn_vq		= get_pfn_vq,
188*c85b4b4dSSasha Levin 		.get_size_vq		= get_size_vq,
189*c85b4b4dSSasha Levin 	};
190e59b1ed0SSasha Levin 
191e59b1ed0SSasha Levin 	cdev.compat_id = compat__add_message("virtio-console device was not detected",
192e59b1ed0SSasha Levin 						"While you have requested a virtio-console device, "
193e59b1ed0SSasha Levin 						"the guest kernel didn't seem to detect it.\n"
194e59b1ed0SSasha Levin 						"Please make sure that the kernel was compiled"
195e59b1ed0SSasha Levin 						"with CONFIG_VIRTIO_CONSOLE.");
1969bd8ac5cSAsias He }
197