xref: /kvmtool/virtio/pci.c (revision 1599d7242db63a7742d5a526cd9476357d7e1e92)
136f5dc91SSasha Levin #include "kvm/virtio-pci.h"
236f5dc91SSasha Levin 
336f5dc91SSasha Levin #include "kvm/ioport.h"
436f5dc91SSasha Levin #include "kvm/kvm.h"
536f5dc91SSasha Levin #include "kvm/virtio-pci-dev.h"
636f5dc91SSasha Levin #include "kvm/irq.h"
736f5dc91SSasha Levin #include "kvm/virtio.h"
8*1599d724SSasha Levin #include "kvm/ioeventfd.h"
936f5dc91SSasha Levin 
1036f5dc91SSasha Levin #include <linux/virtio_pci.h>
1136f5dc91SSasha Levin #include <string.h>
1236f5dc91SSasha Levin 
13*1599d724SSasha Levin static void virtio_pci__ioevent_callback(struct kvm *kvm, void *param)
14*1599d724SSasha Levin {
15*1599d724SSasha Levin 	struct virtio_pci_ioevent_param *ioeventfd = param;
16*1599d724SSasha Levin 
17*1599d724SSasha Levin 	ioeventfd->vpci->ops.notify_vq(kvm, ioeventfd->vpci->dev, ioeventfd->vq);
18*1599d724SSasha Levin }
19*1599d724SSasha Levin 
20*1599d724SSasha Levin static int virtio_pci__init_ioeventfd(struct kvm *kvm, struct virtio_pci *vpci, u32 vq)
21*1599d724SSasha Levin {
22*1599d724SSasha Levin 	struct ioevent ioevent;
23*1599d724SSasha Levin 
24*1599d724SSasha Levin 	vpci->ioeventfds[vq] = (struct virtio_pci_ioevent_param) {
25*1599d724SSasha Levin 		.vpci		= vpci,
26*1599d724SSasha Levin 		.vq		= vq,
27*1599d724SSasha Levin 	};
28*1599d724SSasha Levin 
29*1599d724SSasha Levin 	ioevent = (struct ioevent) {
30*1599d724SSasha Levin 		.io_addr	= vpci->base_addr + VIRTIO_PCI_QUEUE_NOTIFY,
31*1599d724SSasha Levin 		.io_len		= sizeof(u16),
32*1599d724SSasha Levin 		.fn		= virtio_pci__ioevent_callback,
33*1599d724SSasha Levin 		.fn_ptr		= &vpci->ioeventfds[vq],
34*1599d724SSasha Levin 		.datamatch	= vq,
35*1599d724SSasha Levin 		.fn_kvm		= kvm,
36*1599d724SSasha Levin 		.fd		= eventfd(0, 0),
37*1599d724SSasha Levin 	};
38*1599d724SSasha Levin 
39*1599d724SSasha Levin 	ioeventfd__add_event(&ioevent);
40*1599d724SSasha Levin 
41*1599d724SSasha Levin 	return 0;
42*1599d724SSasha Levin }
43*1599d724SSasha Levin 
4436f5dc91SSasha Levin static bool virtio_pci__specific_io_in(struct kvm *kvm, struct virtio_pci *vpci, u16 port,
4536f5dc91SSasha Levin 					void *data, int size, int offset)
4636f5dc91SSasha Levin {
4736f5dc91SSasha Levin 	u32 config_offset;
4836f5dc91SSasha Levin 	int type = virtio__get_dev_specific_field(offset - 20, vpci->msix_enabled,
4936f5dc91SSasha Levin 							0, &config_offset);
5036f5dc91SSasha Levin 	if (type == VIRTIO_PCI_O_MSIX) {
5136f5dc91SSasha Levin 		switch (offset) {
5236f5dc91SSasha Levin 		case VIRTIO_MSI_CONFIG_VECTOR:
5336f5dc91SSasha Levin 			ioport__write16(data, vpci->config_vector);
5436f5dc91SSasha Levin 			break;
5536f5dc91SSasha Levin 		case VIRTIO_MSI_QUEUE_VECTOR:
5636f5dc91SSasha Levin 			ioport__write16(data, vpci->vq_vector[vpci->queue_selector]);
5736f5dc91SSasha Levin 			break;
5836f5dc91SSasha Levin 		};
5936f5dc91SSasha Levin 
6036f5dc91SSasha Levin 		return true;
6136f5dc91SSasha Levin 	} else if (type == VIRTIO_PCI_O_CONFIG) {
6236f5dc91SSasha Levin 		u8 cfg;
6336f5dc91SSasha Levin 
6436f5dc91SSasha Levin 		cfg = vpci->ops.get_config(kvm, vpci->dev, config_offset);
6536f5dc91SSasha Levin 		ioport__write8(data, cfg);
6636f5dc91SSasha Levin 		return true;
6736f5dc91SSasha Levin 	}
6836f5dc91SSasha Levin 
6936f5dc91SSasha Levin 	return false;
7036f5dc91SSasha Levin }
7136f5dc91SSasha Levin 
7236f5dc91SSasha Levin static bool virtio_pci__io_in(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size)
7336f5dc91SSasha Levin {
7436f5dc91SSasha Levin 	unsigned long offset;
7536f5dc91SSasha Levin 	bool ret = true;
7636f5dc91SSasha Levin 	struct virtio_pci *vpci;
7736f5dc91SSasha Levin 	u32 val;
7836f5dc91SSasha Levin 
7936f5dc91SSasha Levin 	vpci = ioport->priv;
8036f5dc91SSasha Levin 	offset = port - vpci->base_addr;
8136f5dc91SSasha Levin 
8236f5dc91SSasha Levin 	switch (offset) {
8336f5dc91SSasha Levin 	case VIRTIO_PCI_HOST_FEATURES:
8436f5dc91SSasha Levin 		val = vpci->ops.get_host_features(kvm, vpci->dev);
8536f5dc91SSasha Levin 		ioport__write32(data, val);
8636f5dc91SSasha Levin 		break;
8736f5dc91SSasha Levin 	case VIRTIO_PCI_QUEUE_PFN:
8836f5dc91SSasha Levin 		val = vpci->ops.get_pfn_vq(kvm, vpci->dev, vpci->queue_selector);
8936f5dc91SSasha Levin 		ioport__write32(data, val);
9036f5dc91SSasha Levin 		break;
9136f5dc91SSasha Levin 	case VIRTIO_PCI_QUEUE_NUM:
9236f5dc91SSasha Levin 		val = vpci->ops.get_size_vq(kvm, vpci->dev, vpci->queue_selector);
9336f5dc91SSasha Levin 		ioport__write32(data, val);
9436f5dc91SSasha Levin 		break;
9536f5dc91SSasha Levin 		break;
9636f5dc91SSasha Levin 	case VIRTIO_PCI_STATUS:
9736f5dc91SSasha Levin 		ioport__write8(data, vpci->status);
9836f5dc91SSasha Levin 		break;
9936f5dc91SSasha Levin 	case VIRTIO_PCI_ISR:
10036f5dc91SSasha Levin 		ioport__write8(data, vpci->isr);
10136f5dc91SSasha Levin 		kvm__irq_line(kvm, vpci->pci_hdr.irq_line, VIRTIO_IRQ_LOW);
10236f5dc91SSasha Levin 		vpci->isr = VIRTIO_IRQ_LOW;
10336f5dc91SSasha Levin 		break;
10436f5dc91SSasha Levin 	default:
10536f5dc91SSasha Levin 		ret = virtio_pci__specific_io_in(kvm, vpci, port, data, size, offset);
10636f5dc91SSasha Levin 		break;
10736f5dc91SSasha Levin 	};
10836f5dc91SSasha Levin 
10936f5dc91SSasha Levin 	return ret;
11036f5dc91SSasha Levin }
11136f5dc91SSasha Levin 
11236f5dc91SSasha Levin static bool virtio_pci__specific_io_out(struct kvm *kvm, struct virtio_pci *vpci, u16 port,
11336f5dc91SSasha Levin 					void *data, int size, int offset)
11436f5dc91SSasha Levin {
11536f5dc91SSasha Levin 	u32 config_offset, gsi, vec;
11636f5dc91SSasha Levin 	int type = virtio__get_dev_specific_field(offset - 20, vpci->msix_enabled,
11736f5dc91SSasha Levin 							0, &config_offset);
11836f5dc91SSasha Levin 	if (type == VIRTIO_PCI_O_MSIX) {
11936f5dc91SSasha Levin 		switch (offset) {
12036f5dc91SSasha Levin 		case VIRTIO_MSI_CONFIG_VECTOR:
12136f5dc91SSasha Levin 			vec = vpci->config_vector = ioport__read16(data);
12236f5dc91SSasha Levin 
12336f5dc91SSasha Levin 			gsi = irq__add_msix_route(kvm,
12436f5dc91SSasha Levin 						  vpci->pci_hdr.msix.table[vec].low,
12536f5dc91SSasha Levin 						  vpci->pci_hdr.msix.table[vec].high,
12636f5dc91SSasha Levin 						  vpci->pci_hdr.msix.table[vec].data);
12736f5dc91SSasha Levin 
12836f5dc91SSasha Levin 			vpci->config_gsi = gsi;
12936f5dc91SSasha Levin 			break;
13036f5dc91SSasha Levin 		case VIRTIO_MSI_QUEUE_VECTOR: {
13136f5dc91SSasha Levin 			vec = vpci->vq_vector[vpci->queue_selector] = ioport__read16(data);
13236f5dc91SSasha Levin 
13336f5dc91SSasha Levin 			gsi = irq__add_msix_route(kvm,
13436f5dc91SSasha Levin 						  vpci->pci_hdr.msix.table[vec].low,
13536f5dc91SSasha Levin 						  vpci->pci_hdr.msix.table[vec].high,
13636f5dc91SSasha Levin 						  vpci->pci_hdr.msix.table[vec].data);
13736f5dc91SSasha Levin 			vpci->gsis[vpci->queue_selector] = gsi;
13836f5dc91SSasha Levin 			break;
13936f5dc91SSasha Levin 		}
14036f5dc91SSasha Levin 		};
14136f5dc91SSasha Levin 
14236f5dc91SSasha Levin 		return true;
14336f5dc91SSasha Levin 	} else if (type == VIRTIO_PCI_O_CONFIG) {
14436f5dc91SSasha Levin 		vpci->ops.set_config(kvm, vpci->dev, *(u8 *)data, config_offset);
14536f5dc91SSasha Levin 
14636f5dc91SSasha Levin 		return true;
14736f5dc91SSasha Levin 	}
14836f5dc91SSasha Levin 
14936f5dc91SSasha Levin 	return false;
15036f5dc91SSasha Levin }
15136f5dc91SSasha Levin 
15236f5dc91SSasha Levin static bool virtio_pci__io_out(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size)
15336f5dc91SSasha Levin {
15436f5dc91SSasha Levin 	unsigned long offset;
15536f5dc91SSasha Levin 	bool ret = true;
15636f5dc91SSasha Levin 	struct virtio_pci *vpci;
15736f5dc91SSasha Levin 	u32 val;
15836f5dc91SSasha Levin 
15936f5dc91SSasha Levin 	vpci = ioport->priv;
16036f5dc91SSasha Levin 	offset = port - vpci->base_addr;
16136f5dc91SSasha Levin 
16236f5dc91SSasha Levin 	switch (offset) {
16336f5dc91SSasha Levin 	case VIRTIO_PCI_GUEST_FEATURES:
16436f5dc91SSasha Levin 		val = ioport__read32(data);
16536f5dc91SSasha Levin 		vpci->ops.set_guest_features(kvm, vpci, val);
16636f5dc91SSasha Levin 		break;
16736f5dc91SSasha Levin 	case VIRTIO_PCI_QUEUE_PFN:
16836f5dc91SSasha Levin 		val = ioport__read32(data);
169*1599d724SSasha Levin 		virtio_pci__init_ioeventfd(kvm, vpci, vpci->queue_selector);
17036f5dc91SSasha Levin 		vpci->ops.init_vq(kvm, vpci->dev, vpci->queue_selector, val);
17136f5dc91SSasha Levin 		break;
17236f5dc91SSasha Levin 	case VIRTIO_PCI_QUEUE_SEL:
17336f5dc91SSasha Levin 		vpci->queue_selector	= ioport__read16(data);
17436f5dc91SSasha Levin 		break;
17536f5dc91SSasha Levin 	case VIRTIO_PCI_QUEUE_NOTIFY:
17636f5dc91SSasha Levin 		val			= ioport__read16(data);
17736f5dc91SSasha Levin 		vpci->ops.notify_vq(kvm, vpci->dev, val);
17836f5dc91SSasha Levin 		break;
17936f5dc91SSasha Levin 	case VIRTIO_PCI_STATUS:
18036f5dc91SSasha Levin 		vpci->status		= ioport__read8(data);
18136f5dc91SSasha Levin 		break;
18236f5dc91SSasha Levin 	default:
18336f5dc91SSasha Levin 		ret = virtio_pci__specific_io_out(kvm, vpci, port, data, size, offset);
18436f5dc91SSasha Levin 		break;
18536f5dc91SSasha Levin 	};
18636f5dc91SSasha Levin 
18736f5dc91SSasha Levin 	return ret;
18836f5dc91SSasha Levin }
18936f5dc91SSasha Levin 
19036f5dc91SSasha Levin static struct ioport_operations virtio_pci__io_ops = {
19136f5dc91SSasha Levin 	.io_in	= virtio_pci__io_in,
19236f5dc91SSasha Levin 	.io_out	= virtio_pci__io_out,
19336f5dc91SSasha Levin };
19436f5dc91SSasha Levin 
19536f5dc91SSasha Levin static void callback_mmio(u64 addr, u8 *data, u32 len, u8 is_write, void *ptr)
19636f5dc91SSasha Levin {
19736f5dc91SSasha Levin 	struct virtio_pci *vpci = ptr;
19836f5dc91SSasha Levin 	void *table = &vpci->pci_hdr.msix.table;
19936f5dc91SSasha Levin 
20036f5dc91SSasha Levin 	vpci->msix_enabled = 1;
20136f5dc91SSasha Levin 	if (is_write)
20236f5dc91SSasha Levin 		memcpy(table + addr - vpci->msix_io_block, data, len);
20336f5dc91SSasha Levin 	else
20436f5dc91SSasha Levin 		memcpy(data, table + addr - vpci->msix_io_block, len);
20536f5dc91SSasha Levin }
20636f5dc91SSasha Levin 
20736f5dc91SSasha Levin int virtio_pci__signal_vq(struct kvm *kvm, struct virtio_pci *vpci, u32 vq)
20836f5dc91SSasha Levin {
20936f5dc91SSasha Levin 	kvm__irq_line(kvm, vpci->gsis[vq], VIRTIO_IRQ_HIGH);
21036f5dc91SSasha Levin 
21136f5dc91SSasha Levin 	return 0;
21236f5dc91SSasha Levin }
21336f5dc91SSasha Levin 
21436f5dc91SSasha Levin int virtio_pci__signal_config(struct kvm *kvm, struct virtio_pci *vpci)
21536f5dc91SSasha Levin {
21636f5dc91SSasha Levin 	kvm__irq_line(kvm, vpci->config_gsi, VIRTIO_IRQ_HIGH);
21736f5dc91SSasha Levin 
21836f5dc91SSasha Levin 	return 0;
21936f5dc91SSasha Levin }
22036f5dc91SSasha Levin 
22136f5dc91SSasha Levin int virtio_pci__init(struct kvm *kvm, struct virtio_pci *vpci, void *dev,
22236f5dc91SSasha Levin 			int device_id, int subsys_id)
22336f5dc91SSasha Levin {
22436f5dc91SSasha Levin 	u8 pin, line, ndev;
22536f5dc91SSasha Levin 
22636f5dc91SSasha Levin 	vpci->dev = dev;
22736f5dc91SSasha Levin 	vpci->msix_io_block = pci_get_io_space_block();
22836f5dc91SSasha Levin 
22936f5dc91SSasha Levin 	vpci->base_addr = ioport__register(IOPORT_EMPTY, &virtio_pci__io_ops, IOPORT_SIZE, vpci);
23036f5dc91SSasha Levin 	kvm__register_mmio(kvm, vpci->msix_io_block, 0x100, callback_mmio, vpci);
23136f5dc91SSasha Levin 
23236f5dc91SSasha Levin 	vpci->pci_hdr = (struct pci_device_header) {
23336f5dc91SSasha Levin 		.vendor_id		= PCI_VENDOR_ID_REDHAT_QUMRANET,
23436f5dc91SSasha Levin 		.device_id		= device_id,
23536f5dc91SSasha Levin 		.header_type		= PCI_HEADER_TYPE_NORMAL,
23636f5dc91SSasha Levin 		.revision_id		= 0,
23736f5dc91SSasha Levin 		.class			= 0x010000,
23836f5dc91SSasha Levin 		.subsys_vendor_id	= PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET,
23936f5dc91SSasha Levin 		.subsys_id		= subsys_id,
24036f5dc91SSasha Levin 		.bar[0]			= vpci->base_addr | PCI_BASE_ADDRESS_SPACE_IO,
24136f5dc91SSasha Levin 		.bar[1]			= vpci->msix_io_block |
24236f5dc91SSasha Levin 					PCI_BASE_ADDRESS_SPACE_MEMORY |
24336f5dc91SSasha Levin 					PCI_BASE_ADDRESS_MEM_TYPE_64,
24436f5dc91SSasha Levin 		/* bar[2] is the continuation of bar[1] for 64bit addressing */
24536f5dc91SSasha Levin 		.bar[2]			= 0,
24636f5dc91SSasha Levin 		.status			= PCI_STATUS_CAP_LIST,
24736f5dc91SSasha Levin 		.capabilities		= (void *)&vpci->pci_hdr.msix - (void *)&vpci->pci_hdr,
24836f5dc91SSasha Levin 	};
24936f5dc91SSasha Levin 
25036f5dc91SSasha Levin 	vpci->pci_hdr.msix.cap = PCI_CAP_ID_MSIX;
25136f5dc91SSasha Levin 	vpci->pci_hdr.msix.next = 0;
25236f5dc91SSasha Levin 	vpci->pci_hdr.msix.table_size = (VIRTIO_PCI_MAX_VQ + 1) | PCI_MSIX_FLAGS_ENABLE;
25336f5dc91SSasha Levin 	vpci->pci_hdr.msix.table_offset = 1; /* Use BAR 1 */
25436f5dc91SSasha Levin 	vpci->config_vector = 0;
25536f5dc91SSasha Levin 
25636f5dc91SSasha Levin 	if (irq__register_device(VIRTIO_ID_RNG, &ndev, &pin, &line) < 0)
25736f5dc91SSasha Levin 		return -1;
25836f5dc91SSasha Levin 
25936f5dc91SSasha Levin 	vpci->pci_hdr.irq_pin	= pin;
26036f5dc91SSasha Levin 	vpci->pci_hdr.irq_line	= line;
26136f5dc91SSasha Levin 	pci__register(&vpci->pci_hdr, ndev);
26236f5dc91SSasha Levin 
26336f5dc91SSasha Levin 	return 0;
26436f5dc91SSasha Levin }
265