aboutsummaryrefslogtreecommitdiffstats
path: root/virtio/pci-modern.c
blob: c5b4bc5034978ae2eeb2dd0174a7178110605ddd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
#include "kvm/virtio-pci.h"

#include "kvm/ioport.h"
#include "kvm/virtio.h"
#include "kvm/virtio-pci-dev.h"

#include <linux/virtio_config.h>

#define VPCI_CFG_COMMON_SIZE	sizeof(struct virtio_pci_common_cfg)
#define VPCI_CFG_COMMON_START	0
#define VPCI_CFG_COMMON_END	(VPCI_CFG_COMMON_SIZE - 1)
/*
 * Use a naturally aligned 4-byte doorbell, in case we ever want to
 * implement VIRTIO_F_NOTIFICATION_DATA
 */
#define VPCI_CFG_NOTIFY_SIZE	4
#define VPCI_CFG_NOTIFY_START	(VPCI_CFG_COMMON_END + 1)
#define VPCI_CFG_NOTIFY_END	(VPCI_CFG_COMMON_END + VPCI_CFG_NOTIFY_SIZE)
#define VPCI_CFG_ISR_SIZE	4
#define VPCI_CFG_ISR_START	(VPCI_CFG_NOTIFY_END + 1)
#define VPCI_CFG_ISR_END	(VPCI_CFG_NOTIFY_END + VPCI_CFG_ISR_SIZE)
/*
 * We're at 64 bytes. Use the remaining 192 bytes in PCI_IO_SIZE for the
 * device-specific config space. It's sufficient for the devices we
 * currently implement (virtio_blk_config is 60 bytes) and, I think, all
 * existing virtio 1.2 devices.
 */
#define VPCI_CFG_DEV_START	(VPCI_CFG_ISR_END + 1)
#define VPCI_CFG_DEV_END	((PCI_IO_SIZE) - 1)
#define VPCI_CFG_DEV_SIZE	(VPCI_CFG_DEV_END - VPCI_CFG_DEV_START + 1)

#define vpci_selected_vq(vpci) \
	vdev->ops->get_vq((vpci)->kvm, (vpci)->dev, (vpci)->queue_selector)

typedef bool (*access_handler_t)(struct virtio_device *, unsigned long, void *, int);

static bool virtio_pci__common_write(struct virtio_device *vdev,
				     unsigned long offset, void *data, int size)
{
	u64 features;
	u32 val, gsi, vec;
	struct virtio_pci *vpci = vdev->virtio;

	switch (offset - VPCI_CFG_COMMON_START) {
	case VIRTIO_PCI_COMMON_DFSELECT:
		vpci->device_features_sel = ioport__read32(data);
		break;
	case VIRTIO_PCI_COMMON_GFSELECT:
		vpci->driver_features_sel = ioport__read32(data);
		break;
	case VIRTIO_PCI_COMMON_GF:
		val = ioport__read32(data);
		if (vpci->driver_features_sel > 1)
			break;

		features = (u64)val << (32 * vpci->driver_features_sel);
		virtio_set_guest_features(vpci->kvm, vdev, vpci->dev, features);
		break;
	case VIRTIO_PCI_COMMON_MSIX:
		vec = vpci->config_vector = ioport__read16(data);
		gsi = virtio_pci__add_msix_route(vpci, vec);
		if (gsi < 0)
			break;

		vpci->config_gsi = gsi;
		break;
	case VIRTIO_PCI_COMMON_STATUS:
		vpci->status = ioport__read8(data);
		virtio_notify_status(vpci->kvm, vdev, vpci->dev, vpci->status);
		break;
	case VIRTIO_PCI_COMMON_Q_SELECT:
		val = ioport__read16(data);
		if (val >= (u32)vdev->ops->get_vq_count(vpci->kvm, vpci->dev))
			pr_warning("invalid vq number %u", val);
		else
			vpci->queue_selector = val;
		break;
	case VIRTIO_PCI_COMMON_Q_SIZE:
		vdev->ops->set_size_vq(vpci->kvm, vpci->dev,
				       vpci->queue_selector,
				       ioport__read16(data));
		break;
	case VIRTIO_PCI_COMMON_Q_MSIX:
		vec = vpci->vq_vector[vpci->queue_selector] = ioport__read16(data);

		gsi = virtio_pci__add_msix_route(vpci, vec);
		if (gsi < 0)
			break;

		vpci->gsis[vpci->queue_selector] = gsi;
		if (vdev->ops->notify_vq_gsi)
			vdev->ops->notify_vq_gsi(vpci->kvm, vpci->dev,
						 vpci->queue_selector, gsi);
		break;
	case VIRTIO_PCI_COMMON_Q_ENABLE:
		val = ioport__read16(data);
		if (val)
			virtio_pci_init_vq(vpci->kvm, vdev, vpci->queue_selector);
		else
			virtio_pci_exit_vq(vpci->kvm, vdev, vpci->queue_selector);
		break;
	case VIRTIO_PCI_COMMON_Q_DESCLO:
		vpci_selected_vq(vpci)->vring_addr.desc_lo = ioport__read32(data);
		break;
	case VIRTIO_PCI_COMMON_Q_DESCHI:
		vpci_selected_vq(vpci)->vring_addr.desc_hi = ioport__read32(data);
		break;
	case VIRTIO_PCI_COMMON_Q_AVAILLO:
		vpci_selected_vq(vpci)->vring_addr.avail_lo = ioport__read32(data);
		break;
	case VIRTIO_PCI_COMMON_Q_AVAILHI:
		vpci_selected_vq(vpci)->vring_addr.avail_hi = ioport__read32(data);
		break;
	case VIRTIO_PCI_COMMON_Q_USEDLO:
		vpci_selected_vq(vpci)->vring_addr.used_lo = ioport__read32(data);
		break;
	case VIRTIO_PCI_COMMON_Q_USEDHI:
		vpci_selected_vq(vpci)->vring_addr.used_hi = ioport__read32(data);
		break;
	}

	return true;
}

static bool virtio_pci__notify_write(struct virtio_device *vdev,
				     unsigned long offset, void *data, int size)
{
	u16 vq = ioport__read16(data);
	struct virtio_pci *vpci = vdev->virtio;

	vdev->ops->notify_vq(vpci->kvm, vpci->dev, vq);

	return true;
}

static bool virtio_pci__config_write(struct virtio_device *vdev,
				     unsigned long offset, void *data, int size)
{
	struct virtio_pci *vpci = vdev->virtio;

	return virtio_access_config(vpci->kvm, vdev, vpci->dev,
				    offset - VPCI_CFG_DEV_START, data, size,
				    true);
}

static bool virtio_pci__common_read(struct virtio_device *vdev,
				    unsigned long offset, void *data, int size)
{
	u32 val;
	struct virtio_pci *vpci = vdev->virtio;
	u64 features = 1ULL << VIRTIO_F_VERSION_1;

	switch (offset - VPCI_CFG_COMMON_START) {
	case VIRTIO_PCI_COMMON_DFSELECT:
		val = vpci->device_features_sel;
		ioport__write32(data, val);
		break;
	case VIRTIO_PCI_COMMON_DF:
		if (vpci->device_features_sel > 1)
			break;
		features |= vdev->ops->get_host_features(vpci->kvm, vpci->dev);
		val = features >> (32 * vpci->device_features_sel);
		ioport__write32(data, val);
		break;
	case VIRTIO_PCI_COMMON_GFSELECT:
		val = vpci->driver_features_sel;
		ioport__write32(data, val);
		break;
	case VIRTIO_PCI_COMMON_MSIX:
		val = vpci->config_vector;
		ioport__write32(data, val);
		break;
	case VIRTIO_PCI_COMMON_NUMQ:
		val = vdev->ops->get_vq_count(vpci->kvm, vpci->dev);
		ioport__write32(data, val);
		break;
	case VIRTIO_PCI_COMMON_STATUS:
		ioport__write8(data, vpci->status);
		break;
	case VIRTIO_PCI_COMMON_CFGGENERATION:
		/*
		 * The config generation changes when the device updates a
		 * config field larger than 32 bits, that the driver may read
		 * using multiple accesses. Since kvmtool doesn't use any
		 * mutable config field larger than 32 bits, the generation is
		 * constant.
		 */
		ioport__write8(data, 0);
		break;
	case VIRTIO_PCI_COMMON_Q_SELECT:
		ioport__write16(data, vpci->queue_selector);
		break;
	case VIRTIO_PCI_COMMON_Q_SIZE:
		val = vdev->ops->get_size_vq(vpci->kvm, vpci->dev,
					     vpci->queue_selector);
		ioport__write16(data, val);
		break;
	case VIRTIO_PCI_COMMON_Q_MSIX:
		val = vpci->vq_vector[vpci->queue_selector];
		ioport__write16(data, val);
		break;
	case VIRTIO_PCI_COMMON_Q_ENABLE:
		val = vpci_selected_vq(vpci)->enabled;
		ioport__write16(data, val);
		break;
	case VIRTIO_PCI_COMMON_Q_NOFF:
		val = vpci->queue_selector;
		ioport__write16(data, val);
		break;
	case VIRTIO_PCI_COMMON_Q_DESCLO:
		val = vpci_selected_vq(vpci)->vring_addr.desc_lo;
		ioport__write32(data, val);
		break;
	case VIRTIO_PCI_COMMON_Q_DESCHI:
		val = vpci_selected_vq(vpci)->vring_addr.desc_hi;
		ioport__write32(data, val);
		break;
	case VIRTIO_PCI_COMMON_Q_AVAILLO:
		val = vpci_selected_vq(vpci)->vring_addr.avail_lo;
		ioport__write32(data, val);
		break;
	case VIRTIO_PCI_COMMON_Q_AVAILHI:
		val = vpci_selected_vq(vpci)->vring_addr.avail_hi;
		ioport__write32(data, val);
		break;
	case VIRTIO_PCI_COMMON_Q_USEDLO:
		val = vpci_selected_vq(vpci)->vring_addr.used_lo;
		ioport__write32(data, val);
		break;
	case VIRTIO_PCI_COMMON_Q_USEDHI:
		val = vpci_selected_vq(vpci)->vring_addr.used_hi;
		ioport__write32(data, val);
		break;
	};

	return true;
}

static bool virtio_pci__isr_read(struct virtio_device *vdev,
				 unsigned long offset, void *data, int size)
{
	struct virtio_pci *vpci = vdev->virtio;

	if (WARN_ON(offset - VPCI_CFG_ISR_START != 0))
		return false;

	ioport__write8(data, vpci->isr);
	kvm__irq_line(vpci->kvm, vpci->legacy_irq_line, VIRTIO_IRQ_LOW);
	vpci->isr = 0;

	return 0;
}

static bool virtio_pci__config_read(struct virtio_device *vdev,
				    unsigned long offset, void *data, int size)
{
	struct virtio_pci *vpci = vdev->virtio;

	return virtio_access_config(vpci->kvm, vdev, vpci->dev,
				    offset - VPCI_CFG_DEV_START, data, size,
				    false);
}

static bool virtio_pci_access(struct kvm_cpu *vcpu, struct virtio_device *vdev,
			      unsigned long offset, void *data, int size,
			      bool write)
{
	access_handler_t handler = NULL;

	switch (offset) {
	case VPCI_CFG_COMMON_START...VPCI_CFG_COMMON_END:
		if (write)
			handler = virtio_pci__common_write;
		else
			handler = virtio_pci__common_read;
		break;
	case VPCI_CFG_NOTIFY_START...VPCI_CFG_NOTIFY_END:
		if (write)
			handler = virtio_pci__notify_write;
		break;
	case VPCI_CFG_ISR_START...VPCI_CFG_ISR_END:
		if (!write)
			handler = virtio_pci__isr_read;
		break;
	case VPCI_CFG_DEV_START...VPCI_CFG_DEV_END:
		if (write)
			handler = virtio_pci__config_write;
		else
			handler = virtio_pci__config_read;
		break;
	}

	if (!handler)
		return false;

	return handler(vdev, offset, data, size);
}

void virtio_pci_modern__io_mmio_callback(struct kvm_cpu *vcpu, u64 addr,
					 u8 *data, u32 len, u8 is_write,
					 void *ptr)
{
	struct virtio_device *vdev = ptr;
	struct virtio_pci *vpci = vdev->virtio;
	u32 mmio_addr = virtio_pci__mmio_addr(vpci);

	virtio_pci_access(vcpu, vdev, addr - mmio_addr, data, len, is_write);
}

int virtio_pci_modern_init(struct virtio_device *vdev)
{
	int subsys_id;
	struct virtio_pci *vpci = vdev->virtio;
	struct pci_device_header *hdr = &vpci->pci_hdr;

	subsys_id = le16_to_cpu(hdr->subsys_id);

	hdr->device_id = cpu_to_le16(PCI_DEVICE_ID_VIRTIO_BASE + subsys_id);
	hdr->subsys_id = cpu_to_le16(PCI_SUBSYS_ID_VIRTIO_BASE + subsys_id);

	vpci->doorbell_offset = VPCI_CFG_NOTIFY_START;
	vdev->endian = VIRTIO_ENDIAN_LE;

	hdr->msix.next = PCI_CAP_OFF(hdr, virtio);

	hdr->virtio.common = (struct virtio_pci_cap) {
		.cap_vndr		= PCI_CAP_ID_VNDR,
		.cap_next		= PCI_CAP_OFF(hdr, virtio.notify),
		.cap_len		= sizeof(hdr->virtio.common),
		.cfg_type		= VIRTIO_PCI_CAP_COMMON_CFG,
		.bar			= 1,
		.offset			= cpu_to_le32(VPCI_CFG_COMMON_START),
		.length			= cpu_to_le32(VPCI_CFG_COMMON_SIZE),
	};
	BUILD_BUG_ON(VPCI_CFG_COMMON_START & 0x3);

	hdr->virtio.notify = (struct virtio_pci_notify_cap) {
		.cap.cap_vndr		= PCI_CAP_ID_VNDR,
		.cap.cap_next		= PCI_CAP_OFF(hdr, virtio.isr),
		.cap.cap_len		= sizeof(hdr->virtio.notify),
		.cap.cfg_type		= VIRTIO_PCI_CAP_NOTIFY_CFG,
		.cap.bar		= 1,
		.cap.offset		= cpu_to_le32(VPCI_CFG_NOTIFY_START),
		.cap.length		= cpu_to_le32(VPCI_CFG_NOTIFY_SIZE),
		/*
		 * Notify multiplier is 0, meaning that notifications are all on
		 * the same register
		 */
	};
	BUILD_BUG_ON(VPCI_CFG_NOTIFY_START & 0x3);

	hdr->virtio.isr = (struct virtio_pci_cap) {
		.cap_vndr		= PCI_CAP_ID_VNDR,
		.cap_next		= PCI_CAP_OFF(hdr, virtio.device),
		.cap_len		= sizeof(hdr->virtio.isr),
		.cfg_type		= VIRTIO_PCI_CAP_ISR_CFG,
		.bar			= 1,
		.offset			= cpu_to_le32(VPCI_CFG_ISR_START),
		.length			= cpu_to_le32(VPCI_CFG_ISR_SIZE),
	};

	hdr->virtio.device = (struct virtio_pci_cap) {
		.cap_vndr		= PCI_CAP_ID_VNDR,
		.cap_next		= PCI_CAP_OFF(hdr, virtio.pci),
		.cap_len		= sizeof(hdr->virtio.device),
		.cfg_type		= VIRTIO_PCI_CAP_DEVICE_CFG,
		.bar			= 1,
		.offset			= cpu_to_le32(VPCI_CFG_DEV_START),
		.length			= cpu_to_le32(VPCI_CFG_DEV_SIZE),
	};
	BUILD_BUG_ON(VPCI_CFG_DEV_START & 0x3);

	/*
	 * TODO: implement this weird proxy capability (it is a "MUST" in the
	 * spec, but I don't know if anyone actually uses it).
	 * It doesn't use any BAR space. Instead the driver writes .cap.offset
	 * and .cap.length to access a register in a BAR.
	 */
	hdr->virtio.pci = (struct virtio_pci_cfg_cap) {
		.cap.cap_vndr		= PCI_CAP_ID_VNDR,
		.cap.cap_next		= 0,
		.cap.cap_len		= sizeof(hdr->virtio.pci),
		.cap.cfg_type		= VIRTIO_PCI_CAP_PCI_CFG,
	};

	return 0;
}