xref: /kvmtool/virtio/pci.c (revision 36f5dc916e7f0a5bf900aa188c7dbf7c34b55fcc)
1*36f5dc91SSasha Levin #include "kvm/virtio-pci.h"
2*36f5dc91SSasha Levin 
3*36f5dc91SSasha Levin #include "kvm/ioport.h"
4*36f5dc91SSasha Levin #include "kvm/kvm.h"
5*36f5dc91SSasha Levin #include "kvm/virtio-pci-dev.h"
6*36f5dc91SSasha Levin #include "kvm/irq.h"
7*36f5dc91SSasha Levin #include "kvm/virtio.h"
8*36f5dc91SSasha Levin 
9*36f5dc91SSasha Levin #include <linux/virtio_pci.h>
10*36f5dc91SSasha Levin #include <string.h>
11*36f5dc91SSasha Levin 
12*36f5dc91SSasha Levin static bool virtio_pci__specific_io_in(struct kvm *kvm, struct virtio_pci *vpci, u16 port,
13*36f5dc91SSasha Levin 					void *data, int size, int offset)
14*36f5dc91SSasha Levin {
15*36f5dc91SSasha Levin 	u32 config_offset;
16*36f5dc91SSasha Levin 	int type = virtio__get_dev_specific_field(offset - 20, vpci->msix_enabled,
17*36f5dc91SSasha Levin 							0, &config_offset);
18*36f5dc91SSasha Levin 	if (type == VIRTIO_PCI_O_MSIX) {
19*36f5dc91SSasha Levin 		switch (offset) {
20*36f5dc91SSasha Levin 		case VIRTIO_MSI_CONFIG_VECTOR:
21*36f5dc91SSasha Levin 			ioport__write16(data, vpci->config_vector);
22*36f5dc91SSasha Levin 			break;
23*36f5dc91SSasha Levin 		case VIRTIO_MSI_QUEUE_VECTOR:
24*36f5dc91SSasha Levin 			ioport__write16(data, vpci->vq_vector[vpci->queue_selector]);
25*36f5dc91SSasha Levin 			break;
26*36f5dc91SSasha Levin 		};
27*36f5dc91SSasha Levin 
28*36f5dc91SSasha Levin 		return true;
29*36f5dc91SSasha Levin 	} else if (type == VIRTIO_PCI_O_CONFIG) {
30*36f5dc91SSasha Levin 		u8 cfg;
31*36f5dc91SSasha Levin 
32*36f5dc91SSasha Levin 		cfg = vpci->ops.get_config(kvm, vpci->dev, config_offset);
33*36f5dc91SSasha Levin 		ioport__write8(data, cfg);
34*36f5dc91SSasha Levin 		return true;
35*36f5dc91SSasha Levin 	}
36*36f5dc91SSasha Levin 
37*36f5dc91SSasha Levin 	return false;
38*36f5dc91SSasha Levin }
39*36f5dc91SSasha Levin 
40*36f5dc91SSasha Levin static bool virtio_pci__io_in(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size)
41*36f5dc91SSasha Levin {
42*36f5dc91SSasha Levin 	unsigned long offset;
43*36f5dc91SSasha Levin 	bool ret = true;
44*36f5dc91SSasha Levin 	struct virtio_pci *vpci;
45*36f5dc91SSasha Levin 	u32 val;
46*36f5dc91SSasha Levin 
47*36f5dc91SSasha Levin 	vpci = ioport->priv;
48*36f5dc91SSasha Levin 	offset = port - vpci->base_addr;
49*36f5dc91SSasha Levin 
50*36f5dc91SSasha Levin 	switch (offset) {
51*36f5dc91SSasha Levin 	case VIRTIO_PCI_HOST_FEATURES:
52*36f5dc91SSasha Levin 		val = vpci->ops.get_host_features(kvm, vpci->dev);
53*36f5dc91SSasha Levin 		ioport__write32(data, val);
54*36f5dc91SSasha Levin 		break;
55*36f5dc91SSasha Levin 	case VIRTIO_PCI_QUEUE_PFN:
56*36f5dc91SSasha Levin 		val = vpci->ops.get_pfn_vq(kvm, vpci->dev, vpci->queue_selector);
57*36f5dc91SSasha Levin 		ioport__write32(data, val);
58*36f5dc91SSasha Levin 		break;
59*36f5dc91SSasha Levin 	case VIRTIO_PCI_QUEUE_NUM:
60*36f5dc91SSasha Levin 		val = vpci->ops.get_size_vq(kvm, vpci->dev, vpci->queue_selector);
61*36f5dc91SSasha Levin 		ioport__write32(data, val);
62*36f5dc91SSasha Levin 		break;
63*36f5dc91SSasha Levin 		break;
64*36f5dc91SSasha Levin 	case VIRTIO_PCI_STATUS:
65*36f5dc91SSasha Levin 		ioport__write8(data, vpci->status);
66*36f5dc91SSasha Levin 		break;
67*36f5dc91SSasha Levin 	case VIRTIO_PCI_ISR:
68*36f5dc91SSasha Levin 		ioport__write8(data, vpci->isr);
69*36f5dc91SSasha Levin 		kvm__irq_line(kvm, vpci->pci_hdr.irq_line, VIRTIO_IRQ_LOW);
70*36f5dc91SSasha Levin 		vpci->isr = VIRTIO_IRQ_LOW;
71*36f5dc91SSasha Levin 		break;
72*36f5dc91SSasha Levin 	default:
73*36f5dc91SSasha Levin 		ret = virtio_pci__specific_io_in(kvm, vpci, port, data, size, offset);
74*36f5dc91SSasha Levin 		break;
75*36f5dc91SSasha Levin 	};
76*36f5dc91SSasha Levin 
77*36f5dc91SSasha Levin 	return ret;
78*36f5dc91SSasha Levin }
79*36f5dc91SSasha Levin 
80*36f5dc91SSasha Levin static bool virtio_pci__specific_io_out(struct kvm *kvm, struct virtio_pci *vpci, u16 port,
81*36f5dc91SSasha Levin 					void *data, int size, int offset)
82*36f5dc91SSasha Levin {
83*36f5dc91SSasha Levin 	u32 config_offset, gsi, vec;
84*36f5dc91SSasha Levin 	int type = virtio__get_dev_specific_field(offset - 20, vpci->msix_enabled,
85*36f5dc91SSasha Levin 							0, &config_offset);
86*36f5dc91SSasha Levin 	if (type == VIRTIO_PCI_O_MSIX) {
87*36f5dc91SSasha Levin 		switch (offset) {
88*36f5dc91SSasha Levin 		case VIRTIO_MSI_CONFIG_VECTOR:
89*36f5dc91SSasha Levin 			vec = vpci->config_vector = ioport__read16(data);
90*36f5dc91SSasha Levin 
91*36f5dc91SSasha Levin 			gsi = irq__add_msix_route(kvm,
92*36f5dc91SSasha Levin 						  vpci->pci_hdr.msix.table[vec].low,
93*36f5dc91SSasha Levin 						  vpci->pci_hdr.msix.table[vec].high,
94*36f5dc91SSasha Levin 						  vpci->pci_hdr.msix.table[vec].data);
95*36f5dc91SSasha Levin 
96*36f5dc91SSasha Levin 			vpci->config_gsi = gsi;
97*36f5dc91SSasha Levin 			break;
98*36f5dc91SSasha Levin 		case VIRTIO_MSI_QUEUE_VECTOR: {
99*36f5dc91SSasha Levin 			vec = vpci->vq_vector[vpci->queue_selector] = ioport__read16(data);
100*36f5dc91SSasha Levin 
101*36f5dc91SSasha Levin 			gsi = irq__add_msix_route(kvm,
102*36f5dc91SSasha Levin 						  vpci->pci_hdr.msix.table[vec].low,
103*36f5dc91SSasha Levin 						  vpci->pci_hdr.msix.table[vec].high,
104*36f5dc91SSasha Levin 						  vpci->pci_hdr.msix.table[vec].data);
105*36f5dc91SSasha Levin 			vpci->gsis[vpci->queue_selector] = gsi;
106*36f5dc91SSasha Levin 			break;
107*36f5dc91SSasha Levin 		}
108*36f5dc91SSasha Levin 		};
109*36f5dc91SSasha Levin 
110*36f5dc91SSasha Levin 		return true;
111*36f5dc91SSasha Levin 	} else if (type == VIRTIO_PCI_O_CONFIG) {
112*36f5dc91SSasha Levin 		vpci->ops.set_config(kvm, vpci->dev, *(u8 *)data, config_offset);
113*36f5dc91SSasha Levin 
114*36f5dc91SSasha Levin 		return true;
115*36f5dc91SSasha Levin 	}
116*36f5dc91SSasha Levin 
117*36f5dc91SSasha Levin 	return false;
118*36f5dc91SSasha Levin }
119*36f5dc91SSasha Levin 
120*36f5dc91SSasha Levin static bool virtio_pci__io_out(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size)
121*36f5dc91SSasha Levin {
122*36f5dc91SSasha Levin 	unsigned long offset;
123*36f5dc91SSasha Levin 	bool ret = true;
124*36f5dc91SSasha Levin 	struct virtio_pci *vpci;
125*36f5dc91SSasha Levin 	u32 val;
126*36f5dc91SSasha Levin 
127*36f5dc91SSasha Levin 	vpci = ioport->priv;
128*36f5dc91SSasha Levin 	offset = port - vpci->base_addr;
129*36f5dc91SSasha Levin 
130*36f5dc91SSasha Levin 	switch (offset) {
131*36f5dc91SSasha Levin 	case VIRTIO_PCI_GUEST_FEATURES:
132*36f5dc91SSasha Levin 		val = ioport__read32(data);
133*36f5dc91SSasha Levin 		vpci->ops.set_guest_features(kvm, vpci, val);
134*36f5dc91SSasha Levin 		break;
135*36f5dc91SSasha Levin 	case VIRTIO_PCI_QUEUE_PFN:
136*36f5dc91SSasha Levin 		val = ioport__read32(data);
137*36f5dc91SSasha Levin 		vpci->ops.init_vq(kvm, vpci->dev, vpci->queue_selector, val);
138*36f5dc91SSasha Levin 		break;
139*36f5dc91SSasha Levin 	case VIRTIO_PCI_QUEUE_SEL:
140*36f5dc91SSasha Levin 		vpci->queue_selector	= ioport__read16(data);
141*36f5dc91SSasha Levin 		break;
142*36f5dc91SSasha Levin 	case VIRTIO_PCI_QUEUE_NOTIFY:
143*36f5dc91SSasha Levin 		val			= ioport__read16(data);
144*36f5dc91SSasha Levin 		vpci->ops.notify_vq(kvm, vpci->dev, val);
145*36f5dc91SSasha Levin 		break;
146*36f5dc91SSasha Levin 	case VIRTIO_PCI_STATUS:
147*36f5dc91SSasha Levin 		vpci->status		= ioport__read8(data);
148*36f5dc91SSasha Levin 		break;
149*36f5dc91SSasha Levin 	default:
150*36f5dc91SSasha Levin 		ret = virtio_pci__specific_io_out(kvm, vpci, port, data, size, offset);
151*36f5dc91SSasha Levin 		break;
152*36f5dc91SSasha Levin 	};
153*36f5dc91SSasha Levin 
154*36f5dc91SSasha Levin 	return ret;
155*36f5dc91SSasha Levin }
156*36f5dc91SSasha Levin 
157*36f5dc91SSasha Levin static struct ioport_operations virtio_pci__io_ops = {
158*36f5dc91SSasha Levin 	.io_in	= virtio_pci__io_in,
159*36f5dc91SSasha Levin 	.io_out	= virtio_pci__io_out,
160*36f5dc91SSasha Levin };
161*36f5dc91SSasha Levin 
162*36f5dc91SSasha Levin static void callback_mmio(u64 addr, u8 *data, u32 len, u8 is_write, void *ptr)
163*36f5dc91SSasha Levin {
164*36f5dc91SSasha Levin 	struct virtio_pci *vpci = ptr;
165*36f5dc91SSasha Levin 	void *table = &vpci->pci_hdr.msix.table;
166*36f5dc91SSasha Levin 
167*36f5dc91SSasha Levin 	vpci->msix_enabled = 1;
168*36f5dc91SSasha Levin 	if (is_write)
169*36f5dc91SSasha Levin 		memcpy(table + addr - vpci->msix_io_block, data, len);
170*36f5dc91SSasha Levin 	else
171*36f5dc91SSasha Levin 		memcpy(data, table + addr - vpci->msix_io_block, len);
172*36f5dc91SSasha Levin }
173*36f5dc91SSasha Levin 
174*36f5dc91SSasha Levin int virtio_pci__signal_vq(struct kvm *kvm, struct virtio_pci *vpci, u32 vq)
175*36f5dc91SSasha Levin {
176*36f5dc91SSasha Levin 	kvm__irq_line(kvm, vpci->gsis[vq], VIRTIO_IRQ_HIGH);
177*36f5dc91SSasha Levin 
178*36f5dc91SSasha Levin 	return 0;
179*36f5dc91SSasha Levin }
180*36f5dc91SSasha Levin 
181*36f5dc91SSasha Levin int virtio_pci__signal_config(struct kvm *kvm, struct virtio_pci *vpci)
182*36f5dc91SSasha Levin {
183*36f5dc91SSasha Levin 	kvm__irq_line(kvm, vpci->config_gsi, VIRTIO_IRQ_HIGH);
184*36f5dc91SSasha Levin 
185*36f5dc91SSasha Levin 	return 0;
186*36f5dc91SSasha Levin }
187*36f5dc91SSasha Levin 
188*36f5dc91SSasha Levin int virtio_pci__init(struct kvm *kvm, struct virtio_pci *vpci, void *dev,
189*36f5dc91SSasha Levin 			int device_id, int subsys_id)
190*36f5dc91SSasha Levin {
191*36f5dc91SSasha Levin 	u8 pin, line, ndev;
192*36f5dc91SSasha Levin 
193*36f5dc91SSasha Levin 	vpci->dev = dev;
194*36f5dc91SSasha Levin 	vpci->msix_io_block = pci_get_io_space_block();
195*36f5dc91SSasha Levin 
196*36f5dc91SSasha Levin 	vpci->base_addr = ioport__register(IOPORT_EMPTY, &virtio_pci__io_ops, IOPORT_SIZE, vpci);
197*36f5dc91SSasha Levin 	kvm__register_mmio(kvm, vpci->msix_io_block, 0x100, callback_mmio, vpci);
198*36f5dc91SSasha Levin 
199*36f5dc91SSasha Levin 	vpci->pci_hdr = (struct pci_device_header) {
200*36f5dc91SSasha Levin 		.vendor_id		= PCI_VENDOR_ID_REDHAT_QUMRANET,
201*36f5dc91SSasha Levin 		.device_id		= device_id,
202*36f5dc91SSasha Levin 		.header_type		= PCI_HEADER_TYPE_NORMAL,
203*36f5dc91SSasha Levin 		.revision_id		= 0,
204*36f5dc91SSasha Levin 		.class			= 0x010000,
205*36f5dc91SSasha Levin 		.subsys_vendor_id	= PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET,
206*36f5dc91SSasha Levin 		.subsys_id		= subsys_id,
207*36f5dc91SSasha Levin 		.bar[0]			= vpci->base_addr | PCI_BASE_ADDRESS_SPACE_IO,
208*36f5dc91SSasha Levin 		.bar[1]			= vpci->msix_io_block |
209*36f5dc91SSasha Levin 					PCI_BASE_ADDRESS_SPACE_MEMORY |
210*36f5dc91SSasha Levin 					PCI_BASE_ADDRESS_MEM_TYPE_64,
211*36f5dc91SSasha Levin 		/* bar[2] is the continuation of bar[1] for 64bit addressing */
212*36f5dc91SSasha Levin 		.bar[2]			= 0,
213*36f5dc91SSasha Levin 		.status			= PCI_STATUS_CAP_LIST,
214*36f5dc91SSasha Levin 		.capabilities		= (void *)&vpci->pci_hdr.msix - (void *)&vpci->pci_hdr,
215*36f5dc91SSasha Levin 	};
216*36f5dc91SSasha Levin 
217*36f5dc91SSasha Levin 	vpci->pci_hdr.msix.cap = PCI_CAP_ID_MSIX;
218*36f5dc91SSasha Levin 	vpci->pci_hdr.msix.next = 0;
219*36f5dc91SSasha Levin 	vpci->pci_hdr.msix.table_size = (VIRTIO_PCI_MAX_VQ + 1) | PCI_MSIX_FLAGS_ENABLE;
220*36f5dc91SSasha Levin 	vpci->pci_hdr.msix.table_offset = 1; /* Use BAR 1 */
221*36f5dc91SSasha Levin 	vpci->config_vector = 0;
222*36f5dc91SSasha Levin 
223*36f5dc91SSasha Levin 	if (irq__register_device(VIRTIO_ID_RNG, &ndev, &pin, &line) < 0)
224*36f5dc91SSasha Levin 		return -1;
225*36f5dc91SSasha Levin 
226*36f5dc91SSasha Levin 	vpci->pci_hdr.irq_pin	= pin;
227*36f5dc91SSasha Levin 	vpci->pci_hdr.irq_line	= line;
228*36f5dc91SSasha Levin 	pci__register(&vpci->pci_hdr, ndev);
229*36f5dc91SSasha Levin 
230*36f5dc91SSasha Levin 	return 0;
231*36f5dc91SSasha Levin }
232