xref: /kvmtool/virtio/pci-modern.c (revision fe2182731b721ecaec97cb7f02b2c334469669ce)
1b0d56e3cSJean-Philippe Brucker #include "kvm/virtio-pci.h"
2b0d56e3cSJean-Philippe Brucker 
3b0d56e3cSJean-Philippe Brucker #include "kvm/ioport.h"
4b0d56e3cSJean-Philippe Brucker #include "kvm/virtio.h"
5b0d56e3cSJean-Philippe Brucker #include "kvm/virtio-pci-dev.h"
6b0d56e3cSJean-Philippe Brucker 
7b0d56e3cSJean-Philippe Brucker #include <linux/virtio_config.h>
8b0d56e3cSJean-Philippe Brucker 
9b0d56e3cSJean-Philippe Brucker #define VPCI_CFG_COMMON_SIZE	sizeof(struct virtio_pci_common_cfg)
10b0d56e3cSJean-Philippe Brucker #define VPCI_CFG_COMMON_START	0
11b0d56e3cSJean-Philippe Brucker #define VPCI_CFG_COMMON_END	(VPCI_CFG_COMMON_SIZE - 1)
12b0d56e3cSJean-Philippe Brucker /*
13b0d56e3cSJean-Philippe Brucker  * Use a naturally aligned 4-byte doorbell, in case we ever want to
14b0d56e3cSJean-Philippe Brucker  * implement VIRTIO_F_NOTIFICATION_DATA
15b0d56e3cSJean-Philippe Brucker  */
16b0d56e3cSJean-Philippe Brucker #define VPCI_CFG_NOTIFY_SIZE	4
17b0d56e3cSJean-Philippe Brucker #define VPCI_CFG_NOTIFY_START	(VPCI_CFG_COMMON_END + 1)
18b0d56e3cSJean-Philippe Brucker #define VPCI_CFG_NOTIFY_END	(VPCI_CFG_COMMON_END + VPCI_CFG_NOTIFY_SIZE)
19b0d56e3cSJean-Philippe Brucker #define VPCI_CFG_ISR_SIZE	4
20b0d56e3cSJean-Philippe Brucker #define VPCI_CFG_ISR_START	(VPCI_CFG_NOTIFY_END + 1)
21b0d56e3cSJean-Philippe Brucker #define VPCI_CFG_ISR_END	(VPCI_CFG_NOTIFY_END + VPCI_CFG_ISR_SIZE)
22b0d56e3cSJean-Philippe Brucker /*
23b0d56e3cSJean-Philippe Brucker  * We're at 64 bytes. Use the remaining 192 bytes in PCI_IO_SIZE for the
24b0d56e3cSJean-Philippe Brucker  * device-specific config space. It's sufficient for the devices we
25b0d56e3cSJean-Philippe Brucker  * currently implement (virtio_blk_config is 60 bytes) and, I think, all
26b0d56e3cSJean-Philippe Brucker  * existing virtio 1.2 devices.
27b0d56e3cSJean-Philippe Brucker  */
28b0d56e3cSJean-Philippe Brucker #define VPCI_CFG_DEV_START	(VPCI_CFG_ISR_END + 1)
29b0d56e3cSJean-Philippe Brucker #define VPCI_CFG_DEV_END	((PCI_IO_SIZE) - 1)
30b0d56e3cSJean-Philippe Brucker #define VPCI_CFG_DEV_SIZE	(VPCI_CFG_DEV_END - VPCI_CFG_DEV_START + 1)
31b0d56e3cSJean-Philippe Brucker 
32b0d56e3cSJean-Philippe Brucker #define vpci_selected_vq(vpci) \
33b0d56e3cSJean-Philippe Brucker 	vdev->ops->get_vq((vpci)->kvm, (vpci)->dev, (vpci)->queue_selector)
34b0d56e3cSJean-Philippe Brucker 
35b0d56e3cSJean-Philippe Brucker typedef bool (*access_handler_t)(struct virtio_device *, unsigned long, void *, int);
36b0d56e3cSJean-Philippe Brucker 
virtio_pci__common_write(struct virtio_device * vdev,unsigned long offset,void * data,int size)37b0d56e3cSJean-Philippe Brucker static bool virtio_pci__common_write(struct virtio_device *vdev,
38b0d56e3cSJean-Philippe Brucker 				     unsigned long offset, void *data, int size)
39b0d56e3cSJean-Philippe Brucker {
40b0d56e3cSJean-Philippe Brucker 	u64 features;
41b0d56e3cSJean-Philippe Brucker 	u32 val, gsi, vec;
42b0d56e3cSJean-Philippe Brucker 	struct virtio_pci *vpci = vdev->virtio;
43b0d56e3cSJean-Philippe Brucker 
44b0d56e3cSJean-Philippe Brucker 	switch (offset - VPCI_CFG_COMMON_START) {
45b0d56e3cSJean-Philippe Brucker 	case VIRTIO_PCI_COMMON_DFSELECT:
46b0d56e3cSJean-Philippe Brucker 		vpci->device_features_sel = ioport__read32(data);
47b0d56e3cSJean-Philippe Brucker 		break;
48b0d56e3cSJean-Philippe Brucker 	case VIRTIO_PCI_COMMON_GFSELECT:
49b0d56e3cSJean-Philippe Brucker 		vpci->driver_features_sel = ioport__read32(data);
50b0d56e3cSJean-Philippe Brucker 		break;
51b0d56e3cSJean-Philippe Brucker 	case VIRTIO_PCI_COMMON_GF:
52b0d56e3cSJean-Philippe Brucker 		val = ioport__read32(data);
53b0d56e3cSJean-Philippe Brucker 		if (vpci->driver_features_sel > 1)
54b0d56e3cSJean-Philippe Brucker 			break;
55b0d56e3cSJean-Philippe Brucker 
56b0d56e3cSJean-Philippe Brucker 		features = (u64)val << (32 * vpci->driver_features_sel);
57b0d56e3cSJean-Philippe Brucker 		virtio_set_guest_features(vpci->kvm, vdev, vpci->dev, features);
58b0d56e3cSJean-Philippe Brucker 		break;
59b0d56e3cSJean-Philippe Brucker 	case VIRTIO_PCI_COMMON_MSIX:
60b0d56e3cSJean-Philippe Brucker 		vec = vpci->config_vector = ioport__read16(data);
61b0d56e3cSJean-Philippe Brucker 		gsi = virtio_pci__add_msix_route(vpci, vec);
62b0d56e3cSJean-Philippe Brucker 		if (gsi < 0)
63b0d56e3cSJean-Philippe Brucker 			break;
64b0d56e3cSJean-Philippe Brucker 
65b0d56e3cSJean-Philippe Brucker 		vpci->config_gsi = gsi;
66b0d56e3cSJean-Philippe Brucker 		break;
67b0d56e3cSJean-Philippe Brucker 	case VIRTIO_PCI_COMMON_STATUS:
68b0d56e3cSJean-Philippe Brucker 		vpci->status = ioport__read8(data);
69b0d56e3cSJean-Philippe Brucker 		virtio_notify_status(vpci->kvm, vdev, vpci->dev, vpci->status);
70b0d56e3cSJean-Philippe Brucker 		break;
71b0d56e3cSJean-Philippe Brucker 	case VIRTIO_PCI_COMMON_Q_SELECT:
72b0d56e3cSJean-Philippe Brucker 		val = ioport__read16(data);
73b0d56e3cSJean-Philippe Brucker 		if (val >= (u32)vdev->ops->get_vq_count(vpci->kvm, vpci->dev))
74b0d56e3cSJean-Philippe Brucker 			pr_warning("invalid vq number %u", val);
75b0d56e3cSJean-Philippe Brucker 		else
76b0d56e3cSJean-Philippe Brucker 			vpci->queue_selector = val;
77b0d56e3cSJean-Philippe Brucker 		break;
78b0d56e3cSJean-Philippe Brucker 	case VIRTIO_PCI_COMMON_Q_SIZE:
79b0d56e3cSJean-Philippe Brucker 		vdev->ops->set_size_vq(vpci->kvm, vpci->dev,
80b0d56e3cSJean-Philippe Brucker 				       vpci->queue_selector,
81b0d56e3cSJean-Philippe Brucker 				       ioport__read16(data));
82b0d56e3cSJean-Philippe Brucker 		break;
83b0d56e3cSJean-Philippe Brucker 	case VIRTIO_PCI_COMMON_Q_MSIX:
84b0d56e3cSJean-Philippe Brucker 		vec = vpci->vq_vector[vpci->queue_selector] = ioport__read16(data);
85b0d56e3cSJean-Philippe Brucker 
86b0d56e3cSJean-Philippe Brucker 		gsi = virtio_pci__add_msix_route(vpci, vec);
87b0d56e3cSJean-Philippe Brucker 		if (gsi < 0)
88b0d56e3cSJean-Philippe Brucker 			break;
89b0d56e3cSJean-Philippe Brucker 
90b0d56e3cSJean-Philippe Brucker 		vpci->gsis[vpci->queue_selector] = gsi;
91b0d56e3cSJean-Philippe Brucker 		if (vdev->ops->notify_vq_gsi)
92b0d56e3cSJean-Philippe Brucker 			vdev->ops->notify_vq_gsi(vpci->kvm, vpci->dev,
93b0d56e3cSJean-Philippe Brucker 						 vpci->queue_selector, gsi);
94b0d56e3cSJean-Philippe Brucker 		break;
95b0d56e3cSJean-Philippe Brucker 	case VIRTIO_PCI_COMMON_Q_ENABLE:
96b0d56e3cSJean-Philippe Brucker 		val = ioport__read16(data);
97b0d56e3cSJean-Philippe Brucker 		if (val)
98b0d56e3cSJean-Philippe Brucker 			virtio_pci_init_vq(vpci->kvm, vdev, vpci->queue_selector);
99b0d56e3cSJean-Philippe Brucker 		else
100b0d56e3cSJean-Philippe Brucker 			virtio_pci_exit_vq(vpci->kvm, vdev, vpci->queue_selector);
101b0d56e3cSJean-Philippe Brucker 		break;
102b0d56e3cSJean-Philippe Brucker 	case VIRTIO_PCI_COMMON_Q_DESCLO:
103b0d56e3cSJean-Philippe Brucker 		vpci_selected_vq(vpci)->vring_addr.desc_lo = ioport__read32(data);
104b0d56e3cSJean-Philippe Brucker 		break;
105b0d56e3cSJean-Philippe Brucker 	case VIRTIO_PCI_COMMON_Q_DESCHI:
106b0d56e3cSJean-Philippe Brucker 		vpci_selected_vq(vpci)->vring_addr.desc_hi = ioport__read32(data);
107b0d56e3cSJean-Philippe Brucker 		break;
108b0d56e3cSJean-Philippe Brucker 	case VIRTIO_PCI_COMMON_Q_AVAILLO:
109b0d56e3cSJean-Philippe Brucker 		vpci_selected_vq(vpci)->vring_addr.avail_lo = ioport__read32(data);
110b0d56e3cSJean-Philippe Brucker 		break;
111b0d56e3cSJean-Philippe Brucker 	case VIRTIO_PCI_COMMON_Q_AVAILHI:
112b0d56e3cSJean-Philippe Brucker 		vpci_selected_vq(vpci)->vring_addr.avail_hi = ioport__read32(data);
113b0d56e3cSJean-Philippe Brucker 		break;
114b0d56e3cSJean-Philippe Brucker 	case VIRTIO_PCI_COMMON_Q_USEDLO:
115b0d56e3cSJean-Philippe Brucker 		vpci_selected_vq(vpci)->vring_addr.used_lo = ioport__read32(data);
116b0d56e3cSJean-Philippe Brucker 		break;
117b0d56e3cSJean-Philippe Brucker 	case VIRTIO_PCI_COMMON_Q_USEDHI:
118b0d56e3cSJean-Philippe Brucker 		vpci_selected_vq(vpci)->vring_addr.used_hi = ioport__read32(data);
119b0d56e3cSJean-Philippe Brucker 		break;
120b0d56e3cSJean-Philippe Brucker 	}
121b0d56e3cSJean-Philippe Brucker 
122b0d56e3cSJean-Philippe Brucker 	return true;
123b0d56e3cSJean-Philippe Brucker }
124b0d56e3cSJean-Philippe Brucker 
virtio_pci__notify_write(struct virtio_device * vdev,unsigned long offset,void * data,int size)125b0d56e3cSJean-Philippe Brucker static bool virtio_pci__notify_write(struct virtio_device *vdev,
126b0d56e3cSJean-Philippe Brucker 				     unsigned long offset, void *data, int size)
127b0d56e3cSJean-Philippe Brucker {
128b0d56e3cSJean-Philippe Brucker 	u16 vq = ioport__read16(data);
129b0d56e3cSJean-Philippe Brucker 	struct virtio_pci *vpci = vdev->virtio;
130b0d56e3cSJean-Philippe Brucker 
131b0d56e3cSJean-Philippe Brucker 	vdev->ops->notify_vq(vpci->kvm, vpci->dev, vq);
132b0d56e3cSJean-Philippe Brucker 
133b0d56e3cSJean-Philippe Brucker 	return true;
134b0d56e3cSJean-Philippe Brucker }
135b0d56e3cSJean-Philippe Brucker 
virtio_pci__config_write(struct virtio_device * vdev,unsigned long offset,void * data,int size)136b0d56e3cSJean-Philippe Brucker static bool virtio_pci__config_write(struct virtio_device *vdev,
137b0d56e3cSJean-Philippe Brucker 				     unsigned long offset, void *data, int size)
138b0d56e3cSJean-Philippe Brucker {
139b0d56e3cSJean-Philippe Brucker 	struct virtio_pci *vpci = vdev->virtio;
140b0d56e3cSJean-Philippe Brucker 
141b0d56e3cSJean-Philippe Brucker 	return virtio_access_config(vpci->kvm, vdev, vpci->dev,
142b0d56e3cSJean-Philippe Brucker 				    offset - VPCI_CFG_DEV_START, data, size,
143b0d56e3cSJean-Philippe Brucker 				    true);
144b0d56e3cSJean-Philippe Brucker }
145b0d56e3cSJean-Philippe Brucker 
virtio_pci__common_read(struct virtio_device * vdev,unsigned long offset,void * data,int size)146b0d56e3cSJean-Philippe Brucker static bool virtio_pci__common_read(struct virtio_device *vdev,
147b0d56e3cSJean-Philippe Brucker 				    unsigned long offset, void *data, int size)
148b0d56e3cSJean-Philippe Brucker {
149b0d56e3cSJean-Philippe Brucker 	u32 val;
150b0d56e3cSJean-Philippe Brucker 	struct virtio_pci *vpci = vdev->virtio;
151b0d56e3cSJean-Philippe Brucker 	u64 features = 1ULL << VIRTIO_F_VERSION_1;
152b0d56e3cSJean-Philippe Brucker 
153b0d56e3cSJean-Philippe Brucker 	switch (offset - VPCI_CFG_COMMON_START) {
154b0d56e3cSJean-Philippe Brucker 	case VIRTIO_PCI_COMMON_DFSELECT:
155b0d56e3cSJean-Philippe Brucker 		val = vpci->device_features_sel;
156b0d56e3cSJean-Philippe Brucker 		ioport__write32(data, val);
157b0d56e3cSJean-Philippe Brucker 		break;
158b0d56e3cSJean-Philippe Brucker 	case VIRTIO_PCI_COMMON_DF:
159b0d56e3cSJean-Philippe Brucker 		if (vpci->device_features_sel > 1)
160b0d56e3cSJean-Philippe Brucker 			break;
161b0d56e3cSJean-Philippe Brucker 		features |= vdev->ops->get_host_features(vpci->kvm, vpci->dev);
162b0d56e3cSJean-Philippe Brucker 		val = features >> (32 * vpci->device_features_sel);
163b0d56e3cSJean-Philippe Brucker 		ioport__write32(data, val);
164b0d56e3cSJean-Philippe Brucker 		break;
165b0d56e3cSJean-Philippe Brucker 	case VIRTIO_PCI_COMMON_GFSELECT:
166b0d56e3cSJean-Philippe Brucker 		val = vpci->driver_features_sel;
167b0d56e3cSJean-Philippe Brucker 		ioport__write32(data, val);
168b0d56e3cSJean-Philippe Brucker 		break;
169b0d56e3cSJean-Philippe Brucker 	case VIRTIO_PCI_COMMON_MSIX:
170b0d56e3cSJean-Philippe Brucker 		val = vpci->config_vector;
171b0d56e3cSJean-Philippe Brucker 		ioport__write32(data, val);
172b0d56e3cSJean-Philippe Brucker 		break;
173b0d56e3cSJean-Philippe Brucker 	case VIRTIO_PCI_COMMON_NUMQ:
174b0d56e3cSJean-Philippe Brucker 		val = vdev->ops->get_vq_count(vpci->kvm, vpci->dev);
175b0d56e3cSJean-Philippe Brucker 		ioport__write32(data, val);
176b0d56e3cSJean-Philippe Brucker 		break;
177b0d56e3cSJean-Philippe Brucker 	case VIRTIO_PCI_COMMON_STATUS:
178b0d56e3cSJean-Philippe Brucker 		ioport__write8(data, vpci->status);
179b0d56e3cSJean-Philippe Brucker 		break;
180b0d56e3cSJean-Philippe Brucker 	case VIRTIO_PCI_COMMON_CFGGENERATION:
181b0d56e3cSJean-Philippe Brucker 		/*
182b0d56e3cSJean-Philippe Brucker 		 * The config generation changes when the device updates a
183b0d56e3cSJean-Philippe Brucker 		 * config field larger than 32 bits, that the driver may read
184b0d56e3cSJean-Philippe Brucker 		 * using multiple accesses. Since kvmtool doesn't use any
185b0d56e3cSJean-Philippe Brucker 		 * mutable config field larger than 32 bits, the generation is
186b0d56e3cSJean-Philippe Brucker 		 * constant.
187b0d56e3cSJean-Philippe Brucker 		 */
188b0d56e3cSJean-Philippe Brucker 		ioport__write8(data, 0);
189b0d56e3cSJean-Philippe Brucker 		break;
190b0d56e3cSJean-Philippe Brucker 	case VIRTIO_PCI_COMMON_Q_SELECT:
191b0d56e3cSJean-Philippe Brucker 		ioport__write16(data, vpci->queue_selector);
192b0d56e3cSJean-Philippe Brucker 		break;
193b0d56e3cSJean-Philippe Brucker 	case VIRTIO_PCI_COMMON_Q_SIZE:
194b0d56e3cSJean-Philippe Brucker 		val = vdev->ops->get_size_vq(vpci->kvm, vpci->dev,
195b0d56e3cSJean-Philippe Brucker 					     vpci->queue_selector);
196b0d56e3cSJean-Philippe Brucker 		ioport__write16(data, val);
197b0d56e3cSJean-Philippe Brucker 		break;
198b0d56e3cSJean-Philippe Brucker 	case VIRTIO_PCI_COMMON_Q_MSIX:
199b0d56e3cSJean-Philippe Brucker 		val = vpci->vq_vector[vpci->queue_selector];
200b0d56e3cSJean-Philippe Brucker 		ioport__write16(data, val);
201b0d56e3cSJean-Philippe Brucker 		break;
202b0d56e3cSJean-Philippe Brucker 	case VIRTIO_PCI_COMMON_Q_ENABLE:
203b0d56e3cSJean-Philippe Brucker 		val = vpci_selected_vq(vpci)->enabled;
204b0d56e3cSJean-Philippe Brucker 		ioport__write16(data, val);
205b0d56e3cSJean-Philippe Brucker 		break;
206b0d56e3cSJean-Philippe Brucker 	case VIRTIO_PCI_COMMON_Q_NOFF:
207b0d56e3cSJean-Philippe Brucker 		val = vpci->queue_selector;
208b0d56e3cSJean-Philippe Brucker 		ioport__write16(data, val);
209b0d56e3cSJean-Philippe Brucker 		break;
210b0d56e3cSJean-Philippe Brucker 	case VIRTIO_PCI_COMMON_Q_DESCLO:
211b0d56e3cSJean-Philippe Brucker 		val = vpci_selected_vq(vpci)->vring_addr.desc_lo;
212b0d56e3cSJean-Philippe Brucker 		ioport__write32(data, val);
213b0d56e3cSJean-Philippe Brucker 		break;
214b0d56e3cSJean-Philippe Brucker 	case VIRTIO_PCI_COMMON_Q_DESCHI:
215b0d56e3cSJean-Philippe Brucker 		val = vpci_selected_vq(vpci)->vring_addr.desc_hi;
216b0d56e3cSJean-Philippe Brucker 		ioport__write32(data, val);
217b0d56e3cSJean-Philippe Brucker 		break;
218b0d56e3cSJean-Philippe Brucker 	case VIRTIO_PCI_COMMON_Q_AVAILLO:
219b0d56e3cSJean-Philippe Brucker 		val = vpci_selected_vq(vpci)->vring_addr.avail_lo;
220b0d56e3cSJean-Philippe Brucker 		ioport__write32(data, val);
221b0d56e3cSJean-Philippe Brucker 		break;
222b0d56e3cSJean-Philippe Brucker 	case VIRTIO_PCI_COMMON_Q_AVAILHI:
223b0d56e3cSJean-Philippe Brucker 		val = vpci_selected_vq(vpci)->vring_addr.avail_hi;
224b0d56e3cSJean-Philippe Brucker 		ioport__write32(data, val);
225b0d56e3cSJean-Philippe Brucker 		break;
226b0d56e3cSJean-Philippe Brucker 	case VIRTIO_PCI_COMMON_Q_USEDLO:
227b0d56e3cSJean-Philippe Brucker 		val = vpci_selected_vq(vpci)->vring_addr.used_lo;
228b0d56e3cSJean-Philippe Brucker 		ioport__write32(data, val);
229b0d56e3cSJean-Philippe Brucker 		break;
230b0d56e3cSJean-Philippe Brucker 	case VIRTIO_PCI_COMMON_Q_USEDHI:
231b0d56e3cSJean-Philippe Brucker 		val = vpci_selected_vq(vpci)->vring_addr.used_hi;
232b0d56e3cSJean-Philippe Brucker 		ioport__write32(data, val);
233b0d56e3cSJean-Philippe Brucker 		break;
234b0d56e3cSJean-Philippe Brucker 	};
235b0d56e3cSJean-Philippe Brucker 
236b0d56e3cSJean-Philippe Brucker 	return true;
237b0d56e3cSJean-Philippe Brucker }
238b0d56e3cSJean-Philippe Brucker 
virtio_pci__isr_read(struct virtio_device * vdev,unsigned long offset,void * data,int size)239b0d56e3cSJean-Philippe Brucker static bool virtio_pci__isr_read(struct virtio_device *vdev,
240b0d56e3cSJean-Philippe Brucker 				 unsigned long offset, void *data, int size)
241b0d56e3cSJean-Philippe Brucker {
242b0d56e3cSJean-Philippe Brucker 	struct virtio_pci *vpci = vdev->virtio;
243b0d56e3cSJean-Philippe Brucker 
244b0d56e3cSJean-Philippe Brucker 	if (WARN_ON(offset - VPCI_CFG_ISR_START != 0))
245b0d56e3cSJean-Philippe Brucker 		return false;
246b0d56e3cSJean-Philippe Brucker 
247b0d56e3cSJean-Philippe Brucker 	ioport__write8(data, vpci->isr);
248*fe218273SJean-Philippe Brucker 	kvm__irq_line(vpci->kvm, vpci->legacy_irq_line, VIRTIO_IRQ_LOW);
249b0d56e3cSJean-Philippe Brucker 	vpci->isr = 0;
250b0d56e3cSJean-Philippe Brucker 
251b0d56e3cSJean-Philippe Brucker 	return 0;
252b0d56e3cSJean-Philippe Brucker }
253b0d56e3cSJean-Philippe Brucker 
virtio_pci__config_read(struct virtio_device * vdev,unsigned long offset,void * data,int size)254b0d56e3cSJean-Philippe Brucker static bool virtio_pci__config_read(struct virtio_device *vdev,
255b0d56e3cSJean-Philippe Brucker 				    unsigned long offset, void *data, int size)
256b0d56e3cSJean-Philippe Brucker {
257b0d56e3cSJean-Philippe Brucker 	struct virtio_pci *vpci = vdev->virtio;
258b0d56e3cSJean-Philippe Brucker 
259b0d56e3cSJean-Philippe Brucker 	return virtio_access_config(vpci->kvm, vdev, vpci->dev,
260b0d56e3cSJean-Philippe Brucker 				    offset - VPCI_CFG_DEV_START, data, size,
261b0d56e3cSJean-Philippe Brucker 				    false);
262b0d56e3cSJean-Philippe Brucker }
263b0d56e3cSJean-Philippe Brucker 
virtio_pci_access(struct kvm_cpu * vcpu,struct virtio_device * vdev,unsigned long offset,void * data,int size,bool write)264b0d56e3cSJean-Philippe Brucker static bool virtio_pci_access(struct kvm_cpu *vcpu, struct virtio_device *vdev,
265b0d56e3cSJean-Philippe Brucker 			      unsigned long offset, void *data, int size,
266b0d56e3cSJean-Philippe Brucker 			      bool write)
267b0d56e3cSJean-Philippe Brucker {
268b0d56e3cSJean-Philippe Brucker 	access_handler_t handler = NULL;
269b0d56e3cSJean-Philippe Brucker 
270b0d56e3cSJean-Philippe Brucker 	switch (offset) {
271b0d56e3cSJean-Philippe Brucker 	case VPCI_CFG_COMMON_START...VPCI_CFG_COMMON_END:
272b0d56e3cSJean-Philippe Brucker 		if (write)
273b0d56e3cSJean-Philippe Brucker 			handler = virtio_pci__common_write;
274b0d56e3cSJean-Philippe Brucker 		else
275b0d56e3cSJean-Philippe Brucker 			handler = virtio_pci__common_read;
276b0d56e3cSJean-Philippe Brucker 		break;
277b0d56e3cSJean-Philippe Brucker 	case VPCI_CFG_NOTIFY_START...VPCI_CFG_NOTIFY_END:
278b0d56e3cSJean-Philippe Brucker 		if (write)
279b0d56e3cSJean-Philippe Brucker 			handler = virtio_pci__notify_write;
280b0d56e3cSJean-Philippe Brucker 		break;
281b0d56e3cSJean-Philippe Brucker 	case VPCI_CFG_ISR_START...VPCI_CFG_ISR_END:
282b0d56e3cSJean-Philippe Brucker 		if (!write)
283b0d56e3cSJean-Philippe Brucker 			handler = virtio_pci__isr_read;
284b0d56e3cSJean-Philippe Brucker 		break;
285b0d56e3cSJean-Philippe Brucker 	case VPCI_CFG_DEV_START...VPCI_CFG_DEV_END:
286b0d56e3cSJean-Philippe Brucker 		if (write)
287b0d56e3cSJean-Philippe Brucker 			handler = virtio_pci__config_write;
288b0d56e3cSJean-Philippe Brucker 		else
289b0d56e3cSJean-Philippe Brucker 			handler = virtio_pci__config_read;
290b0d56e3cSJean-Philippe Brucker 		break;
291b0d56e3cSJean-Philippe Brucker 	}
292b0d56e3cSJean-Philippe Brucker 
293b0d56e3cSJean-Philippe Brucker 	if (!handler)
294b0d56e3cSJean-Philippe Brucker 		return false;
295b0d56e3cSJean-Philippe Brucker 
296b0d56e3cSJean-Philippe Brucker 	return handler(vdev, offset, data, size);
297b0d56e3cSJean-Philippe Brucker }
298b0d56e3cSJean-Philippe Brucker 
virtio_pci_modern__io_mmio_callback(struct kvm_cpu * vcpu,u64 addr,u8 * data,u32 len,u8 is_write,void * ptr)299b0d56e3cSJean-Philippe Brucker void virtio_pci_modern__io_mmio_callback(struct kvm_cpu *vcpu, u64 addr,
300b0d56e3cSJean-Philippe Brucker 					 u8 *data, u32 len, u8 is_write,
301b0d56e3cSJean-Philippe Brucker 					 void *ptr)
302b0d56e3cSJean-Philippe Brucker {
303b0d56e3cSJean-Philippe Brucker 	struct virtio_device *vdev = ptr;
304b0d56e3cSJean-Philippe Brucker 	struct virtio_pci *vpci = vdev->virtio;
305b0d56e3cSJean-Philippe Brucker 	u32 mmio_addr = virtio_pci__mmio_addr(vpci);
306b0d56e3cSJean-Philippe Brucker 
307b0d56e3cSJean-Philippe Brucker 	virtio_pci_access(vcpu, vdev, addr - mmio_addr, data, len, is_write);
308b0d56e3cSJean-Philippe Brucker }
309b0d56e3cSJean-Philippe Brucker 
virtio_pci_modern_init(struct virtio_device * vdev)310b0d56e3cSJean-Philippe Brucker int virtio_pci_modern_init(struct virtio_device *vdev)
311b0d56e3cSJean-Philippe Brucker {
312b0d56e3cSJean-Philippe Brucker 	int subsys_id;
313b0d56e3cSJean-Philippe Brucker 	struct virtio_pci *vpci = vdev->virtio;
314b0d56e3cSJean-Philippe Brucker 	struct pci_device_header *hdr = &vpci->pci_hdr;
315b0d56e3cSJean-Philippe Brucker 
316b0d56e3cSJean-Philippe Brucker 	subsys_id = le16_to_cpu(hdr->subsys_id);
317b0d56e3cSJean-Philippe Brucker 
318b0d56e3cSJean-Philippe Brucker 	hdr->device_id = cpu_to_le16(PCI_DEVICE_ID_VIRTIO_BASE + subsys_id);
319b0d56e3cSJean-Philippe Brucker 	hdr->subsys_id = cpu_to_le16(PCI_SUBSYS_ID_VIRTIO_BASE + subsys_id);
320b0d56e3cSJean-Philippe Brucker 
321b0d56e3cSJean-Philippe Brucker 	vpci->doorbell_offset = VPCI_CFG_NOTIFY_START;
322b0d56e3cSJean-Philippe Brucker 	vdev->endian = VIRTIO_ENDIAN_LE;
323b0d56e3cSJean-Philippe Brucker 
324b0d56e3cSJean-Philippe Brucker 	hdr->msix.next = PCI_CAP_OFF(hdr, virtio);
325b0d56e3cSJean-Philippe Brucker 
326b0d56e3cSJean-Philippe Brucker 	hdr->virtio.common = (struct virtio_pci_cap) {
327b0d56e3cSJean-Philippe Brucker 		.cap_vndr		= PCI_CAP_ID_VNDR,
328b0d56e3cSJean-Philippe Brucker 		.cap_next		= PCI_CAP_OFF(hdr, virtio.notify),
329b0d56e3cSJean-Philippe Brucker 		.cap_len		= sizeof(hdr->virtio.common),
330b0d56e3cSJean-Philippe Brucker 		.cfg_type		= VIRTIO_PCI_CAP_COMMON_CFG,
331b0d56e3cSJean-Philippe Brucker 		.bar			= 1,
332b0d56e3cSJean-Philippe Brucker 		.offset			= cpu_to_le32(VPCI_CFG_COMMON_START),
333b0d56e3cSJean-Philippe Brucker 		.length			= cpu_to_le32(VPCI_CFG_COMMON_SIZE),
334b0d56e3cSJean-Philippe Brucker 	};
335b0d56e3cSJean-Philippe Brucker 	BUILD_BUG_ON(VPCI_CFG_COMMON_START & 0x3);
336b0d56e3cSJean-Philippe Brucker 
337b0d56e3cSJean-Philippe Brucker 	hdr->virtio.notify = (struct virtio_pci_notify_cap) {
338b0d56e3cSJean-Philippe Brucker 		.cap.cap_vndr		= PCI_CAP_ID_VNDR,
339b0d56e3cSJean-Philippe Brucker 		.cap.cap_next		= PCI_CAP_OFF(hdr, virtio.isr),
340b0d56e3cSJean-Philippe Brucker 		.cap.cap_len		= sizeof(hdr->virtio.notify),
341b0d56e3cSJean-Philippe Brucker 		.cap.cfg_type		= VIRTIO_PCI_CAP_NOTIFY_CFG,
342b0d56e3cSJean-Philippe Brucker 		.cap.bar		= 1,
343b0d56e3cSJean-Philippe Brucker 		.cap.offset		= cpu_to_le32(VPCI_CFG_NOTIFY_START),
344b0d56e3cSJean-Philippe Brucker 		.cap.length		= cpu_to_le32(VPCI_CFG_NOTIFY_SIZE),
345b0d56e3cSJean-Philippe Brucker 		/*
346b0d56e3cSJean-Philippe Brucker 		 * Notify multiplier is 0, meaning that notifications are all on
347b0d56e3cSJean-Philippe Brucker 		 * the same register
348b0d56e3cSJean-Philippe Brucker 		 */
349b0d56e3cSJean-Philippe Brucker 	};
350b0d56e3cSJean-Philippe Brucker 	BUILD_BUG_ON(VPCI_CFG_NOTIFY_START & 0x3);
351b0d56e3cSJean-Philippe Brucker 
352b0d56e3cSJean-Philippe Brucker 	hdr->virtio.isr = (struct virtio_pci_cap) {
353b0d56e3cSJean-Philippe Brucker 		.cap_vndr		= PCI_CAP_ID_VNDR,
354b0d56e3cSJean-Philippe Brucker 		.cap_next		= PCI_CAP_OFF(hdr, virtio.device),
355b0d56e3cSJean-Philippe Brucker 		.cap_len		= sizeof(hdr->virtio.isr),
356b0d56e3cSJean-Philippe Brucker 		.cfg_type		= VIRTIO_PCI_CAP_ISR_CFG,
357b0d56e3cSJean-Philippe Brucker 		.bar			= 1,
358b0d56e3cSJean-Philippe Brucker 		.offset			= cpu_to_le32(VPCI_CFG_ISR_START),
359b0d56e3cSJean-Philippe Brucker 		.length			= cpu_to_le32(VPCI_CFG_ISR_SIZE),
360b0d56e3cSJean-Philippe Brucker 	};
361b0d56e3cSJean-Philippe Brucker 
362b0d56e3cSJean-Philippe Brucker 	hdr->virtio.device = (struct virtio_pci_cap) {
363b0d56e3cSJean-Philippe Brucker 		.cap_vndr		= PCI_CAP_ID_VNDR,
364b0d56e3cSJean-Philippe Brucker 		.cap_next		= PCI_CAP_OFF(hdr, virtio.pci),
365b0d56e3cSJean-Philippe Brucker 		.cap_len		= sizeof(hdr->virtio.device),
366b0d56e3cSJean-Philippe Brucker 		.cfg_type		= VIRTIO_PCI_CAP_DEVICE_CFG,
367b0d56e3cSJean-Philippe Brucker 		.bar			= 1,
368b0d56e3cSJean-Philippe Brucker 		.offset			= cpu_to_le32(VPCI_CFG_DEV_START),
369b0d56e3cSJean-Philippe Brucker 		.length			= cpu_to_le32(VPCI_CFG_DEV_SIZE),
370b0d56e3cSJean-Philippe Brucker 	};
371b0d56e3cSJean-Philippe Brucker 	BUILD_BUG_ON(VPCI_CFG_DEV_START & 0x3);
372b0d56e3cSJean-Philippe Brucker 
373b0d56e3cSJean-Philippe Brucker 	/*
374b0d56e3cSJean-Philippe Brucker 	 * TODO: implement this weird proxy capability (it is a "MUST" in the
375b0d56e3cSJean-Philippe Brucker 	 * spec, but I don't know if anyone actually uses it).
376b0d56e3cSJean-Philippe Brucker 	 * It doesn't use any BAR space. Instead the driver writes .cap.offset
377b0d56e3cSJean-Philippe Brucker 	 * and .cap.length to access a register in a BAR.
378b0d56e3cSJean-Philippe Brucker 	 */
379b0d56e3cSJean-Philippe Brucker 	hdr->virtio.pci = (struct virtio_pci_cfg_cap) {
380b0d56e3cSJean-Philippe Brucker 		.cap.cap_vndr		= PCI_CAP_ID_VNDR,
381b0d56e3cSJean-Philippe Brucker 		.cap.cap_next		= 0,
382b0d56e3cSJean-Philippe Brucker 		.cap.cap_len		= sizeof(hdr->virtio.pci),
383b0d56e3cSJean-Philippe Brucker 		.cap.cfg_type		= VIRTIO_PCI_CAP_PCI_CFG,
384b0d56e3cSJean-Philippe Brucker 	};
385b0d56e3cSJean-Philippe Brucker 
386b0d56e3cSJean-Philippe Brucker 	return 0;
387b0d56e3cSJean-Philippe Brucker }
388