xref: /kvmtool/virtio/console.c (revision 2449f6e369079b277e4e1b3759da0e8d73c690e2)
1416b2c2dSAsias He #include "kvm/virtio-console.h"
2416b2c2dSAsias He #include "kvm/virtio-pci.h"
331638bcaSCyrill Gorcunov #include "kvm/virtio-pci-dev.h"
49bd8ac5cSAsias He #include "kvm/disk-image.h"
539d6af07SAsias He #include "kvm/virtio.h"
69bd8ac5cSAsias He #include "kvm/ioport.h"
79bd8ac5cSAsias He #include "kvm/util.h"
89bd8ac5cSAsias He #include "kvm/term.h"
97512639bSAsias He #include "kvm/mutex.h"
109bd8ac5cSAsias He #include "kvm/kvm.h"
119bd8ac5cSAsias He #include "kvm/pci.h"
12b6638c22SSasha Levin #include "kvm/threadpool.h"
13*2449f6e3SSasha Levin #include "kvm/irq.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 <assert.h>
249bd8ac5cSAsias He #include <unistd.h>
259bd8ac5cSAsias He #include <fcntl.h>
269bd8ac5cSAsias He 
279bd8ac5cSAsias He #define VIRTIO_CONSOLE_QUEUE_SIZE	128
289bd8ac5cSAsias He #define VIRTIO_CONSOLE_NUM_QUEUES	2
299bd8ac5cSAsias He #define VIRTIO_CONSOLE_RX_QUEUE		0
309bd8ac5cSAsias He #define VIRTIO_CONSOLE_TX_QUEUE		1
319bd8ac5cSAsias He 
32*2449f6e3SSasha Levin static struct pci_device_header virtio_console_pci_device = {
33*2449f6e3SSasha Levin 	.vendor_id		= PCI_VENDOR_ID_REDHAT_QUMRANET,
34*2449f6e3SSasha Levin 	.device_id		= PCI_DEVICE_ID_VIRTIO_CONSOLE,
35*2449f6e3SSasha Levin 	.header_type		= PCI_HEADER_TYPE_NORMAL,
36*2449f6e3SSasha Levin 	.revision_id		= 0,
37*2449f6e3SSasha Levin 	.class			= 0x078000,
38*2449f6e3SSasha Levin 	.subsys_vendor_id	= PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET,
39*2449f6e3SSasha Levin 	.subsys_id		= PCI_SUBSYSTEM_ID_VIRTIO_CONSOLE,
40*2449f6e3SSasha Levin 	.bar[0]			= IOPORT_VIRTIO_CONSOLE | PCI_BASE_ADDRESS_SPACE_IO,
41*2449f6e3SSasha Levin };
42*2449f6e3SSasha Levin 
43a643306eSSasha Levin struct con_dev {
447512639bSAsias He 	pthread_mutex_t			mutex;
457512639bSAsias He 
469bd8ac5cSAsias He 	struct virt_queue		vqs[VIRTIO_CONSOLE_NUM_QUEUES];
479bd8ac5cSAsias He 	struct virtio_console_config	console_config;
483fdf659dSSasha Levin 	u32				host_features;
493fdf659dSSasha Levin 	u32				guest_features;
503fdf659dSSasha Levin 	u16				config_vector;
513fdf659dSSasha Levin 	u8				status;
523fdf659dSSasha Levin 	u16				queue_selector;
53b6638c22SSasha Levin 
54b6638c22SSasha Levin 	void				*jobs[VIRTIO_CONSOLE_NUM_QUEUES];
559bd8ac5cSAsias He };
569bd8ac5cSAsias He 
57a643306eSSasha Levin static struct con_dev cdev = {
587512639bSAsias He 	.mutex				= PTHREAD_MUTEX_INITIALIZER,
597512639bSAsias He 
609bd8ac5cSAsias He 	.console_config = {
619bd8ac5cSAsias He 		.cols			= 80,
629bd8ac5cSAsias He 		.rows			= 24,
639bd8ac5cSAsias He 		.max_nr_ports		= 1,
649bd8ac5cSAsias He 	},
659bd8ac5cSAsias He 
669bd8ac5cSAsias He 	.host_features			= 0,
679bd8ac5cSAsias He };
689bd8ac5cSAsias He 
699bd8ac5cSAsias He /*
709bd8ac5cSAsias He  * Interrupts are injected for hvc0 only.
719bd8ac5cSAsias He  */
72b6638c22SSasha Levin static void virtio_console__inject_interrupt_callback(struct kvm *self, void *param)
739bd8ac5cSAsias He {
749bd8ac5cSAsias He 	struct iovec iov[VIRTIO_CONSOLE_QUEUE_SIZE];
759bd8ac5cSAsias He 	struct virt_queue *vq;
763fdf659dSSasha Levin 	u16 out, in;
773fdf659dSSasha Levin 	u16 head;
789bd8ac5cSAsias He 	int len;
799bd8ac5cSAsias He 
80a643306eSSasha Levin 	mutex_lock(&cdev.mutex);
817512639bSAsias He 
82b6638c22SSasha Levin 	vq = param;
839bd8ac5cSAsias He 
849bd8ac5cSAsias He 	if (term_readable(CONSOLE_VIRTIO) && virt_queue__available(vq)) {
859bd8ac5cSAsias He 		head = virt_queue__get_iov(vq, iov, &out, &in, self);
869bd8ac5cSAsias He 		len = term_getc_iov(CONSOLE_VIRTIO, iov, in);
879bd8ac5cSAsias He 		virt_queue__set_used_elem(vq, head, len);
88*2449f6e3SSasha Levin 		kvm__irq_line(self, virtio_console_pci_device.irq_line, 1);
899bd8ac5cSAsias He 	}
907512639bSAsias He 
91a643306eSSasha Levin 	mutex_unlock(&cdev.mutex);
929bd8ac5cSAsias He }
939bd8ac5cSAsias He 
94b6638c22SSasha Levin void virtio_console__inject_interrupt(struct kvm *self)
95b6638c22SSasha Levin {
96a643306eSSasha Levin 	thread_pool__do_job(cdev.jobs[VIRTIO_CONSOLE_RX_QUEUE]);
97b6638c22SSasha Levin }
98b6638c22SSasha Levin 
993fdf659dSSasha Levin static bool virtio_console_pci_io_device_specific_in(void *data, unsigned long offset, int size, u32 count)
1009bd8ac5cSAsias He {
101a643306eSSasha Levin 	u8 *config_space = (u8 *) &cdev.console_config;
1029bd8ac5cSAsias He 
1039bd8ac5cSAsias He 	if (size != 1 || count != 1)
1049bd8ac5cSAsias He 		return false;
1059bd8ac5cSAsias He 
1069bd8ac5cSAsias He 	if ((offset - VIRTIO_PCI_CONFIG_NOMSI) > sizeof(struct virtio_console_config))
1079bd8ac5cSAsias He 		error("config offset is too big: %li", offset - VIRTIO_PCI_CONFIG_NOMSI);
1089bd8ac5cSAsias He 
1099bd8ac5cSAsias He 	ioport__write8(data, config_space[offset - VIRTIO_PCI_CONFIG_NOMSI]);
1109bd8ac5cSAsias He 
1119bd8ac5cSAsias He 	return true;
1129bd8ac5cSAsias He }
1139bd8ac5cSAsias He 
1143fdf659dSSasha Levin static bool virtio_console_pci_io_in(struct kvm *self, u16 port, void *data, int size, u32 count)
1159bd8ac5cSAsias He {
1169bd8ac5cSAsias He 	unsigned long offset = port - IOPORT_VIRTIO_CONSOLE;
1177512639bSAsias He 	bool ret = true;
1187512639bSAsias He 
119a643306eSSasha Levin 	mutex_lock(&cdev.mutex);
1209bd8ac5cSAsias He 
1219bd8ac5cSAsias He 	switch (offset) {
1229bd8ac5cSAsias He 	case VIRTIO_PCI_HOST_FEATURES:
123a643306eSSasha Levin 		ioport__write32(data, cdev.host_features);
1249bd8ac5cSAsias He 		break;
1259bd8ac5cSAsias He 	case VIRTIO_PCI_GUEST_FEATURES:
1267512639bSAsias He 		ret = false;
1277512639bSAsias He 		break;
1289bd8ac5cSAsias He 	case VIRTIO_PCI_QUEUE_PFN:
129a643306eSSasha Levin 		ioport__write32(data, cdev.vqs[cdev.queue_selector].pfn);
1309bd8ac5cSAsias He 		break;
1319bd8ac5cSAsias He 	case VIRTIO_PCI_QUEUE_NUM:
1329bd8ac5cSAsias He 		ioport__write16(data, VIRTIO_CONSOLE_QUEUE_SIZE);
1339bd8ac5cSAsias He 		break;
1349bd8ac5cSAsias He 	case VIRTIO_PCI_QUEUE_SEL:
1359bd8ac5cSAsias He 	case VIRTIO_PCI_QUEUE_NOTIFY:
1367512639bSAsias He 		ret = false;
1377512639bSAsias He 		break;
1389bd8ac5cSAsias He 	case VIRTIO_PCI_STATUS:
139a643306eSSasha Levin 		ioport__write8(data, cdev.status);
1409bd8ac5cSAsias He 		break;
1419bd8ac5cSAsias He 	case VIRTIO_PCI_ISR:
1429bd8ac5cSAsias He 		ioport__write8(data, 0x1);
143*2449f6e3SSasha Levin 		kvm__irq_line(self, virtio_console_pci_device.irq_line, 0);
1449bd8ac5cSAsias He 		break;
1459bd8ac5cSAsias He 	case VIRTIO_MSI_CONFIG_VECTOR:
146a643306eSSasha Levin 		ioport__write16(data, cdev.config_vector);
1479bd8ac5cSAsias He 		break;
1489bd8ac5cSAsias He 	default:
1497512639bSAsias He 		ret = virtio_console_pci_io_device_specific_in(data, offset, size, count);
1509bd8ac5cSAsias He 	};
1519bd8ac5cSAsias He 
152a643306eSSasha Levin 	mutex_unlock(&cdev.mutex);
1537512639bSAsias He 
1547512639bSAsias He 	return ret;
1559bd8ac5cSAsias He }
1569bd8ac5cSAsias He 
157b6638c22SSasha Levin static void virtio_console_handle_callback(struct kvm *self, void *param)
1589bd8ac5cSAsias He {
1599bd8ac5cSAsias He 	struct iovec iov[VIRTIO_CONSOLE_QUEUE_SIZE];
1609bd8ac5cSAsias He 	struct virt_queue *vq;
1613fdf659dSSasha Levin 	u16 out, in;
1623fdf659dSSasha Levin 	u16 head;
1633fdf659dSSasha Levin 	u32 len;
1649bd8ac5cSAsias He 
165b6638c22SSasha Levin 	vq = param;
1669bd8ac5cSAsias He 
1679bd8ac5cSAsias He 	while (virt_queue__available(vq)) {
1689bd8ac5cSAsias He 		head = virt_queue__get_iov(vq, iov, &out, &in, self);
1699bd8ac5cSAsias He 		len = term_putc_iov(CONSOLE_VIRTIO, iov, out);
1709bd8ac5cSAsias He 		virt_queue__set_used_elem(vq, head, len);
1719bd8ac5cSAsias He 	}
1729bd8ac5cSAsias He 
173*2449f6e3SSasha Levin 	kvm__irq_line(self, virtio_console_pci_device.irq_line, 1);
1749bd8ac5cSAsias He }
1759bd8ac5cSAsias He 
1763fdf659dSSasha Levin static bool virtio_console_pci_io_out(struct kvm *self, u16 port, void *data, int size, u32 count)
1779bd8ac5cSAsias He {
1789bd8ac5cSAsias He 	unsigned long offset = port - IOPORT_VIRTIO_CONSOLE;
1797512639bSAsias He 	bool ret = true;
1809bd8ac5cSAsias He 
181a643306eSSasha Levin 	mutex_lock(&cdev.mutex);
182e147d838SAsias He 
1839bd8ac5cSAsias He 	switch (offset) {
1849bd8ac5cSAsias He 	case VIRTIO_PCI_GUEST_FEATURES:
185a643306eSSasha Levin 		cdev.guest_features	= ioport__read32(data);
1869bd8ac5cSAsias He 		break;
1879bd8ac5cSAsias He 	case VIRTIO_PCI_QUEUE_PFN: {
1889bd8ac5cSAsias He 		struct virt_queue *queue;
1899bd8ac5cSAsias He 		void *p;
1909bd8ac5cSAsias He 
191a643306eSSasha Levin 		assert(cdev.queue_selector < VIRTIO_CONSOLE_NUM_QUEUES);
1929bd8ac5cSAsias He 
193a643306eSSasha Levin 		queue			= &cdev.vqs[cdev.queue_selector];
1949bd8ac5cSAsias He 		queue->pfn		= ioport__read32(data);
1959bd8ac5cSAsias He 		p			= guest_flat_to_host(self, queue->pfn << 12);
1969bd8ac5cSAsias He 
1979bd8ac5cSAsias He 		vring_init(&queue->vring, VIRTIO_CONSOLE_QUEUE_SIZE, p, 4096);
1989bd8ac5cSAsias He 
199a643306eSSasha Levin 		if (cdev.queue_selector == VIRTIO_CONSOLE_TX_QUEUE)
200407475bfSPekka Enberg 			cdev.jobs[cdev.queue_selector] = thread_pool__add_job(self, virtio_console_handle_callback, queue);
201a643306eSSasha Levin 		else if (cdev.queue_selector == VIRTIO_CONSOLE_RX_QUEUE)
202407475bfSPekka Enberg 			cdev.jobs[cdev.queue_selector] = thread_pool__add_job(self, virtio_console__inject_interrupt_callback, queue);
203b6638c22SSasha Levin 
2049bd8ac5cSAsias He 		break;
2059bd8ac5cSAsias He 	}
2069bd8ac5cSAsias He 	case VIRTIO_PCI_QUEUE_SEL:
207a643306eSSasha Levin 		cdev.queue_selector	= ioport__read16(data);
2089bd8ac5cSAsias He 		break;
2099bd8ac5cSAsias He 	case VIRTIO_PCI_QUEUE_NOTIFY: {
210a643306eSSasha Levin 		u16 queue_index		= ioport__read16(data);
211a643306eSSasha Levin 		thread_pool__do_job(cdev.jobs[queue_index]);
2129bd8ac5cSAsias He 		break;
2139bd8ac5cSAsias He 	}
2149bd8ac5cSAsias He 	case VIRTIO_PCI_STATUS:
215a643306eSSasha Levin 		cdev.status		= ioport__read8(data);
2169bd8ac5cSAsias He 		break;
2179bd8ac5cSAsias He 	case VIRTIO_MSI_CONFIG_VECTOR:
218a643306eSSasha Levin 		cdev.config_vector	= VIRTIO_MSI_NO_VECTOR;
2199bd8ac5cSAsias He 		break;
2209bd8ac5cSAsias He 	case VIRTIO_MSI_QUEUE_VECTOR:
2219bd8ac5cSAsias He 		break;
2229bd8ac5cSAsias He 	default:
2237512639bSAsias He 		ret			= false;
224407475bfSPekka Enberg 		break;
2259bd8ac5cSAsias He 	};
2269bd8ac5cSAsias He 
227a643306eSSasha Levin 	mutex_unlock(&cdev.mutex);
228407475bfSPekka Enberg 
2297512639bSAsias He 	return ret;
2309bd8ac5cSAsias He }
2319bd8ac5cSAsias He 
2329bd8ac5cSAsias He static struct ioport_operations virtio_console_io_ops = {
2339bd8ac5cSAsias He 	.io_in			= virtio_console_pci_io_in,
2349bd8ac5cSAsias He 	.io_out			= virtio_console_pci_io_out,
2359bd8ac5cSAsias He };
2369bd8ac5cSAsias He 
2379bd8ac5cSAsias He void virtio_console__init(struct kvm *self)
2389bd8ac5cSAsias He {
239*2449f6e3SSasha Levin 	u8 dev, line, pin;
240*2449f6e3SSasha Levin 
241*2449f6e3SSasha Levin 	if (irq__register_device(PCI_DEVICE_ID_VIRTIO_CONSOLE, &dev, &pin, &line) < 0)
242*2449f6e3SSasha Levin 		return;
243*2449f6e3SSasha Levin 
244*2449f6e3SSasha Levin 	virtio_console_pci_device.irq_pin	= pin;
245*2449f6e3SSasha Levin 	virtio_console_pci_device.irq_line	= line;
246*2449f6e3SSasha Levin 	pci__register(&virtio_console_pci_device, dev);
2479bd8ac5cSAsias He 	ioport__register(IOPORT_VIRTIO_CONSOLE, &virtio_console_io_ops, IOPORT_VIRTIO_CONSOLE_SIZE);
2489bd8ac5cSAsias He }
249