xref: /kvmtool/virtio/pci.c (revision 5a8e4f25dd7b32228ff214b5d5a68a27d96c9a6c)
136f5dc91SSasha Levin #include "kvm/virtio-pci.h"
236f5dc91SSasha Levin 
336f5dc91SSasha Levin #include "kvm/ioport.h"
436f5dc91SSasha Levin #include "kvm/kvm.h"
54123ca55SMarc Zyngier #include "kvm/kvm-cpu.h"
636f5dc91SSasha Levin #include "kvm/virtio-pci-dev.h"
736f5dc91SSasha Levin #include "kvm/irq.h"
836f5dc91SSasha Levin #include "kvm/virtio.h"
91599d724SSasha Levin #include "kvm/ioeventfd.h"
1036f5dc91SSasha Levin 
1143c81c74SSasha Levin #include <sys/ioctl.h>
1236f5dc91SSasha Levin #include <linux/virtio_pci.h>
13aa73be70SMatt Evans #include <linux/byteorder.h>
14*5a8e4f25SAlexandru Elisei #include <assert.h>
1536f5dc91SSasha Levin #include <string.h>
1636f5dc91SSasha Levin 
17e539f3e4SAlexandru Elisei static u16 virtio_pci__port_addr(struct virtio_pci *vpci)
18e539f3e4SAlexandru Elisei {
19e539f3e4SAlexandru Elisei 	return pci__bar_address(&vpci->pci_hdr, 0);
20e539f3e4SAlexandru Elisei }
21e539f3e4SAlexandru Elisei 
22e539f3e4SAlexandru Elisei static u32 virtio_pci__mmio_addr(struct virtio_pci *vpci)
23e539f3e4SAlexandru Elisei {
24e539f3e4SAlexandru Elisei 	return pci__bar_address(&vpci->pci_hdr, 1);
25e539f3e4SAlexandru Elisei }
26e539f3e4SAlexandru Elisei 
27e539f3e4SAlexandru Elisei static u32 virtio_pci__msix_io_addr(struct virtio_pci *vpci)
28e539f3e4SAlexandru Elisei {
29e539f3e4SAlexandru Elisei 	return pci__bar_address(&vpci->pci_hdr, 2);
30e539f3e4SAlexandru Elisei }
31e539f3e4SAlexandru Elisei 
321599d724SSasha Levin static void virtio_pci__ioevent_callback(struct kvm *kvm, void *param)
331599d724SSasha Levin {
341599d724SSasha Levin 	struct virtio_pci_ioevent_param *ioeventfd = param;
3502eca50cSAsias He 	struct virtio_pci *vpci = ioeventfd->vdev->virtio;
361599d724SSasha Levin 
3702eca50cSAsias He 	ioeventfd->vdev->ops->notify_vq(kvm, vpci->dev, ioeventfd->vq);
381599d724SSasha Levin }
391599d724SSasha Levin 
4002eca50cSAsias He static int virtio_pci__init_ioeventfd(struct kvm *kvm, struct virtio_device *vdev, u32 vq)
411599d724SSasha Levin {
421599d724SSasha Levin 	struct ioevent ioevent;
4302eca50cSAsias He 	struct virtio_pci *vpci = vdev->virtio;
44e539f3e4SAlexandru Elisei 	u32 mmio_addr = virtio_pci__mmio_addr(vpci);
45e539f3e4SAlexandru Elisei 	u16 port_addr = virtio_pci__port_addr(vpci);
460e1882a4SJean-Philippe Brucker 	int r, flags = 0;
470e1882a4SJean-Philippe Brucker 	int fd;
481599d724SSasha Levin 
491599d724SSasha Levin 	vpci->ioeventfds[vq] = (struct virtio_pci_ioevent_param) {
5002eca50cSAsias He 		.vdev		= vdev,
511599d724SSasha Levin 		.vq		= vq,
521599d724SSasha Levin 	};
531599d724SSasha Levin 
541599d724SSasha Levin 	ioevent = (struct ioevent) {
551599d724SSasha Levin 		.fn		= virtio_pci__ioevent_callback,
561599d724SSasha Levin 		.fn_ptr		= &vpci->ioeventfds[vq],
571599d724SSasha Levin 		.datamatch	= vq,
581599d724SSasha Levin 		.fn_kvm		= kvm,
591599d724SSasha Levin 	};
601599d724SSasha Levin 
61627d6874SAsias He 	/*
62a463650cSWill Deacon 	 * Vhost will poll the eventfd in host kernel side, otherwise we
63a463650cSWill Deacon 	 * need to poll in userspace.
64627d6874SAsias He 	 */
65a463650cSWill Deacon 	if (!vdev->use_vhost)
66a463650cSWill Deacon 		flags |= IOEVENTFD_FLAG_USER_POLL;
67a463650cSWill Deacon 
68a463650cSWill Deacon 	/* ioport */
69e539f3e4SAlexandru Elisei 	ioevent.io_addr	= port_addr + VIRTIO_PCI_QUEUE_NOTIFY;
70a463650cSWill Deacon 	ioevent.io_len	= sizeof(u16);
710e1882a4SJean-Philippe Brucker 	ioevent.fd	= fd = eventfd(0, 0);
7271ca0facSAndre Przywara 	r = ioeventfd__add_event(&ioevent, flags | IOEVENTFD_FLAG_PIO);
73ea6eeb1cSSasha Levin 	if (r)
74ea6eeb1cSSasha Levin 		return r;
751599d724SSasha Levin 
76a463650cSWill Deacon 	/* mmio */
77e539f3e4SAlexandru Elisei 	ioevent.io_addr	= mmio_addr + VIRTIO_PCI_QUEUE_NOTIFY;
78fe50bacbSAndreas Herrmann 	ioevent.io_len	= sizeof(u16);
790e1882a4SJean-Philippe Brucker 	ioevent.fd	= eventfd(0, 0);
80a463650cSWill Deacon 	r = ioeventfd__add_event(&ioevent, flags);
81a463650cSWill Deacon 	if (r)
82a463650cSWill Deacon 		goto free_ioport_evt;
83263b80e8SSasha Levin 
84a463650cSWill Deacon 	if (vdev->ops->notify_vq_eventfd)
850e1882a4SJean-Philippe Brucker 		vdev->ops->notify_vq_eventfd(kvm, vpci->dev, vq, fd);
861599d724SSasha Levin 	return 0;
87a463650cSWill Deacon 
88a463650cSWill Deacon free_ioport_evt:
89e539f3e4SAlexandru Elisei 	ioeventfd__del_event(port_addr + VIRTIO_PCI_QUEUE_NOTIFY, vq);
90a463650cSWill Deacon 	return r;
911599d724SSasha Levin }
921599d724SSasha Levin 
93ad346c2eSJean-Philippe Brucker static void virtio_pci_exit_vq(struct kvm *kvm, struct virtio_device *vdev,
94ad346c2eSJean-Philippe Brucker 			       int vq)
95ad346c2eSJean-Philippe Brucker {
96ad346c2eSJean-Philippe Brucker 	struct virtio_pci *vpci = vdev->virtio;
97e539f3e4SAlexandru Elisei 	u32 mmio_addr = virtio_pci__mmio_addr(vpci);
98e539f3e4SAlexandru Elisei 	u16 port_addr = virtio_pci__port_addr(vpci);
99ad346c2eSJean-Philippe Brucker 
100e539f3e4SAlexandru Elisei 	ioeventfd__del_event(mmio_addr + VIRTIO_PCI_QUEUE_NOTIFY, vq);
101e539f3e4SAlexandru Elisei 	ioeventfd__del_event(port_addr + VIRTIO_PCI_QUEUE_NOTIFY, vq);
102ad346c2eSJean-Philippe Brucker 	virtio_exit_vq(kvm, vdev, vpci->dev, vq);
103ad346c2eSJean-Philippe Brucker }
104ad346c2eSJean-Philippe Brucker 
10506f48103SSasha Levin static inline bool virtio_pci__msix_enabled(struct virtio_pci *vpci)
10606f48103SSasha Levin {
107aa73be70SMatt Evans 	return vpci->pci_hdr.msix.ctrl & cpu_to_le16(PCI_MSIX_FLAGS_ENABLE);
10806f48103SSasha Levin }
10906f48103SSasha Levin 
110e09b599aSJulien Thierry static bool virtio_pci__specific_data_in(struct kvm *kvm, struct virtio_device *vdev,
111e09b599aSJulien Thierry 					 void *data, int size, unsigned long offset)
11236f5dc91SSasha Levin {
11336f5dc91SSasha Levin 	u32 config_offset;
11402eca50cSAsias He 	struct virtio_pci *vpci = vdev->virtio;
11506f48103SSasha Levin 	int type = virtio__get_dev_specific_field(offset - 20,
11606f48103SSasha Levin 							virtio_pci__msix_enabled(vpci),
1171382aba0SSasha Levin 							&config_offset);
11836f5dc91SSasha Levin 	if (type == VIRTIO_PCI_O_MSIX) {
11936f5dc91SSasha Levin 		switch (offset) {
12036f5dc91SSasha Levin 		case VIRTIO_MSI_CONFIG_VECTOR:
12136f5dc91SSasha Levin 			ioport__write16(data, vpci->config_vector);
12236f5dc91SSasha Levin 			break;
12336f5dc91SSasha Levin 		case VIRTIO_MSI_QUEUE_VECTOR:
12436f5dc91SSasha Levin 			ioport__write16(data, vpci->vq_vector[vpci->queue_selector]);
12536f5dc91SSasha Levin 			break;
12636f5dc91SSasha Levin 		};
12736f5dc91SSasha Levin 
12836f5dc91SSasha Levin 		return true;
12936f5dc91SSasha Levin 	} else if (type == VIRTIO_PCI_O_CONFIG) {
13036f5dc91SSasha Levin 		u8 cfg;
13136f5dc91SSasha Levin 
132c5ae742bSSasha Levin 		cfg = vdev->ops->get_config(kvm, vpci->dev)[config_offset];
13336f5dc91SSasha Levin 		ioport__write8(data, cfg);
13436f5dc91SSasha Levin 		return true;
13536f5dc91SSasha Levin 	}
13636f5dc91SSasha Levin 
13736f5dc91SSasha Levin 	return false;
13836f5dc91SSasha Levin }
13936f5dc91SSasha Levin 
140e09b599aSJulien Thierry static bool virtio_pci__data_in(struct kvm_cpu *vcpu, struct virtio_device *vdev,
141e09b599aSJulien Thierry 				unsigned long offset, void *data, int size)
14236f5dc91SSasha Levin {
14336f5dc91SSasha Levin 	bool ret = true;
14436f5dc91SSasha Levin 	struct virtio_pci *vpci;
14553fbb17bSJean-Philippe Brucker 	struct virt_queue *vq;
1464123ca55SMarc Zyngier 	struct kvm *kvm;
14736f5dc91SSasha Levin 	u32 val;
14836f5dc91SSasha Levin 
1494123ca55SMarc Zyngier 	kvm = vcpu->kvm;
15002eca50cSAsias He 	vpci = vdev->virtio;
15136f5dc91SSasha Levin 
15236f5dc91SSasha Levin 	switch (offset) {
15336f5dc91SSasha Levin 	case VIRTIO_PCI_HOST_FEATURES:
15402eca50cSAsias He 		val = vdev->ops->get_host_features(kvm, vpci->dev);
15536f5dc91SSasha Levin 		ioport__write32(data, val);
15636f5dc91SSasha Levin 		break;
15736f5dc91SSasha Levin 	case VIRTIO_PCI_QUEUE_PFN:
15853fbb17bSJean-Philippe Brucker 		vq = vdev->ops->get_vq(kvm, vpci->dev, vpci->queue_selector);
15953fbb17bSJean-Philippe Brucker 		ioport__write32(data, vq->pfn);
16036f5dc91SSasha Levin 		break;
16136f5dc91SSasha Levin 	case VIRTIO_PCI_QUEUE_NUM:
16202eca50cSAsias He 		val = vdev->ops->get_size_vq(kvm, vpci->dev, vpci->queue_selector);
163657ee18bSMatt Evans 		ioport__write16(data, val);
16436f5dc91SSasha Levin 		break;
16536f5dc91SSasha Levin 	case VIRTIO_PCI_STATUS:
16636f5dc91SSasha Levin 		ioport__write8(data, vpci->status);
16736f5dc91SSasha Levin 		break;
16836f5dc91SSasha Levin 	case VIRTIO_PCI_ISR:
16936f5dc91SSasha Levin 		ioport__write8(data, vpci->isr);
170e9922aafSAndre Przywara 		kvm__irq_line(kvm, vpci->legacy_irq_line, VIRTIO_IRQ_LOW);
17136f5dc91SSasha Levin 		vpci->isr = VIRTIO_IRQ_LOW;
17236f5dc91SSasha Levin 		break;
17336f5dc91SSasha Levin 	default:
174e09b599aSJulien Thierry 		ret = virtio_pci__specific_data_in(kvm, vdev, data, size, offset);
17536f5dc91SSasha Levin 		break;
17636f5dc91SSasha Levin 	};
17736f5dc91SSasha Levin 
17836f5dc91SSasha Levin 	return ret;
17936f5dc91SSasha Levin }
18036f5dc91SSasha Levin 
181e09b599aSJulien Thierry static bool virtio_pci__io_in(struct ioport *ioport, struct kvm_cpu *vcpu, u16 port, void *data, int size)
182e09b599aSJulien Thierry {
183e09b599aSJulien Thierry 	struct virtio_device *vdev = ioport->priv;
184e09b599aSJulien Thierry 	struct virtio_pci *vpci = vdev->virtio;
185e539f3e4SAlexandru Elisei 	unsigned long offset = port - virtio_pci__port_addr(vpci);
186e09b599aSJulien Thierry 
187e09b599aSJulien Thierry 	return virtio_pci__data_in(vcpu, vdev, offset, data, size);
188e09b599aSJulien Thierry }
189e09b599aSJulien Thierry 
1906518065aSAndre Przywara static void update_msix_map(struct virtio_pci *vpci,
1916518065aSAndre Przywara 			    struct msix_table *msix_entry, u32 vecnum)
1926518065aSAndre Przywara {
1936518065aSAndre Przywara 	u32 gsi, i;
1946518065aSAndre Przywara 
1956518065aSAndre Przywara 	/* Find the GSI number used for that vector */
1966518065aSAndre Przywara 	if (vecnum == vpci->config_vector) {
1976518065aSAndre Przywara 		gsi = vpci->config_gsi;
1986518065aSAndre Przywara 	} else {
1996518065aSAndre Przywara 		for (i = 0; i < VIRTIO_PCI_MAX_VQ; i++)
2006518065aSAndre Przywara 			if (vpci->vq_vector[i] == vecnum)
2016518065aSAndre Przywara 				break;
2026518065aSAndre Przywara 		if (i == VIRTIO_PCI_MAX_VQ)
2036518065aSAndre Przywara 			return;
2046518065aSAndre Przywara 		gsi = vpci->gsis[i];
2056518065aSAndre Przywara 	}
2066518065aSAndre Przywara 
2076518065aSAndre Przywara 	if (gsi == 0)
2086518065aSAndre Przywara 		return;
2096518065aSAndre Przywara 
2106518065aSAndre Przywara 	msix_entry = &msix_entry[vecnum];
2116518065aSAndre Przywara 	irq__update_msix_route(vpci->kvm, gsi, &msix_entry->msg);
2126518065aSAndre Przywara }
2136518065aSAndre Przywara 
214e09b599aSJulien Thierry static bool virtio_pci__specific_data_out(struct kvm *kvm, struct virtio_device *vdev,
215e09b599aSJulien Thierry 					  void *data, int size, unsigned long offset)
21636f5dc91SSasha Levin {
21702eca50cSAsias He 	struct virtio_pci *vpci = vdev->virtio;
2188ccc8549SAndre Przywara 	u32 config_offset, vec;
2198ccc8549SAndre Przywara 	int gsi;
22006f48103SSasha Levin 	int type = virtio__get_dev_specific_field(offset - 20, virtio_pci__msix_enabled(vpci),
2211382aba0SSasha Levin 							&config_offset);
22236f5dc91SSasha Levin 	if (type == VIRTIO_PCI_O_MSIX) {
22336f5dc91SSasha Levin 		switch (offset) {
22436f5dc91SSasha Levin 		case VIRTIO_MSI_CONFIG_VECTOR:
22536f5dc91SSasha Levin 			vec = vpci->config_vector = ioport__read16(data);
226f8327b05SSasha Levin 			if (vec == VIRTIO_MSI_NO_VECTOR)
227f8327b05SSasha Levin 				break;
22836f5dc91SSasha Levin 
2298ccc8549SAndre Przywara 			gsi = irq__add_msix_route(kvm,
230f9ef46f2SAndre Przywara 						  &vpci->msix_table[vec].msg,
231f9ef46f2SAndre Przywara 						  vpci->dev_hdr.dev_num << 3);
232928ab7acSAndre Przywara 			/*
233928ab7acSAndre Przywara 			 * We don't need IRQ routing if we can use
234928ab7acSAndre Przywara 			 * MSI injection via the KVM_SIGNAL_MSI ioctl.
235928ab7acSAndre Przywara 			 */
236928ab7acSAndre Przywara 			if (gsi == -ENXIO &&
237928ab7acSAndre Przywara 			    vpci->features & VIRTIO_PCI_F_SIGNAL_MSI)
238928ab7acSAndre Przywara 				break;
239928ab7acSAndre Przywara 
240928ab7acSAndre Przywara 			if (gsi < 0) {
241928ab7acSAndre Przywara 				die("failed to configure MSIs");
242928ab7acSAndre Przywara 				break;
243928ab7acSAndre Przywara 			}
244928ab7acSAndre Przywara 
24536f5dc91SSasha Levin 			vpci->config_gsi = gsi;
24636f5dc91SSasha Levin 			break;
2473a60be06SSasha Levin 		case VIRTIO_MSI_QUEUE_VECTOR:
2488ccc8549SAndre Przywara 			vec = ioport__read16(data);
2498ccc8549SAndre Przywara 			vpci->vq_vector[vpci->queue_selector] = vec;
25036f5dc91SSasha Levin 
251f8327b05SSasha Levin 			if (vec == VIRTIO_MSI_NO_VECTOR)
252f8327b05SSasha Levin 				break;
253f8327b05SSasha Levin 
2548ccc8549SAndre Przywara 			gsi = irq__add_msix_route(kvm,
255f9ef46f2SAndre Przywara 						  &vpci->msix_table[vec].msg,
256f9ef46f2SAndre Przywara 						  vpci->dev_hdr.dev_num << 3);
257928ab7acSAndre Przywara 			/*
258928ab7acSAndre Przywara 			 * We don't need IRQ routing if we can use
259928ab7acSAndre Przywara 			 * MSI injection via the KVM_SIGNAL_MSI ioctl.
260928ab7acSAndre Przywara 			 */
261928ab7acSAndre Przywara 			if (gsi == -ENXIO &&
262928ab7acSAndre Przywara 			    vpci->features & VIRTIO_PCI_F_SIGNAL_MSI)
2638ccc8549SAndre Przywara 				break;
264928ab7acSAndre Przywara 
265928ab7acSAndre Przywara 			if (gsi < 0) {
266928ab7acSAndre Przywara 				die("failed to configure MSIs");
267928ab7acSAndre Przywara 				break;
268928ab7acSAndre Przywara 			}
269928ab7acSAndre Przywara 
27036f5dc91SSasha Levin 			vpci->gsis[vpci->queue_selector] = gsi;
27102eca50cSAsias He 			if (vdev->ops->notify_vq_gsi)
27202eca50cSAsias He 				vdev->ops->notify_vq_gsi(kvm, vpci->dev,
2738ccc8549SAndre Przywara 							 vpci->queue_selector,
2748ccc8549SAndre Przywara 							 gsi);
27536f5dc91SSasha Levin 			break;
27636f5dc91SSasha Levin 		};
27736f5dc91SSasha Levin 
27836f5dc91SSasha Levin 		return true;
27936f5dc91SSasha Levin 	} else if (type == VIRTIO_PCI_O_CONFIG) {
280c5ae742bSSasha Levin 		vdev->ops->get_config(kvm, vpci->dev)[config_offset] = *(u8 *)data;
28136f5dc91SSasha Levin 
28236f5dc91SSasha Levin 		return true;
28336f5dc91SSasha Levin 	}
28436f5dc91SSasha Levin 
28536f5dc91SSasha Levin 	return false;
28636f5dc91SSasha Levin }
28736f5dc91SSasha Levin 
288e09b599aSJulien Thierry static bool virtio_pci__data_out(struct kvm_cpu *vcpu, struct virtio_device *vdev,
289e09b599aSJulien Thierry 				 unsigned long offset, void *data, int size)
29036f5dc91SSasha Levin {
29136f5dc91SSasha Levin 	bool ret = true;
29236f5dc91SSasha Levin 	struct virtio_pci *vpci;
2934123ca55SMarc Zyngier 	struct kvm *kvm;
29436f5dc91SSasha Levin 	u32 val;
29536f5dc91SSasha Levin 
2964123ca55SMarc Zyngier 	kvm = vcpu->kvm;
29702eca50cSAsias He 	vpci = vdev->virtio;
29836f5dc91SSasha Levin 
29936f5dc91SSasha Levin 	switch (offset) {
30036f5dc91SSasha Levin 	case VIRTIO_PCI_GUEST_FEATURES:
30136f5dc91SSasha Levin 		val = ioport__read32(data);
30256a16c90SJean-Philippe Brucker 		virtio_set_guest_features(kvm, vdev, vpci->dev, val);
30336f5dc91SSasha Levin 		break;
30436f5dc91SSasha Levin 	case VIRTIO_PCI_QUEUE_PFN:
30536f5dc91SSasha Levin 		val = ioport__read32(data);
306ad346c2eSJean-Philippe Brucker 		if (val) {
307ad346c2eSJean-Philippe Brucker 			virtio_pci__init_ioeventfd(kvm, vdev,
308ad346c2eSJean-Philippe Brucker 						   vpci->queue_selector);
309c59ba304SWill Deacon 			vdev->ops->init_vq(kvm, vpci->dev, vpci->queue_selector,
310c59ba304SWill Deacon 					   1 << VIRTIO_PCI_QUEUE_ADDR_SHIFT,
311c59ba304SWill Deacon 					   VIRTIO_PCI_VRING_ALIGN, val);
312ad346c2eSJean-Philippe Brucker 		} else {
313ad346c2eSJean-Philippe Brucker 			virtio_pci_exit_vq(kvm, vdev, vpci->queue_selector);
314ad346c2eSJean-Philippe Brucker 		}
31536f5dc91SSasha Levin 		break;
31636f5dc91SSasha Levin 	case VIRTIO_PCI_QUEUE_SEL:
31736f5dc91SSasha Levin 		vpci->queue_selector = ioport__read16(data);
31836f5dc91SSasha Levin 		break;
31936f5dc91SSasha Levin 	case VIRTIO_PCI_QUEUE_NOTIFY:
32036f5dc91SSasha Levin 		val = ioport__read16(data);
32102eca50cSAsias He 		vdev->ops->notify_vq(kvm, vpci->dev, val);
32236f5dc91SSasha Levin 		break;
32336f5dc91SSasha Levin 	case VIRTIO_PCI_STATUS:
32436f5dc91SSasha Levin 		vpci->status = ioport__read8(data);
3254123ca55SMarc Zyngier 		if (!vpci->status) /* Sample endianness on reset */
3264123ca55SMarc Zyngier 			vdev->endian = kvm_cpu__get_endianness(vcpu);
32795242e44SJean-Philippe Brucker 		virtio_notify_status(kvm, vdev, vpci->dev, vpci->status);
32836f5dc91SSasha Levin 		break;
32936f5dc91SSasha Levin 	default:
330e09b599aSJulien Thierry 		ret = virtio_pci__specific_data_out(kvm, vdev, data, size, offset);
33136f5dc91SSasha Levin 		break;
33236f5dc91SSasha Levin 	};
33336f5dc91SSasha Levin 
33436f5dc91SSasha Levin 	return ret;
33536f5dc91SSasha Levin }
33636f5dc91SSasha Levin 
337e09b599aSJulien Thierry static bool virtio_pci__io_out(struct ioport *ioport, struct kvm_cpu *vcpu, u16 port, void *data, int size)
338e09b599aSJulien Thierry {
339e09b599aSJulien Thierry 	struct virtio_device *vdev = ioport->priv;
340e09b599aSJulien Thierry 	struct virtio_pci *vpci = vdev->virtio;
341e539f3e4SAlexandru Elisei 	unsigned long offset = port - virtio_pci__port_addr(vpci);
342e09b599aSJulien Thierry 
343e09b599aSJulien Thierry 	return virtio_pci__data_out(vcpu, vdev, offset, data, size);
344e09b599aSJulien Thierry }
345e09b599aSJulien Thierry 
34636f5dc91SSasha Levin static struct ioport_operations virtio_pci__io_ops = {
34736f5dc91SSasha Levin 	.io_in	= virtio_pci__io_in,
34836f5dc91SSasha Levin 	.io_out	= virtio_pci__io_out,
34936f5dc91SSasha Levin };
35036f5dc91SSasha Levin 
3519b735910SMarc Zyngier static void virtio_pci__msix_mmio_callback(struct kvm_cpu *vcpu,
3529b735910SMarc Zyngier 					   u64 addr, u8 *data, u32 len,
353a463650cSWill Deacon 					   u8 is_write, void *ptr)
35436f5dc91SSasha Levin {
355e09b599aSJulien Thierry 	struct virtio_device *vdev = ptr;
356e09b599aSJulien Thierry 	struct virtio_pci *vpci = vdev->virtio;
3576518065aSAndre Przywara 	struct msix_table *table;
358e539f3e4SAlexandru Elisei 	u32 msix_io_addr = virtio_pci__msix_io_addr(vpci);
3596518065aSAndre Przywara 	int vecnum;
3606518065aSAndre Przywara 	size_t offset;
36136f5dc91SSasha Levin 
362e539f3e4SAlexandru Elisei 	if (addr > msix_io_addr + PCI_IO_SIZE) {
3636518065aSAndre Przywara 		if (is_write)
3646518065aSAndre Przywara 			return;
3656518065aSAndre Przywara 		table  = (struct msix_table *)&vpci->msix_pba;
366e539f3e4SAlexandru Elisei 		offset = addr - (msix_io_addr + PCI_IO_SIZE);
3679c26dab4SSasha Levin 	} else {
3686518065aSAndre Przywara 		table  = vpci->msix_table;
369e539f3e4SAlexandru Elisei 		offset = addr - msix_io_addr;
3706518065aSAndre Przywara 	}
3716518065aSAndre Przywara 	vecnum = offset / sizeof(struct msix_table);
3726518065aSAndre Przywara 	offset = offset % sizeof(struct msix_table);
3736518065aSAndre Przywara 
3746518065aSAndre Przywara 	if (!is_write) {
3756518065aSAndre Przywara 		memcpy(data, (void *)&table[vecnum] + offset, len);
3766518065aSAndre Przywara 		return;
37736f5dc91SSasha Levin 	}
37836f5dc91SSasha Levin 
3796518065aSAndre Przywara 	memcpy((void *)&table[vecnum] + offset, data, len);
3806518065aSAndre Przywara 
3816518065aSAndre Przywara 	/* Did we just update the address or payload? */
3826518065aSAndre Przywara 	if (offset < offsetof(struct msix_table, ctrl))
3836518065aSAndre Przywara 		update_msix_map(vpci, table, vecnum);
38406f48103SSasha Levin }
38506f48103SSasha Levin 
386714ab9e6SAndre Przywara static void virtio_pci__signal_msi(struct kvm *kvm, struct virtio_pci *vpci,
387714ab9e6SAndre Przywara 				   int vec)
38843c81c74SSasha Levin {
38943c81c74SSasha Levin 	struct kvm_msi msi = {
39043c81c74SSasha Levin 		.address_lo = vpci->msix_table[vec].msg.address_lo,
39143c81c74SSasha Levin 		.address_hi = vpci->msix_table[vec].msg.address_hi,
39243c81c74SSasha Levin 		.data = vpci->msix_table[vec].msg.data,
39343c81c74SSasha Levin 	};
39443c81c74SSasha Levin 
395714ab9e6SAndre Przywara 	if (kvm->msix_needs_devid) {
396714ab9e6SAndre Przywara 		msi.flags = KVM_MSI_VALID_DEVID;
397714ab9e6SAndre Przywara 		msi.devid = vpci->dev_hdr.dev_num << 3;
398714ab9e6SAndre Przywara 	}
399714ab9e6SAndre Przywara 
400f6108d72SJean-Philippe Brucker 	irq__signal_msi(kvm, &msi);
40143c81c74SSasha Levin }
40243c81c74SSasha Levin 
40302eca50cSAsias He int virtio_pci__signal_vq(struct kvm *kvm, struct virtio_device *vdev, u32 vq)
40436f5dc91SSasha Levin {
40502eca50cSAsias He 	struct virtio_pci *vpci = vdev->virtio;
40606f48103SSasha Levin 	int tbl = vpci->vq_vector[vq];
40736f5dc91SSasha Levin 
408f8327b05SSasha Levin 	if (virtio_pci__msix_enabled(vpci) && tbl != VIRTIO_MSI_NO_VECTOR) {
409aa73be70SMatt Evans 		if (vpci->pci_hdr.msix.ctrl & cpu_to_le16(PCI_MSIX_FLAGS_MASKALL) ||
410aa73be70SMatt Evans 		    vpci->msix_table[tbl].ctrl & cpu_to_le16(PCI_MSIX_ENTRY_CTRL_MASKBIT)) {
41106f48103SSasha Levin 
41206f48103SSasha Levin 			vpci->msix_pba |= 1 << tbl;
41306f48103SSasha Levin 			return 0;
41406f48103SSasha Levin 		}
41506f48103SSasha Levin 
41643c81c74SSasha Levin 		if (vpci->features & VIRTIO_PCI_F_SIGNAL_MSI)
41743c81c74SSasha Levin 			virtio_pci__signal_msi(kvm, vpci, vpci->vq_vector[vq]);
41843c81c74SSasha Levin 		else
41906f48103SSasha Levin 			kvm__irq_trigger(kvm, vpci->gsis[vq]);
42006f48103SSasha Levin 	} else {
421a36eca7bSSasha Levin 		vpci->isr = VIRTIO_IRQ_HIGH;
422e9922aafSAndre Przywara 		kvm__irq_trigger(kvm, vpci->legacy_irq_line);
42306f48103SSasha Levin 	}
42436f5dc91SSasha Levin 	return 0;
42536f5dc91SSasha Levin }
42636f5dc91SSasha Levin 
42702eca50cSAsias He int virtio_pci__signal_config(struct kvm *kvm, struct virtio_device *vdev)
42836f5dc91SSasha Levin {
42902eca50cSAsias He 	struct virtio_pci *vpci = vdev->virtio;
43006f48103SSasha Levin 	int tbl = vpci->config_vector;
43106f48103SSasha Levin 
432f8327b05SSasha Levin 	if (virtio_pci__msix_enabled(vpci) && tbl != VIRTIO_MSI_NO_VECTOR) {
433aa73be70SMatt Evans 		if (vpci->pci_hdr.msix.ctrl & cpu_to_le16(PCI_MSIX_FLAGS_MASKALL) ||
434aa73be70SMatt Evans 		    vpci->msix_table[tbl].ctrl & cpu_to_le16(PCI_MSIX_ENTRY_CTRL_MASKBIT)) {
43506f48103SSasha Levin 
43606f48103SSasha Levin 			vpci->msix_pba |= 1 << tbl;
43706f48103SSasha Levin 			return 0;
43806f48103SSasha Levin 		}
43906f48103SSasha Levin 
44043c81c74SSasha Levin 		if (vpci->features & VIRTIO_PCI_F_SIGNAL_MSI)
441f8327b05SSasha Levin 			virtio_pci__signal_msi(kvm, vpci, tbl);
44243c81c74SSasha Levin 		else
44306f48103SSasha Levin 			kvm__irq_trigger(kvm, vpci->config_gsi);
44406f48103SSasha Levin 	} else {
44506f48103SSasha Levin 		vpci->isr = VIRTIO_PCI_ISR_CONFIG;
446e9922aafSAndre Przywara 		kvm__irq_trigger(kvm, vpci->legacy_irq_line);
44706f48103SSasha Levin 	}
44836f5dc91SSasha Levin 
44936f5dc91SSasha Levin 	return 0;
45036f5dc91SSasha Levin }
45136f5dc91SSasha Levin 
4529b735910SMarc Zyngier static void virtio_pci__io_mmio_callback(struct kvm_cpu *vcpu,
4539b735910SMarc Zyngier 					 u64 addr, u8 *data, u32 len,
454a463650cSWill Deacon 					 u8 is_write, void *ptr)
455a463650cSWill Deacon {
456e09b599aSJulien Thierry 	struct virtio_device *vdev = ptr;
457e09b599aSJulien Thierry 	struct virtio_pci *vpci = vdev->virtio;
458e539f3e4SAlexandru Elisei 	u32 mmio_addr = virtio_pci__mmio_addr(vpci);
459a463650cSWill Deacon 
460e09b599aSJulien Thierry 	if (!is_write)
461e539f3e4SAlexandru Elisei 		virtio_pci__data_in(vcpu, vdev, addr - mmio_addr, data, len);
462e09b599aSJulien Thierry 	else
463e539f3e4SAlexandru Elisei 		virtio_pci__data_out(vcpu, vdev, addr - mmio_addr, data, len);
464a463650cSWill Deacon }
465a463650cSWill Deacon 
466*5a8e4f25SAlexandru Elisei static int virtio_pci__bar_activate(struct kvm *kvm,
467*5a8e4f25SAlexandru Elisei 				    struct pci_device_header *pci_hdr,
468*5a8e4f25SAlexandru Elisei 				    int bar_num, void *data)
469*5a8e4f25SAlexandru Elisei {
470*5a8e4f25SAlexandru Elisei 	struct virtio_device *vdev = data;
471*5a8e4f25SAlexandru Elisei 	u32 bar_addr, bar_size;
472*5a8e4f25SAlexandru Elisei 	int r = -EINVAL;
473*5a8e4f25SAlexandru Elisei 
474*5a8e4f25SAlexandru Elisei 	assert(bar_num <= 2);
475*5a8e4f25SAlexandru Elisei 
476*5a8e4f25SAlexandru Elisei 	bar_addr = pci__bar_address(pci_hdr, bar_num);
477*5a8e4f25SAlexandru Elisei 	bar_size = pci__bar_size(pci_hdr, bar_num);
478*5a8e4f25SAlexandru Elisei 
479*5a8e4f25SAlexandru Elisei 	switch (bar_num) {
480*5a8e4f25SAlexandru Elisei 	case 0:
481*5a8e4f25SAlexandru Elisei 		r = ioport__register(kvm, bar_addr, &virtio_pci__io_ops,
482*5a8e4f25SAlexandru Elisei 				     bar_size, vdev);
483*5a8e4f25SAlexandru Elisei 		if (r > 0)
484*5a8e4f25SAlexandru Elisei 			r = 0;
485*5a8e4f25SAlexandru Elisei 		break;
486*5a8e4f25SAlexandru Elisei 	case 1:
487*5a8e4f25SAlexandru Elisei 		r =  kvm__register_mmio(kvm, bar_addr, bar_size, false,
488*5a8e4f25SAlexandru Elisei 					virtio_pci__io_mmio_callback, vdev);
489*5a8e4f25SAlexandru Elisei 		break;
490*5a8e4f25SAlexandru Elisei 	case 2:
491*5a8e4f25SAlexandru Elisei 		r =  kvm__register_mmio(kvm, bar_addr, bar_size, false,
492*5a8e4f25SAlexandru Elisei 					virtio_pci__msix_mmio_callback, vdev);
493*5a8e4f25SAlexandru Elisei 		break;
494*5a8e4f25SAlexandru Elisei 	}
495*5a8e4f25SAlexandru Elisei 
496*5a8e4f25SAlexandru Elisei 	return r;
497*5a8e4f25SAlexandru Elisei }
498*5a8e4f25SAlexandru Elisei 
499*5a8e4f25SAlexandru Elisei static int virtio_pci__bar_deactivate(struct kvm *kvm,
500*5a8e4f25SAlexandru Elisei 				      struct pci_device_header *pci_hdr,
501*5a8e4f25SAlexandru Elisei 				      int bar_num, void *data)
502*5a8e4f25SAlexandru Elisei {
503*5a8e4f25SAlexandru Elisei 	u32 bar_addr;
504*5a8e4f25SAlexandru Elisei 	bool success;
505*5a8e4f25SAlexandru Elisei 	int r = -EINVAL;
506*5a8e4f25SAlexandru Elisei 
507*5a8e4f25SAlexandru Elisei 	assert(bar_num <= 2);
508*5a8e4f25SAlexandru Elisei 
509*5a8e4f25SAlexandru Elisei 	bar_addr = pci__bar_address(pci_hdr, bar_num);
510*5a8e4f25SAlexandru Elisei 
511*5a8e4f25SAlexandru Elisei 	switch (bar_num) {
512*5a8e4f25SAlexandru Elisei 	case 0:
513*5a8e4f25SAlexandru Elisei 		r = ioport__unregister(kvm, bar_addr);
514*5a8e4f25SAlexandru Elisei 		break;
515*5a8e4f25SAlexandru Elisei 	case 1:
516*5a8e4f25SAlexandru Elisei 	case 2:
517*5a8e4f25SAlexandru Elisei 		success = kvm__deregister_mmio(kvm, bar_addr);
518*5a8e4f25SAlexandru Elisei 		/* kvm__deregister_mmio fails when the region is not found. */
519*5a8e4f25SAlexandru Elisei 		r = (success ? 0 : -ENOENT);
520*5a8e4f25SAlexandru Elisei 		break;
521*5a8e4f25SAlexandru Elisei 	}
522*5a8e4f25SAlexandru Elisei 
523*5a8e4f25SAlexandru Elisei 	return r;
524*5a8e4f25SAlexandru Elisei }
525*5a8e4f25SAlexandru Elisei 
52602eca50cSAsias He int virtio_pci__init(struct kvm *kvm, void *dev, struct virtio_device *vdev,
527507e02d8SAsias He 		     int device_id, int subsys_id, int class)
52836f5dc91SSasha Levin {
52902eca50cSAsias He 	struct virtio_pci *vpci = vdev->virtio;
530e539f3e4SAlexandru Elisei 	u32 mmio_addr, msix_io_block;
531e539f3e4SAlexandru Elisei 	u16 port_addr;
5327af40b91SSasha Levin 	int r;
53336f5dc91SSasha Levin 
534a463650cSWill Deacon 	vpci->kvm = kvm;
53536f5dc91SSasha Levin 	vpci->dev = dev;
53636f5dc91SSasha Levin 
537ce2fc8f5SAlexandru Elisei 	BUILD_BUG_ON(!is_power_of_two(PCI_IO_SIZE));
538ce2fc8f5SAlexandru Elisei 
539e539f3e4SAlexandru Elisei 	port_addr = pci_get_io_port_block(PCI_IO_SIZE);
540e539f3e4SAlexandru Elisei 	mmio_addr = pci_get_mmio_block(PCI_IO_SIZE);
541e539f3e4SAlexandru Elisei 	msix_io_block = pci_get_mmio_block(PCI_IO_SIZE * 2);
542a463650cSWill Deacon 
54336f5dc91SSasha Levin 	vpci->pci_hdr = (struct pci_device_header) {
544aa73be70SMatt Evans 		.vendor_id		= cpu_to_le16(PCI_VENDOR_ID_REDHAT_QUMRANET),
545aa73be70SMatt Evans 		.device_id		= cpu_to_le16(device_id),
546ec7dd52fSSasha Levin 		.command		= PCI_COMMAND_IO | PCI_COMMAND_MEMORY,
54736f5dc91SSasha Levin 		.header_type		= PCI_HEADER_TYPE_NORMAL,
54836f5dc91SSasha Levin 		.revision_id		= 0,
549aa73be70SMatt Evans 		.class[0]		= class & 0xff,
550aa73be70SMatt Evans 		.class[1]		= (class >> 8) & 0xff,
551aa73be70SMatt Evans 		.class[2]		= (class >> 16) & 0xff,
552aa73be70SMatt Evans 		.subsys_vendor_id	= cpu_to_le16(PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET),
553aa73be70SMatt Evans 		.subsys_id		= cpu_to_le16(subsys_id),
554e539f3e4SAlexandru Elisei 		.bar[0]			= cpu_to_le32(port_addr
5559c26dab4SSasha Levin 							| PCI_BASE_ADDRESS_SPACE_IO),
556e539f3e4SAlexandru Elisei 		.bar[1]			= cpu_to_le32(mmio_addr
557a508ea95SJean-Philippe Brucker 							| PCI_BASE_ADDRESS_SPACE_MEMORY),
558e539f3e4SAlexandru Elisei 		.bar[2]			= cpu_to_le32(msix_io_block
559b4dab816SSasha Levin 							| PCI_BASE_ADDRESS_SPACE_MEMORY),
560aa73be70SMatt Evans 		.status			= cpu_to_le16(PCI_STATUS_CAP_LIST),
56136f5dc91SSasha Levin 		.capabilities		= (void *)&vpci->pci_hdr.msix - (void *)&vpci->pci_hdr,
56248843d10SJulien Thierry 		.bar_size[0]		= cpu_to_le32(PCI_IO_SIZE),
56348843d10SJulien Thierry 		.bar_size[1]		= cpu_to_le32(PCI_IO_SIZE),
564a0a7d66fSDavid Daney 		.bar_size[2]		= cpu_to_le32(PCI_IO_SIZE*2),
56536f5dc91SSasha Levin 	};
56636f5dc91SSasha Levin 
567*5a8e4f25SAlexandru Elisei 	r = pci__register_bar_regions(kvm, &vpci->pci_hdr,
568*5a8e4f25SAlexandru Elisei 				      virtio_pci__bar_activate,
569*5a8e4f25SAlexandru Elisei 				      virtio_pci__bar_deactivate, vdev);
570*5a8e4f25SAlexandru Elisei 	if (r < 0)
571*5a8e4f25SAlexandru Elisei 		return r;
572*5a8e4f25SAlexandru Elisei 
57321ff329dSWill Deacon 	vpci->dev_hdr = (struct device_header) {
57421ff329dSWill Deacon 		.bus_type		= DEVICE_BUS_PCI,
57521ff329dSWill Deacon 		.data			= &vpci->pci_hdr,
57621ff329dSWill Deacon 	};
57721ff329dSWill Deacon 
57836f5dc91SSasha Levin 	vpci->pci_hdr.msix.cap = PCI_CAP_ID_MSIX;
57936f5dc91SSasha Levin 	vpci->pci_hdr.msix.next = 0;
58014bba8a0SAsias He 	/*
58114bba8a0SAsias He 	 * We at most have VIRTIO_PCI_MAX_VQ entries for virt queue,
58214bba8a0SAsias He 	 * VIRTIO_PCI_MAX_CONFIG entries for config.
58314bba8a0SAsias He 	 *
58414bba8a0SAsias He 	 * To quote the PCI spec:
58514bba8a0SAsias He 	 *
58614bba8a0SAsias He 	 * System software reads this field to determine the
58714bba8a0SAsias He 	 * MSI-X Table Size N, which is encoded as N-1.
58814bba8a0SAsias He 	 * For example, a returned value of "00000000011"
58914bba8a0SAsias He 	 * indicates a table size of 4.
59014bba8a0SAsias He 	 */
591aa73be70SMatt Evans 	vpci->pci_hdr.msix.ctrl = cpu_to_le16(VIRTIO_PCI_MAX_VQ + VIRTIO_PCI_MAX_CONFIG - 1);
59206f48103SSasha Levin 
593a463650cSWill Deacon 	/* Both table and PBA are mapped to the same BAR (2) */
594a463650cSWill Deacon 	vpci->pci_hdr.msix.table_offset = cpu_to_le32(2);
595a463650cSWill Deacon 	vpci->pci_hdr.msix.pba_offset = cpu_to_le32(2 | PCI_IO_SIZE);
59636f5dc91SSasha Levin 	vpci->config_vector = 0;
59736f5dc91SSasha Levin 
598f6108d72SJean-Philippe Brucker 	if (irq__can_signal_msi(kvm))
59943c81c74SSasha Levin 		vpci->features |= VIRTIO_PCI_F_SIGNAL_MSI;
60043c81c74SSasha Levin 
601c0c45eedSAndre Przywara 	vpci->legacy_irq_line = pci__assign_irq(&vpci->pci_hdr);
602c0c45eedSAndre Przywara 
60321ff329dSWill Deacon 	r = device__register(&vpci->dev_hdr);
604495fbd4eSSasha Levin 	if (r < 0)
605*5a8e4f25SAlexandru Elisei 		return r;
606495fbd4eSSasha Levin 
607495fbd4eSSasha Levin 	return 0;
608495fbd4eSSasha Levin }
609495fbd4eSSasha Levin 
610eb34a8c2SJean-Philippe Brucker int virtio_pci__reset(struct kvm *kvm, struct virtio_device *vdev)
611eb34a8c2SJean-Philippe Brucker {
612eb34a8c2SJean-Philippe Brucker 	int vq;
613eb34a8c2SJean-Philippe Brucker 	struct virtio_pci *vpci = vdev->virtio;
614eb34a8c2SJean-Philippe Brucker 
615eb34a8c2SJean-Philippe Brucker 	for (vq = 0; vq < vdev->ops->get_vq_count(kvm, vpci->dev); vq++)
616eb34a8c2SJean-Philippe Brucker 		virtio_pci_exit_vq(kvm, vdev, vq);
617eb34a8c2SJean-Philippe Brucker 
618eb34a8c2SJean-Philippe Brucker 	return 0;
619eb34a8c2SJean-Philippe Brucker }
620eb34a8c2SJean-Philippe Brucker 
62102eca50cSAsias He int virtio_pci__exit(struct kvm *kvm, struct virtio_device *vdev)
622495fbd4eSSasha Levin {
62302eca50cSAsias He 	struct virtio_pci *vpci = vdev->virtio;
624495fbd4eSSasha Levin 
625eb34a8c2SJean-Philippe Brucker 	virtio_pci__reset(kvm, vdev);
626e539f3e4SAlexandru Elisei 	kvm__deregister_mmio(kvm, virtio_pci__mmio_addr(vpci));
627e539f3e4SAlexandru Elisei 	kvm__deregister_mmio(kvm, virtio_pci__msix_io_addr(vpci));
628e539f3e4SAlexandru Elisei 	ioport__unregister(kvm, virtio_pci__port_addr(vpci));
629495fbd4eSSasha Levin 
63036f5dc91SSasha Levin 	return 0;
63136f5dc91SSasha Levin }
632