xref: /kvmtool/virtio/console.c (revision b98ac59172405034be3886f7a37f9f2df5989b1f)
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;
38b6638c22SSasha Levin 
3997bb5a9dSJonathan Austin 	pthread_cond_t			poll_cond;
4097bb5a9dSJonathan Austin 	int				vq_ready;
4197bb5a9dSJonathan Austin 
42df0c7f57SSasha Levin 	struct thread_pool__job		jobs[VIRTIO_CONSOLE_NUM_QUEUES];
439bd8ac5cSAsias He };
449bd8ac5cSAsias He 
45a643306eSSasha Levin static struct con_dev cdev = {
46d3476f7dSSasha Levin 	.mutex				= MUTEX_INITIALIZER,
47d7cfdc17SJean-Philippe Brucker 	.poll_cond			= PTHREAD_COND_INITIALIZER,
487512639bSAsias He 
4997bb5a9dSJonathan Austin 	.vq_ready			= 0,
5097bb5a9dSJonathan Austin 
51c85b4b4dSSasha Levin 	.config = {
529bd8ac5cSAsias He 		.cols			= 80,
539bd8ac5cSAsias He 		.rows			= 24,
549bd8ac5cSAsias He 		.max_nr_ports		= 1,
559bd8ac5cSAsias He 	},
569bd8ac5cSAsias He };
579bd8ac5cSAsias He 
58312c62d1SSasha Levin static int compat_id = -1;
59312c62d1SSasha Levin 
609bd8ac5cSAsias He /*
619bd8ac5cSAsias He  * Interrupts are injected for hvc0 only.
629bd8ac5cSAsias He  */
6343835ac9SSasha Levin static void virtio_console__inject_interrupt_callback(struct kvm *kvm, void *param)
649bd8ac5cSAsias He {
659bd8ac5cSAsias He 	struct iovec iov[VIRTIO_CONSOLE_QUEUE_SIZE];
669bd8ac5cSAsias He 	struct virt_queue *vq;
673fdf659dSSasha Levin 	u16 out, in;
683fdf659dSSasha Levin 	u16 head;
699bd8ac5cSAsias He 	int len;
709bd8ac5cSAsias He 
712651ea58SSasha Levin 	if (kvm->cfg.active_console != CONSOLE_VIRTIO)
722651ea58SSasha Levin 		return;
732651ea58SSasha Levin 
74a643306eSSasha Levin 	mutex_lock(&cdev.mutex);
757512639bSAsias He 
76b6638c22SSasha Levin 	vq = param;
779bd8ac5cSAsias He 
7897bb5a9dSJonathan Austin 	if (!cdev.vq_ready)
7997bb5a9dSJonathan Austin 		pthread_cond_wait(&cdev.poll_cond, &cdev.mutex.mutex);
8097bb5a9dSJonathan Austin 
812651ea58SSasha Levin 	if (term_readable(0) && virt_queue__available(vq)) {
8243835ac9SSasha Levin 		head = virt_queue__get_iov(vq, iov, &out, &in, kvm);
834346fd8fSSasha Levin 		len = term_getc_iov(kvm, iov, in, 0);
849bd8ac5cSAsias He 		virt_queue__set_used_elem(vq, head, len);
8502eca50cSAsias He 		cdev.vdev.ops->signal_vq(kvm, &cdev.vdev, vq - cdev.vqs);
869bd8ac5cSAsias He 	}
877512639bSAsias He 
88a643306eSSasha Levin 	mutex_unlock(&cdev.mutex);
899bd8ac5cSAsias He }
909bd8ac5cSAsias He 
9143835ac9SSasha Levin void virtio_console__inject_interrupt(struct kvm *kvm)
92b6638c22SSasha Levin {
9397bb5a9dSJonathan Austin 	virtio_console__inject_interrupt_callback(kvm,
9497bb5a9dSJonathan Austin 					&cdev.vqs[VIRTIO_CONSOLE_RX_QUEUE]);
95b6638c22SSasha Levin }
96b6638c22SSasha Levin 
9743835ac9SSasha Levin static void virtio_console_handle_callback(struct kvm *kvm, void *param)
989bd8ac5cSAsias He {
999bd8ac5cSAsias He 	struct iovec iov[VIRTIO_CONSOLE_QUEUE_SIZE];
1009bd8ac5cSAsias He 	struct virt_queue *vq;
1013fdf659dSSasha Levin 	u16 out, in;
1023fdf659dSSasha Levin 	u16 head;
1033fdf659dSSasha Levin 	u32 len;
1049bd8ac5cSAsias He 
105b6638c22SSasha Levin 	vq = param;
1069bd8ac5cSAsias He 
10720f0438aSAsias He 	/*
10820f0438aSAsias He 	 * The current Linux implementation polls for the buffer
10920f0438aSAsias He 	 * to be used, rather than waiting for an interrupt.
11020f0438aSAsias He 	 * So there is no need to inject an interrupt for the tx path.
11120f0438aSAsias He 	 */
11220f0438aSAsias He 
1139bd8ac5cSAsias He 	while (virt_queue__available(vq)) {
11443835ac9SSasha Levin 		head = virt_queue__get_iov(vq, iov, &out, &in, kvm);
1152651ea58SSasha Levin 		len = term_putc_iov(iov, out, 0);
1169bd8ac5cSAsias He 		virt_queue__set_used_elem(vq, head, len);
1179bd8ac5cSAsias He 	}
1189bd8ac5cSAsias He 
1199bd8ac5cSAsias He }
1209bd8ac5cSAsias He 
121c5ae742bSSasha Levin static u8 *get_config(struct kvm *kvm, void *dev)
1229bd8ac5cSAsias He {
123c85b4b4dSSasha Levin 	struct con_dev *cdev = dev;
1249bd8ac5cSAsias He 
125c5ae742bSSasha Levin 	return ((u8 *)(&cdev->config));
126c85b4b4dSSasha Levin }
127c85b4b4dSSasha Levin 
128c85b4b4dSSasha Levin static u32 get_host_features(struct kvm *kvm, void *dev)
129c85b4b4dSSasha Levin {
130c85b4b4dSSasha Levin 	return 0;
131c85b4b4dSSasha Levin }
132c85b4b4dSSasha Levin 
133c85b4b4dSSasha Levin static void set_guest_features(struct kvm *kvm, void *dev, u32 features)
134c85b4b4dSSasha Levin {
135bd360827SMarc Zyngier 	struct con_dev *cdev = dev;
136bd360827SMarc Zyngier 	struct virtio_console_config *conf = &cdev->config;
137bd360827SMarc Zyngier 
138bd360827SMarc Zyngier 	conf->cols = virtio_host_to_guest_u16(&cdev->vdev, conf->cols);
139bd360827SMarc Zyngier 	conf->rows = virtio_host_to_guest_u16(&cdev->vdev, conf->rows);
140bd360827SMarc Zyngier 	conf->max_nr_ports = virtio_host_to_guest_u32(&cdev->vdev, conf->max_nr_ports);
141c85b4b4dSSasha Levin }
142c85b4b4dSSasha Levin 
14395242e44SJean-Philippe Brucker static void notify_status(struct kvm *kvm, void *dev, u32 status)
14495242e44SJean-Philippe Brucker {
14595242e44SJean-Philippe Brucker }
14695242e44SJean-Philippe Brucker 
147c59ba304SWill Deacon static int init_vq(struct kvm *kvm, void *dev, u32 vq, u32 page_size, u32 align,
148c59ba304SWill Deacon 		   u32 pfn)
149c85b4b4dSSasha Levin {
1509bd8ac5cSAsias He 	struct virt_queue *queue;
1519bd8ac5cSAsias He 	void *p;
1529bd8ac5cSAsias He 
153a2857479SCyrill Gorcunov 	BUG_ON(vq >= VIRTIO_CONSOLE_NUM_QUEUES);
1549bd8ac5cSAsias He 
155312c62d1SSasha Levin 	compat__remove_message(compat_id);
156e59b1ed0SSasha Levin 
157c85b4b4dSSasha Levin 	queue		= &cdev.vqs[vq];
158c85b4b4dSSasha Levin 	queue->pfn	= pfn;
159e7e2950aSSasha Levin 	p		= virtio_get_vq(kvm, queue->pfn, page_size);
1609bd8ac5cSAsias He 
161c59ba304SWill Deacon 	vring_init(&queue->vring, VIRTIO_CONSOLE_QUEUE_SIZE, p, align);
162bd360827SMarc Zyngier 	virtio_init_device_vq(&cdev.vdev, queue);
1639bd8ac5cSAsias He 
16497bb5a9dSJonathan Austin 	if (vq == VIRTIO_CONSOLE_TX_QUEUE) {
165c85b4b4dSSasha Levin 		thread_pool__init_job(&cdev.jobs[vq], kvm, virtio_console_handle_callback, queue);
16697bb5a9dSJonathan Austin 	} else if (vq == VIRTIO_CONSOLE_RX_QUEUE) {
167c85b4b4dSSasha Levin 		thread_pool__init_job(&cdev.jobs[vq], kvm, virtio_console__inject_interrupt_callback, queue);
16897bb5a9dSJonathan Austin 		/* Tell the waiting poll thread that we're ready to go */
16997bb5a9dSJonathan Austin 		mutex_lock(&cdev.mutex);
17097bb5a9dSJonathan Austin 		cdev.vq_ready = 1;
17197bb5a9dSJonathan Austin 		pthread_cond_signal(&cdev.poll_cond);
17297bb5a9dSJonathan Austin 		mutex_unlock(&cdev.mutex);
17397bb5a9dSJonathan Austin 	}
174b6638c22SSasha Levin 
175c85b4b4dSSasha Levin 	return 0;
1769bd8ac5cSAsias He }
1779bd8ac5cSAsias He 
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 
187c85b4b4dSSasha Levin static int get_pfn_vq(struct kvm *kvm, void *dev, u32 vq)
188c85b4b4dSSasha Levin {
189c85b4b4dSSasha Levin 	struct con_dev *cdev = dev;
190c85b4b4dSSasha Levin 
191c85b4b4dSSasha Levin 	return cdev->vqs[vq].pfn;
192c85b4b4dSSasha Levin }
193c85b4b4dSSasha Levin 
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 
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 
205*b98ac591SJean-Philippe Brucker static int get_vq_count(struct kvm *kvm, void *dev)
206*b98ac591SJean-Philippe Brucker {
207*b98ac591SJean-Philippe Brucker 	return VIRTIO_CONSOLE_NUM_QUEUES;
208*b98ac591SJean-Philippe Brucker }
209*b98ac591SJean-Philippe Brucker 
21015542babSAndre Przywara static struct virtio_ops con_dev_virtio_ops = {
211c85b4b4dSSasha Levin 	.get_config		= get_config,
212c85b4b4dSSasha Levin 	.get_host_features	= get_host_features,
213c85b4b4dSSasha Levin 	.set_guest_features	= set_guest_features,
214*b98ac591SJean-Philippe Brucker 	.get_vq_count		= get_vq_count,
215c85b4b4dSSasha Levin 	.init_vq		= init_vq,
21695242e44SJean-Philippe Brucker 	.notify_status		= notify_status,
217c85b4b4dSSasha Levin 	.notify_vq		= notify_vq,
218c85b4b4dSSasha Levin 	.get_pfn_vq		= get_pfn_vq,
219c85b4b4dSSasha Levin 	.get_size_vq		= get_size_vq,
2207aba29c1SWill Deacon 	.set_size_vq		= set_size_vq,
221c85b4b4dSSasha Levin };
222e59b1ed0SSasha Levin 
223a3fa3f86SSasha Levin int virtio_console__init(struct kvm *kvm)
2241c47ce69SSasha Levin {
225a3fa3f86SSasha Levin 	if (kvm->cfg.active_console != CONSOLE_VIRTIO)
226a3fa3f86SSasha Levin 		return 0;
227a3fa3f86SSasha Levin 
22802eca50cSAsias He 	virtio_init(kvm, &cdev, &cdev.vdev, &con_dev_virtio_ops,
229d97dadecSWill Deacon 		    VIRTIO_DEFAULT_TRANS(kvm), PCI_DEVICE_ID_VIRTIO_CONSOLE,
230ae06ce71SWill Deacon 		    VIRTIO_ID_CONSOLE, PCI_CLASS_CONSOLE);
231d278197dSAsias He 	if (compat_id == -1)
23252f34d2cSAsias He 		compat_id = virtio_compat_add_message("virtio-console", "CONFIG_VIRTIO_CONSOLE");
233a3fa3f86SSasha Levin 
234a3fa3f86SSasha Levin 	return 0;
235a3fa3f86SSasha Levin }
23649a8afd1SSasha Levin virtio_dev_init(virtio_console__init);
237a3fa3f86SSasha Levin 
238a3fa3f86SSasha Levin int virtio_console__exit(struct kvm *kvm)
239a3fa3f86SSasha Levin {
240a3fa3f86SSasha Levin 	return 0;
2419bd8ac5cSAsias He }
24249a8afd1SSasha Levin virtio_dev_exit(virtio_console__exit);
243