xref: /kvmtool/virtio/console.c (revision 39d6af073da0205a2714988fb1ba709a77bbc976)
19bd8ac5cSAsias He #include "kvm/console-virtio.h"
29bd8ac5cSAsias He #include "kvm/virtio_pci.h"
39bd8ac5cSAsias He #include "kvm/disk-image.h"
4*39d6af07SAsias He #include "kvm/virtio.h"
59bd8ac5cSAsias He #include "kvm/ioport.h"
69bd8ac5cSAsias He #include "kvm/util.h"
79bd8ac5cSAsias He #include "kvm/term.h"
89bd8ac5cSAsias He #include "kvm/kvm.h"
99bd8ac5cSAsias He #include "kvm/pci.h"
109bd8ac5cSAsias He 
119bd8ac5cSAsias He #include <linux/virtio_console.h>
129bd8ac5cSAsias He #include <linux/virtio_ring.h>
139bd8ac5cSAsias He #include <linux/virtio_blk.h>
149bd8ac5cSAsias He 
159bd8ac5cSAsias He #include <sys/uio.h>
169bd8ac5cSAsias He #include <sys/types.h>
179bd8ac5cSAsias He #include <sys/stat.h>
189bd8ac5cSAsias He #include <inttypes.h>
199bd8ac5cSAsias He #include <termios.h>
209bd8ac5cSAsias He #include <assert.h>
219bd8ac5cSAsias He #include <unistd.h>
229bd8ac5cSAsias He #include <fcntl.h>
239bd8ac5cSAsias He 
249bd8ac5cSAsias He #define VIRTIO_CONSOLE_IRQ		14
259bd8ac5cSAsias He #define VIRTIO_CONSOLE_QUEUE_SIZE	128
269bd8ac5cSAsias He #define VIRTIO_CONSOLE_NUM_QUEUES	2
279bd8ac5cSAsias He #define VIRTIO_CONSOLE_RX_QUEUE		0
289bd8ac5cSAsias He #define VIRTIO_CONSOLE_TX_QUEUE		1
299bd8ac5cSAsias He #define PCI_VIRTIO_CONSOLE_DEVNUM	2
309bd8ac5cSAsias He 
319bd8ac5cSAsias He struct console_device {
329bd8ac5cSAsias He 	struct virt_queue		vqs[VIRTIO_CONSOLE_NUM_QUEUES];
339bd8ac5cSAsias He 	struct virtio_console_config	console_config;
349bd8ac5cSAsias He 	uint32_t			host_features;
359bd8ac5cSAsias He 	uint32_t			guest_features;
369bd8ac5cSAsias He 	uint16_t			config_vector;
379bd8ac5cSAsias He 	uint8_t				status;
389bd8ac5cSAsias He 	uint16_t			queue_selector;
399bd8ac5cSAsias He };
409bd8ac5cSAsias He 
419bd8ac5cSAsias He static struct console_device console_device = {
429bd8ac5cSAsias He 	.console_config = {
439bd8ac5cSAsias He 		.cols		= 80,
449bd8ac5cSAsias He 		.rows		= 24,
459bd8ac5cSAsias He 		.max_nr_ports	= 1,
469bd8ac5cSAsias He 	},
479bd8ac5cSAsias He 
489bd8ac5cSAsias He 	.host_features		= 0,
499bd8ac5cSAsias He };
509bd8ac5cSAsias He 
519bd8ac5cSAsias He /*
529bd8ac5cSAsias He  * Interrupts are injected for hvc0 only.
539bd8ac5cSAsias He  */
549bd8ac5cSAsias He void virtio_console__inject_interrupt(struct kvm *self)
559bd8ac5cSAsias He {
569bd8ac5cSAsias He 	struct iovec iov[VIRTIO_CONSOLE_QUEUE_SIZE];
579bd8ac5cSAsias He 	struct virt_queue *vq;
589bd8ac5cSAsias He 	uint16_t out, in;
599bd8ac5cSAsias He 	uint16_t head;
609bd8ac5cSAsias He 	int len;
619bd8ac5cSAsias He 
629bd8ac5cSAsias He 	vq = &console_device.vqs[VIRTIO_CONSOLE_RX_QUEUE];
639bd8ac5cSAsias He 
649bd8ac5cSAsias He 	if (term_readable(CONSOLE_VIRTIO) && virt_queue__available(vq)) {
659bd8ac5cSAsias He 		head = virt_queue__get_iov(vq, iov, &out, &in, self);
669bd8ac5cSAsias He 		len = term_getc_iov(CONSOLE_VIRTIO, iov, in);
679bd8ac5cSAsias He 		virt_queue__set_used_elem(vq, head, len);
689bd8ac5cSAsias He 		kvm__irq_line(self, VIRTIO_CONSOLE_IRQ, 1);
699bd8ac5cSAsias He 	}
709bd8ac5cSAsias He }
719bd8ac5cSAsias He 
729bd8ac5cSAsias He static bool virtio_console_pci_io_device_specific_in(void *data, unsigned long offset, int size, uint32_t count)
739bd8ac5cSAsias He {
749bd8ac5cSAsias He 	uint8_t *config_space = (uint8_t *) &console_device.console_config;
759bd8ac5cSAsias He 
769bd8ac5cSAsias He 	if (size != 1 || count != 1)
779bd8ac5cSAsias He 		return false;
789bd8ac5cSAsias He 
799bd8ac5cSAsias He 	if ((offset - VIRTIO_PCI_CONFIG_NOMSI) > sizeof(struct virtio_console_config))
809bd8ac5cSAsias He 		error("config offset is too big: %li", offset - VIRTIO_PCI_CONFIG_NOMSI);
819bd8ac5cSAsias He 
829bd8ac5cSAsias He 	ioport__write8(data, config_space[offset - VIRTIO_PCI_CONFIG_NOMSI]);
839bd8ac5cSAsias He 
849bd8ac5cSAsias He 	return true;
859bd8ac5cSAsias He }
869bd8ac5cSAsias He 
879bd8ac5cSAsias He static bool virtio_console_pci_io_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
889bd8ac5cSAsias He {
899bd8ac5cSAsias He 	unsigned long offset = port - IOPORT_VIRTIO_CONSOLE;
909bd8ac5cSAsias He 
919bd8ac5cSAsias He 	switch (offset) {
929bd8ac5cSAsias He 	case VIRTIO_PCI_HOST_FEATURES:
939bd8ac5cSAsias He 		ioport__write32(data, console_device.host_features);
949bd8ac5cSAsias He 		break;
959bd8ac5cSAsias He 	case VIRTIO_PCI_GUEST_FEATURES:
969bd8ac5cSAsias He 		return false;
979bd8ac5cSAsias He 	case VIRTIO_PCI_QUEUE_PFN:
989bd8ac5cSAsias He 		ioport__write32(data, console_device.vqs[console_device.queue_selector].pfn);
999bd8ac5cSAsias He 		break;
1009bd8ac5cSAsias He 	case VIRTIO_PCI_QUEUE_NUM:
1019bd8ac5cSAsias He 		ioport__write16(data, VIRTIO_CONSOLE_QUEUE_SIZE);
1029bd8ac5cSAsias He 		break;
1039bd8ac5cSAsias He 	case VIRTIO_PCI_QUEUE_SEL:
1049bd8ac5cSAsias He 	case VIRTIO_PCI_QUEUE_NOTIFY:
1059bd8ac5cSAsias He 		return false;
1069bd8ac5cSAsias He 	case VIRTIO_PCI_STATUS:
1079bd8ac5cSAsias He 		ioport__write8(data, console_device.status);
1089bd8ac5cSAsias He 		break;
1099bd8ac5cSAsias He 	case VIRTIO_PCI_ISR:
1109bd8ac5cSAsias He 		ioport__write8(data, 0x1);
1119bd8ac5cSAsias He 		kvm__irq_line(self, VIRTIO_CONSOLE_IRQ, 0);
1129bd8ac5cSAsias He 		break;
1139bd8ac5cSAsias He 	case VIRTIO_MSI_CONFIG_VECTOR:
1149bd8ac5cSAsias He 		ioport__write16(data, console_device.config_vector);
1159bd8ac5cSAsias He 		break;
1169bd8ac5cSAsias He 	default:
1179bd8ac5cSAsias He 		return virtio_console_pci_io_device_specific_in(data, offset, size, count);
1189bd8ac5cSAsias He 	};
1199bd8ac5cSAsias He 
1209bd8ac5cSAsias He 	return true;
1219bd8ac5cSAsias He }
1229bd8ac5cSAsias He 
1239bd8ac5cSAsias He static void virtio_console_handle_callback(struct kvm *self, uint16_t queue_index)
1249bd8ac5cSAsias He {
1259bd8ac5cSAsias He 	struct iovec iov[VIRTIO_CONSOLE_QUEUE_SIZE];
1269bd8ac5cSAsias He 	struct virt_queue *vq;
1279bd8ac5cSAsias He 	uint16_t out, in;
1289bd8ac5cSAsias He 	uint16_t head;
1299bd8ac5cSAsias He 	uint32_t len;
1309bd8ac5cSAsias He 
1319bd8ac5cSAsias He 	vq = &console_device.vqs[queue_index];
1329bd8ac5cSAsias He 
1339bd8ac5cSAsias He 	if (queue_index == VIRTIO_CONSOLE_TX_QUEUE) {
1349bd8ac5cSAsias He 
1359bd8ac5cSAsias He 		while (virt_queue__available(vq)) {
1369bd8ac5cSAsias He 			head = virt_queue__get_iov(vq, iov, &out, &in, self);
1379bd8ac5cSAsias He 			len = term_putc_iov(CONSOLE_VIRTIO, iov, out);
1389bd8ac5cSAsias He 			virt_queue__set_used_elem(vq, head, len);
1399bd8ac5cSAsias He 		}
1409bd8ac5cSAsias He 
1419bd8ac5cSAsias He 		kvm__irq_line(self, VIRTIO_CONSOLE_IRQ, 1);
1429bd8ac5cSAsias He 	}
1439bd8ac5cSAsias He }
1449bd8ac5cSAsias He 
1459bd8ac5cSAsias He static bool virtio_console_pci_io_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
1469bd8ac5cSAsias He {
1479bd8ac5cSAsias He 	unsigned long offset = port - IOPORT_VIRTIO_CONSOLE;
1489bd8ac5cSAsias He 
1499bd8ac5cSAsias He 	switch (offset) {
1509bd8ac5cSAsias He 	case VIRTIO_PCI_GUEST_FEATURES:
1519bd8ac5cSAsias He 		console_device.guest_features	= ioport__read32(data);
1529bd8ac5cSAsias He 		break;
1539bd8ac5cSAsias He 	case VIRTIO_PCI_QUEUE_PFN: {
1549bd8ac5cSAsias He 		struct virt_queue *queue;
1559bd8ac5cSAsias He 		void *p;
1569bd8ac5cSAsias He 
1579bd8ac5cSAsias He 		assert(console_device.queue_selector < VIRTIO_CONSOLE_NUM_QUEUES);
1589bd8ac5cSAsias He 
1599bd8ac5cSAsias He 		queue		= &console_device.vqs[console_device.queue_selector];
1609bd8ac5cSAsias He 		queue->pfn	= ioport__read32(data);
1619bd8ac5cSAsias He 		p		= guest_flat_to_host(self, queue->pfn << 12);
1629bd8ac5cSAsias He 
1639bd8ac5cSAsias He 		vring_init(&queue->vring, VIRTIO_CONSOLE_QUEUE_SIZE, p, 4096);
1649bd8ac5cSAsias He 
1659bd8ac5cSAsias He 		break;
1669bd8ac5cSAsias He 	}
1679bd8ac5cSAsias He 	case VIRTIO_PCI_QUEUE_SEL:
1689bd8ac5cSAsias He 		console_device.queue_selector	= ioport__read16(data);
1699bd8ac5cSAsias He 		break;
1709bd8ac5cSAsias He 	case VIRTIO_PCI_QUEUE_NOTIFY: {
1719bd8ac5cSAsias He 		uint16_t queue_index;
1729bd8ac5cSAsias He 		queue_index	= ioport__read16(data);
1739bd8ac5cSAsias He 		virtio_console_handle_callback(self, queue_index);
1749bd8ac5cSAsias He 		break;
1759bd8ac5cSAsias He 	}
1769bd8ac5cSAsias He 	case VIRTIO_PCI_STATUS:
1779bd8ac5cSAsias He 		console_device.status		= ioport__read8(data);
1789bd8ac5cSAsias He 		break;
1799bd8ac5cSAsias He 	case VIRTIO_MSI_CONFIG_VECTOR:
1809bd8ac5cSAsias He 		console_device.config_vector	= VIRTIO_MSI_NO_VECTOR;
1819bd8ac5cSAsias He 		break;
1829bd8ac5cSAsias He 	case VIRTIO_MSI_QUEUE_VECTOR:
1839bd8ac5cSAsias He 		break;
1849bd8ac5cSAsias He 	default:
1859bd8ac5cSAsias He 		return false;
1869bd8ac5cSAsias He 	};
1879bd8ac5cSAsias He 
1889bd8ac5cSAsias He 	return true;
1899bd8ac5cSAsias He }
1909bd8ac5cSAsias He 
1919bd8ac5cSAsias He static struct ioport_operations virtio_console_io_ops = {
1929bd8ac5cSAsias He 	.io_in	= virtio_console_pci_io_in,
1939bd8ac5cSAsias He 	.io_out	= virtio_console_pci_io_out,
1949bd8ac5cSAsias He };
1959bd8ac5cSAsias He 
1969bd8ac5cSAsias He #define PCI_VENDOR_ID_REDHAT_QUMRANET		0x1af4
1979bd8ac5cSAsias He #define PCI_DEVICE_ID_VIRTIO_CONSOLE		0x1002
1989bd8ac5cSAsias He #define PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET	0x1af4
1999bd8ac5cSAsias He #define PCI_SUBSYSTEM_ID_VIRTIO_CONSOLE		0x0003
2009bd8ac5cSAsias He 
2019bd8ac5cSAsias He static struct pci_device_header virtio_console_pci_device = {
2029bd8ac5cSAsias He 	.vendor_id		= PCI_VENDOR_ID_REDHAT_QUMRANET,
2039bd8ac5cSAsias He 	.device_id		= PCI_DEVICE_ID_VIRTIO_CONSOLE,
2049bd8ac5cSAsias He 	.header_type		= PCI_HEADER_TYPE_NORMAL,
2059bd8ac5cSAsias He 	.revision_id		= 0,
2069bd8ac5cSAsias He 	.class			= (0x07 << 8) | (0x80 << 4) | (0x0 << 0),
2079bd8ac5cSAsias He 	.subsys_vendor_id	= PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET,
2089bd8ac5cSAsias He 	.subsys_id		= PCI_SUBSYSTEM_ID_VIRTIO_CONSOLE,
2099bd8ac5cSAsias He 	.bar[0]			= IOPORT_VIRTIO_CONSOLE | PCI_BASE_ADDRESS_SPACE_IO,
2109bd8ac5cSAsias He 	.irq_pin		= 3,
2119bd8ac5cSAsias He 	.irq_line		= VIRTIO_CONSOLE_IRQ,
2129bd8ac5cSAsias He };
2139bd8ac5cSAsias He 
2149bd8ac5cSAsias He void virtio_console__init(struct kvm *self)
2159bd8ac5cSAsias He {
2169bd8ac5cSAsias He 	pci__register(&virtio_console_pci_device, PCI_VIRTIO_CONSOLE_DEVNUM);
2179bd8ac5cSAsias He 	ioport__register(IOPORT_VIRTIO_CONSOLE, &virtio_console_io_ops, IOPORT_VIRTIO_CONSOLE_SIZE);
2189bd8ac5cSAsias He }
219