]>
Commit | Line | Data |
---|---|---|
4b52530b PM |
1 | /* |
2 | * Virtio MMIO bindings | |
3 | * | |
4 | * Copyright (c) 2011 Linaro Limited | |
5 | * | |
6 | * Author: | |
7 | * Peter Maydell <peter.maydell@linaro.org> | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License; either version 2 | |
11 | * of the License, or (at your option) any later version. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License along | |
19 | * with this program; if not, see <http://www.gnu.org/licenses/>. | |
20 | */ | |
21 | ||
9b8bfe21 | 22 | #include "qemu/osdep.h" |
7e71da7f | 23 | #include "standard-headers/linux/virtio_mmio.h" |
64552b6b | 24 | #include "hw/irq.h" |
a27bd6c7 | 25 | #include "hw/qdev-properties.h" |
4b52530b PM |
26 | #include "hw/sysbus.h" |
27 | #include "hw/virtio/virtio.h" | |
ca77ee28 | 28 | #include "migration/qemu-file-types.h" |
4b52530b | 29 | #include "qemu/host-utils.h" |
0b8fa32f | 30 | #include "qemu/module.h" |
434027ba | 31 | #include "sysemu/kvm.h" |
3909c079 | 32 | #include "sysemu/replay.h" |
bca964bc | 33 | #include "hw/virtio/virtio-mmio.h" |
434027ba | 34 | #include "qemu/error-report.h" |
da1804d1 BL |
35 | #include "qemu/log.h" |
36 | #include "trace.h" | |
4b52530b | 37 | |
8e93cef1 | 38 | static bool virtio_mmio_ioeventfd_enabled(DeviceState *d) |
c0971bcb | 39 | { |
b8893a3c PD |
40 | VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d); |
41 | ||
42 | return (proxy->flags & VIRTIO_IOMMIO_FLAG_USE_IOEVENTFD) != 0; | |
434027ba YSP |
43 | } |
44 | ||
c0971bcb CH |
45 | static int virtio_mmio_ioeventfd_assign(DeviceState *d, |
46 | EventNotifier *notifier, | |
47 | int n, bool assign) | |
434027ba | 48 | { |
c0971bcb | 49 | VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d); |
434027ba | 50 | |
c0971bcb | 51 | if (assign) { |
7e71da7f | 52 | memory_region_add_eventfd(&proxy->iomem, VIRTIO_MMIO_QUEUE_NOTIFY, 4, |
c0971bcb CH |
53 | true, n, notifier); |
54 | } else { | |
7e71da7f | 55 | memory_region_del_eventfd(&proxy->iomem, VIRTIO_MMIO_QUEUE_NOTIFY, 4, |
c0971bcb | 56 | true, n, notifier); |
434027ba | 57 | } |
c0971bcb CH |
58 | return 0; |
59 | } | |
434027ba | 60 | |
c0971bcb CH |
61 | static void virtio_mmio_start_ioeventfd(VirtIOMMIOProxy *proxy) |
62 | { | |
63 | virtio_bus_start_ioeventfd(&proxy->bus); | |
64 | } | |
434027ba | 65 | |
c0971bcb CH |
66 | static void virtio_mmio_stop_ioeventfd(VirtIOMMIOProxy *proxy) |
67 | { | |
68 | virtio_bus_stop_ioeventfd(&proxy->bus); | |
434027ba YSP |
69 | } |
70 | ||
351da832 JPB |
71 | static void virtio_mmio_soft_reset(VirtIOMMIOProxy *proxy) |
72 | { | |
73 | int i; | |
74 | ||
75 | if (proxy->legacy) { | |
76 | return; | |
77 | } | |
78 | ||
79 | for (i = 0; i < VIRTIO_QUEUE_MAX; i++) { | |
80 | proxy->vqs[i].enabled = 0; | |
81 | } | |
82 | } | |
83 | ||
4b52530b PM |
84 | static uint64_t virtio_mmio_read(void *opaque, hwaddr offset, unsigned size) |
85 | { | |
86 | VirtIOMMIOProxy *proxy = (VirtIOMMIOProxy *)opaque; | |
06d3dff0 | 87 | VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); |
4b52530b | 88 | |
da1804d1 | 89 | trace_virtio_mmio_read(offset); |
4b52530b PM |
90 | |
91 | if (!vdev) { | |
92 | /* If no backend is present, we treat most registers as | |
93 | * read-as-zero, except for the magic number, version and | |
94 | * vendor ID. This is not strictly sanctioned by the virtio | |
95 | * spec, but it allows us to provide transports with no backend | |
96 | * plugged in which don't confuse Linux's virtio code: the | |
97 | * probe won't complain about the bad magic number, but the | |
98 | * device ID of zero means no backend will claim it. | |
99 | */ | |
100 | switch (offset) { | |
7e71da7f | 101 | case VIRTIO_MMIO_MAGIC_VALUE: |
4b52530b PM |
102 | return VIRT_MAGIC; |
103 | case VIRTIO_MMIO_VERSION: | |
44e687a4 SL |
104 | if (proxy->legacy) { |
105 | return VIRT_VERSION_LEGACY; | |
106 | } else { | |
107 | return VIRT_VERSION; | |
108 | } | |
7e71da7f | 109 | case VIRTIO_MMIO_VENDOR_ID: |
4b52530b PM |
110 | return VIRT_VENDOR; |
111 | default: | |
112 | return 0; | |
113 | } | |
114 | } | |
115 | ||
116 | if (offset >= VIRTIO_MMIO_CONFIG) { | |
117 | offset -= VIRTIO_MMIO_CONFIG; | |
0ab8c021 LV |
118 | if (proxy->legacy) { |
119 | switch (size) { | |
120 | case 1: | |
121 | return virtio_config_readb(vdev, offset); | |
122 | case 2: | |
123 | return virtio_config_readw(vdev, offset); | |
124 | case 4: | |
125 | return virtio_config_readl(vdev, offset); | |
126 | default: | |
127 | abort(); | |
128 | } | |
129 | } else { | |
130 | switch (size) { | |
131 | case 1: | |
132 | return virtio_config_modern_readb(vdev, offset); | |
133 | case 2: | |
134 | return virtio_config_modern_readw(vdev, offset); | |
135 | case 4: | |
136 | return virtio_config_modern_readl(vdev, offset); | |
137 | default: | |
138 | abort(); | |
139 | } | |
4b52530b PM |
140 | } |
141 | } | |
142 | if (size != 4) { | |
da1804d1 BL |
143 | qemu_log_mask(LOG_GUEST_ERROR, |
144 | "%s: wrong size access to register!\n", | |
145 | __func__); | |
4b52530b PM |
146 | return 0; |
147 | } | |
148 | switch (offset) { | |
7e71da7f | 149 | case VIRTIO_MMIO_MAGIC_VALUE: |
4b52530b PM |
150 | return VIRT_MAGIC; |
151 | case VIRTIO_MMIO_VERSION: | |
44e687a4 SL |
152 | if (proxy->legacy) { |
153 | return VIRT_VERSION_LEGACY; | |
154 | } else { | |
155 | return VIRT_VERSION; | |
156 | } | |
7e71da7f | 157 | case VIRTIO_MMIO_DEVICE_ID: |
4b52530b | 158 | return vdev->device_id; |
7e71da7f | 159 | case VIRTIO_MMIO_VENDOR_ID: |
4b52530b | 160 | return VIRT_VENDOR; |
7e71da7f | 161 | case VIRTIO_MMIO_DEVICE_FEATURES: |
44e687a4 SL |
162 | if (proxy->legacy) { |
163 | if (proxy->host_features_sel) { | |
164 | return 0; | |
165 | } else { | |
166 | return vdev->host_features; | |
167 | } | |
168 | } else { | |
169 | VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev); | |
170 | return (vdev->host_features & ~vdc->legacy_features) | |
171 | >> (32 * proxy->host_features_sel); | |
4b52530b | 172 | } |
7e71da7f | 173 | case VIRTIO_MMIO_QUEUE_NUM_MAX: |
f7b803b3 PM |
174 | if (!virtio_queue_get_num(vdev, vdev->queue_sel)) { |
175 | return 0; | |
176 | } | |
4b52530b | 177 | return VIRTQUEUE_MAX_SIZE; |
7e71da7f | 178 | case VIRTIO_MMIO_QUEUE_PFN: |
44e687a4 SL |
179 | if (!proxy->legacy) { |
180 | qemu_log_mask(LOG_GUEST_ERROR, | |
181 | "%s: read from legacy register (0x%" | |
182 | HWADDR_PRIx ") in non-legacy mode\n", | |
183 | __func__, offset); | |
184 | return 0; | |
185 | } | |
4b52530b PM |
186 | return virtio_queue_get_addr(vdev, vdev->queue_sel) |
187 | >> proxy->guest_page_shift; | |
44e687a4 SL |
188 | case VIRTIO_MMIO_QUEUE_READY: |
189 | if (proxy->legacy) { | |
190 | qemu_log_mask(LOG_GUEST_ERROR, | |
191 | "%s: read from non-legacy register (0x%" | |
192 | HWADDR_PRIx ") in legacy mode\n", | |
193 | __func__, offset); | |
194 | return 0; | |
195 | } | |
196 | return proxy->vqs[vdev->queue_sel].enabled; | |
7e71da7f | 197 | case VIRTIO_MMIO_INTERRUPT_STATUS: |
d73415a3 | 198 | return qatomic_read(&vdev->isr); |
4b52530b PM |
199 | case VIRTIO_MMIO_STATUS: |
200 | return vdev->status; | |
44e687a4 SL |
201 | case VIRTIO_MMIO_CONFIG_GENERATION: |
202 | if (proxy->legacy) { | |
203 | qemu_log_mask(LOG_GUEST_ERROR, | |
204 | "%s: read from non-legacy register (0x%" | |
205 | HWADDR_PRIx ") in legacy mode\n", | |
206 | __func__, offset); | |
207 | return 0; | |
208 | } | |
209 | return vdev->generation; | |
2d9e7d4c LV |
210 | case VIRTIO_MMIO_SHM_LEN_LOW: |
211 | case VIRTIO_MMIO_SHM_LEN_HIGH: | |
212 | /* | |
213 | * VIRTIO_MMIO_SHM_SEL is unimplemented | |
214 | * according to the linux driver, if region length is -1 | |
215 | * the shared memory doesn't exist | |
216 | */ | |
217 | return -1; | |
7e71da7f MT |
218 | case VIRTIO_MMIO_DEVICE_FEATURES_SEL: |
219 | case VIRTIO_MMIO_DRIVER_FEATURES: | |
220 | case VIRTIO_MMIO_DRIVER_FEATURES_SEL: | |
221 | case VIRTIO_MMIO_GUEST_PAGE_SIZE: | |
222 | case VIRTIO_MMIO_QUEUE_SEL: | |
223 | case VIRTIO_MMIO_QUEUE_NUM: | |
224 | case VIRTIO_MMIO_QUEUE_ALIGN: | |
225 | case VIRTIO_MMIO_QUEUE_NOTIFY: | |
226 | case VIRTIO_MMIO_INTERRUPT_ACK: | |
44e687a4 SL |
227 | case VIRTIO_MMIO_QUEUE_DESC_LOW: |
228 | case VIRTIO_MMIO_QUEUE_DESC_HIGH: | |
229 | case VIRTIO_MMIO_QUEUE_AVAIL_LOW: | |
230 | case VIRTIO_MMIO_QUEUE_AVAIL_HIGH: | |
231 | case VIRTIO_MMIO_QUEUE_USED_LOW: | |
232 | case VIRTIO_MMIO_QUEUE_USED_HIGH: | |
da1804d1 | 233 | qemu_log_mask(LOG_GUEST_ERROR, |
44e687a4 SL |
234 | "%s: read of write-only register (0x%" HWADDR_PRIx ")\n", |
235 | __func__, offset); | |
4b52530b PM |
236 | return 0; |
237 | default: | |
44e687a4 SL |
238 | qemu_log_mask(LOG_GUEST_ERROR, |
239 | "%s: bad register offset (0x%" HWADDR_PRIx ")\n", | |
240 | __func__, offset); | |
4b52530b PM |
241 | return 0; |
242 | } | |
243 | return 0; | |
244 | } | |
245 | ||
246 | static void virtio_mmio_write(void *opaque, hwaddr offset, uint64_t value, | |
247 | unsigned size) | |
248 | { | |
249 | VirtIOMMIOProxy *proxy = (VirtIOMMIOProxy *)opaque; | |
06d3dff0 | 250 | VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); |
4b52530b | 251 | |
da1804d1 | 252 | trace_virtio_mmio_write_offset(offset, value); |
4b52530b PM |
253 | |
254 | if (!vdev) { | |
255 | /* If no backend is present, we just make all registers | |
256 | * write-ignored. This allows us to provide transports with | |
257 | * no backend plugged in. | |
258 | */ | |
259 | return; | |
260 | } | |
261 | ||
262 | if (offset >= VIRTIO_MMIO_CONFIG) { | |
263 | offset -= VIRTIO_MMIO_CONFIG; | |
0ab8c021 LV |
264 | if (proxy->legacy) { |
265 | switch (size) { | |
266 | case 1: | |
267 | virtio_config_writeb(vdev, offset, value); | |
268 | break; | |
269 | case 2: | |
270 | virtio_config_writew(vdev, offset, value); | |
271 | break; | |
272 | case 4: | |
273 | virtio_config_writel(vdev, offset, value); | |
274 | break; | |
275 | default: | |
276 | abort(); | |
277 | } | |
278 | return; | |
279 | } else { | |
280 | switch (size) { | |
281 | case 1: | |
282 | virtio_config_modern_writeb(vdev, offset, value); | |
283 | break; | |
284 | case 2: | |
285 | virtio_config_modern_writew(vdev, offset, value); | |
286 | break; | |
287 | case 4: | |
288 | virtio_config_modern_writel(vdev, offset, value); | |
289 | break; | |
290 | default: | |
291 | abort(); | |
292 | } | |
293 | return; | |
4b52530b | 294 | } |
4b52530b PM |
295 | } |
296 | if (size != 4) { | |
da1804d1 BL |
297 | qemu_log_mask(LOG_GUEST_ERROR, |
298 | "%s: wrong size access to register!\n", | |
299 | __func__); | |
4b52530b PM |
300 | return; |
301 | } | |
302 | switch (offset) { | |
7e71da7f | 303 | case VIRTIO_MMIO_DEVICE_FEATURES_SEL: |
44e687a4 SL |
304 | if (value) { |
305 | proxy->host_features_sel = 1; | |
306 | } else { | |
307 | proxy->host_features_sel = 0; | |
308 | } | |
4b52530b | 309 | break; |
7e71da7f | 310 | case VIRTIO_MMIO_DRIVER_FEATURES: |
44e687a4 SL |
311 | if (proxy->legacy) { |
312 | if (proxy->guest_features_sel) { | |
313 | qemu_log_mask(LOG_GUEST_ERROR, | |
314 | "%s: attempt to write guest features with " | |
315 | "guest_features_sel > 0 in legacy mode\n", | |
316 | __func__); | |
317 | } else { | |
318 | virtio_set_features(vdev, value); | |
319 | } | |
320 | } else { | |
321 | proxy->guest_features[proxy->guest_features_sel] = value; | |
4b52530b PM |
322 | } |
323 | break; | |
7e71da7f | 324 | case VIRTIO_MMIO_DRIVER_FEATURES_SEL: |
44e687a4 SL |
325 | if (value) { |
326 | proxy->guest_features_sel = 1; | |
327 | } else { | |
328 | proxy->guest_features_sel = 0; | |
329 | } | |
4b52530b | 330 | break; |
7e71da7f | 331 | case VIRTIO_MMIO_GUEST_PAGE_SIZE: |
44e687a4 SL |
332 | if (!proxy->legacy) { |
333 | qemu_log_mask(LOG_GUEST_ERROR, | |
334 | "%s: write to legacy register (0x%" | |
335 | HWADDR_PRIx ") in non-legacy mode\n", | |
336 | __func__, offset); | |
337 | return; | |
338 | } | |
4b52530b PM |
339 | proxy->guest_page_shift = ctz32(value); |
340 | if (proxy->guest_page_shift > 31) { | |
341 | proxy->guest_page_shift = 0; | |
342 | } | |
da1804d1 | 343 | trace_virtio_mmio_guest_page(value, proxy->guest_page_shift); |
4b52530b | 344 | break; |
7e71da7f | 345 | case VIRTIO_MMIO_QUEUE_SEL: |
87b3bd1c | 346 | if (value < VIRTIO_QUEUE_MAX) { |
4b52530b PM |
347 | vdev->queue_sel = value; |
348 | } | |
349 | break; | |
7e71da7f | 350 | case VIRTIO_MMIO_QUEUE_NUM: |
da1804d1 | 351 | trace_virtio_mmio_queue_write(value, VIRTQUEUE_MAX_SIZE); |
1049f4c6 DP |
352 | virtio_queue_set_num(vdev, vdev->queue_sel, value); |
353 | ||
44e687a4 | 354 | if (proxy->legacy) { |
44e687a4 SL |
355 | virtio_queue_update_rings(vdev, vdev->queue_sel); |
356 | } else { | |
357 | proxy->vqs[vdev->queue_sel].num = value; | |
358 | } | |
4b52530b | 359 | break; |
7e71da7f | 360 | case VIRTIO_MMIO_QUEUE_ALIGN: |
44e687a4 SL |
361 | if (!proxy->legacy) { |
362 | qemu_log_mask(LOG_GUEST_ERROR, | |
363 | "%s: write to legacy register (0x%" | |
364 | HWADDR_PRIx ") in non-legacy mode\n", | |
365 | __func__, offset); | |
366 | return; | |
367 | } | |
4b52530b PM |
368 | virtio_queue_set_align(vdev, vdev->queue_sel, value); |
369 | break; | |
7e71da7f | 370 | case VIRTIO_MMIO_QUEUE_PFN: |
44e687a4 SL |
371 | if (!proxy->legacy) { |
372 | qemu_log_mask(LOG_GUEST_ERROR, | |
373 | "%s: write to legacy register (0x%" | |
374 | HWADDR_PRIx ") in non-legacy mode\n", | |
375 | __func__, offset); | |
376 | return; | |
377 | } | |
4b52530b | 378 | if (value == 0) { |
9e43a830 | 379 | virtio_bus_reset(&vdev->bus); |
4b52530b PM |
380 | } else { |
381 | virtio_queue_set_addr(vdev, vdev->queue_sel, | |
382 | value << proxy->guest_page_shift); | |
383 | } | |
384 | break; | |
44e687a4 SL |
385 | case VIRTIO_MMIO_QUEUE_READY: |
386 | if (proxy->legacy) { | |
387 | qemu_log_mask(LOG_GUEST_ERROR, | |
388 | "%s: write to non-legacy register (0x%" | |
389 | HWADDR_PRIx ") in legacy mode\n", | |
390 | __func__, offset); | |
391 | return; | |
392 | } | |
393 | if (value) { | |
394 | virtio_queue_set_num(vdev, vdev->queue_sel, | |
395 | proxy->vqs[vdev->queue_sel].num); | |
396 | virtio_queue_set_rings(vdev, vdev->queue_sel, | |
397 | ((uint64_t)proxy->vqs[vdev->queue_sel].desc[1]) << 32 | | |
398 | proxy->vqs[vdev->queue_sel].desc[0], | |
399 | ((uint64_t)proxy->vqs[vdev->queue_sel].avail[1]) << 32 | | |
400 | proxy->vqs[vdev->queue_sel].avail[0], | |
401 | ((uint64_t)proxy->vqs[vdev->queue_sel].used[1]) << 32 | | |
402 | proxy->vqs[vdev->queue_sel].used[0]); | |
403 | proxy->vqs[vdev->queue_sel].enabled = 1; | |
404 | } else { | |
405 | proxy->vqs[vdev->queue_sel].enabled = 0; | |
406 | } | |
407 | break; | |
7e71da7f | 408 | case VIRTIO_MMIO_QUEUE_NOTIFY: |
87b3bd1c | 409 | if (value < VIRTIO_QUEUE_MAX) { |
4b52530b PM |
410 | virtio_queue_notify(vdev, value); |
411 | } | |
412 | break; | |
7e71da7f | 413 | case VIRTIO_MMIO_INTERRUPT_ACK: |
d73415a3 | 414 | qatomic_and(&vdev->isr, ~value); |
4b52530b PM |
415 | virtio_update_irq(vdev); |
416 | break; | |
417 | case VIRTIO_MMIO_STATUS: | |
434027ba YSP |
418 | if (!(value & VIRTIO_CONFIG_S_DRIVER_OK)) { |
419 | virtio_mmio_stop_ioeventfd(proxy); | |
420 | } | |
421 | ||
44e687a4 SL |
422 | if (!proxy->legacy && (value & VIRTIO_CONFIG_S_FEATURES_OK)) { |
423 | virtio_set_features(vdev, | |
424 | ((uint64_t)proxy->guest_features[1]) << 32 | | |
425 | proxy->guest_features[0]); | |
426 | } | |
427 | ||
4b52530b | 428 | virtio_set_status(vdev, value & 0xff); |
434027ba YSP |
429 | |
430 | if (value & VIRTIO_CONFIG_S_DRIVER_OK) { | |
431 | virtio_mmio_start_ioeventfd(proxy); | |
432 | } | |
433 | ||
4b52530b PM |
434 | if (vdev->status == 0) { |
435 | virtio_reset(vdev); | |
351da832 | 436 | virtio_mmio_soft_reset(proxy); |
4b52530b PM |
437 | } |
438 | break; | |
44e687a4 SL |
439 | case VIRTIO_MMIO_QUEUE_DESC_LOW: |
440 | if (proxy->legacy) { | |
441 | qemu_log_mask(LOG_GUEST_ERROR, | |
442 | "%s: write to non-legacy register (0x%" | |
443 | HWADDR_PRIx ") in legacy mode\n", | |
444 | __func__, offset); | |
445 | return; | |
446 | } | |
447 | proxy->vqs[vdev->queue_sel].desc[0] = value; | |
448 | break; | |
449 | case VIRTIO_MMIO_QUEUE_DESC_HIGH: | |
450 | if (proxy->legacy) { | |
451 | qemu_log_mask(LOG_GUEST_ERROR, | |
452 | "%s: write to non-legacy register (0x%" | |
453 | HWADDR_PRIx ") in legacy mode\n", | |
454 | __func__, offset); | |
455 | return; | |
456 | } | |
457 | proxy->vqs[vdev->queue_sel].desc[1] = value; | |
458 | break; | |
459 | case VIRTIO_MMIO_QUEUE_AVAIL_LOW: | |
460 | if (proxy->legacy) { | |
461 | qemu_log_mask(LOG_GUEST_ERROR, | |
462 | "%s: write to non-legacy register (0x%" | |
463 | HWADDR_PRIx ") in legacy mode\n", | |
464 | __func__, offset); | |
465 | return; | |
466 | } | |
467 | proxy->vqs[vdev->queue_sel].avail[0] = value; | |
468 | break; | |
469 | case VIRTIO_MMIO_QUEUE_AVAIL_HIGH: | |
470 | if (proxy->legacy) { | |
471 | qemu_log_mask(LOG_GUEST_ERROR, | |
472 | "%s: write to non-legacy register (0x%" | |
473 | HWADDR_PRIx ") in legacy mode\n", | |
474 | __func__, offset); | |
475 | return; | |
476 | } | |
477 | proxy->vqs[vdev->queue_sel].avail[1] = value; | |
478 | break; | |
479 | case VIRTIO_MMIO_QUEUE_USED_LOW: | |
480 | if (proxy->legacy) { | |
481 | qemu_log_mask(LOG_GUEST_ERROR, | |
482 | "%s: write to non-legacy register (0x%" | |
483 | HWADDR_PRIx ") in legacy mode\n", | |
484 | __func__, offset); | |
485 | return; | |
486 | } | |
487 | proxy->vqs[vdev->queue_sel].used[0] = value; | |
488 | break; | |
489 | case VIRTIO_MMIO_QUEUE_USED_HIGH: | |
490 | if (proxy->legacy) { | |
491 | qemu_log_mask(LOG_GUEST_ERROR, | |
492 | "%s: write to non-legacy register (0x%" | |
493 | HWADDR_PRIx ") in legacy mode\n", | |
494 | __func__, offset); | |
495 | return; | |
496 | } | |
497 | proxy->vqs[vdev->queue_sel].used[1] = value; | |
498 | break; | |
7e71da7f | 499 | case VIRTIO_MMIO_MAGIC_VALUE: |
4b52530b | 500 | case VIRTIO_MMIO_VERSION: |
7e71da7f MT |
501 | case VIRTIO_MMIO_DEVICE_ID: |
502 | case VIRTIO_MMIO_VENDOR_ID: | |
503 | case VIRTIO_MMIO_DEVICE_FEATURES: | |
504 | case VIRTIO_MMIO_QUEUE_NUM_MAX: | |
505 | case VIRTIO_MMIO_INTERRUPT_STATUS: | |
44e687a4 | 506 | case VIRTIO_MMIO_CONFIG_GENERATION: |
da1804d1 | 507 | qemu_log_mask(LOG_GUEST_ERROR, |
44e687a4 SL |
508 | "%s: write to read-only register (0x%" HWADDR_PRIx ")\n", |
509 | __func__, offset); | |
4b52530b PM |
510 | break; |
511 | ||
512 | default: | |
44e687a4 SL |
513 | qemu_log_mask(LOG_GUEST_ERROR, |
514 | "%s: bad register offset (0x%" HWADDR_PRIx ")\n", | |
515 | __func__, offset); | |
4b52530b PM |
516 | } |
517 | } | |
518 | ||
44e687a4 | 519 | static const MemoryRegionOps virtio_legacy_mem_ops = { |
4b52530b PM |
520 | .read = virtio_mmio_read, |
521 | .write = virtio_mmio_write, | |
522 | .endianness = DEVICE_NATIVE_ENDIAN, | |
523 | }; | |
524 | ||
44e687a4 SL |
525 | static const MemoryRegionOps virtio_mem_ops = { |
526 | .read = virtio_mmio_read, | |
527 | .write = virtio_mmio_write, | |
528 | .endianness = DEVICE_LITTLE_ENDIAN, | |
529 | }; | |
530 | ||
4b52530b PM |
531 | static void virtio_mmio_update_irq(DeviceState *opaque, uint16_t vector) |
532 | { | |
533 | VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque); | |
06d3dff0 | 534 | VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); |
4b52530b PM |
535 | int level; |
536 | ||
06d3dff0 | 537 | if (!vdev) { |
4b52530b PM |
538 | return; |
539 | } | |
d73415a3 | 540 | level = (qatomic_read(&vdev->isr) != 0); |
da1804d1 | 541 | trace_virtio_mmio_setting_irq(level); |
4b52530b PM |
542 | qemu_set_irq(proxy->irq, level); |
543 | } | |
544 | ||
4b52530b PM |
545 | static int virtio_mmio_load_config(DeviceState *opaque, QEMUFile *f) |
546 | { | |
547 | VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque); | |
548 | ||
549 | proxy->host_features_sel = qemu_get_be32(f); | |
550 | proxy->guest_features_sel = qemu_get_be32(f); | |
551 | proxy->guest_page_shift = qemu_get_be32(f); | |
552 | return 0; | |
553 | } | |
554 | ||
555 | static void virtio_mmio_save_config(DeviceState *opaque, QEMUFile *f) | |
556 | { | |
557 | VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque); | |
558 | ||
559 | qemu_put_be32(f, proxy->host_features_sel); | |
560 | qemu_put_be32(f, proxy->guest_features_sel); | |
561 | qemu_put_be32(f, proxy->guest_page_shift); | |
562 | } | |
563 | ||
44e687a4 SL |
564 | static const VMStateDescription vmstate_virtio_mmio_queue_state = { |
565 | .name = "virtio_mmio/queue_state", | |
566 | .version_id = 1, | |
567 | .minimum_version_id = 1, | |
568 | .fields = (VMStateField[]) { | |
569 | VMSTATE_UINT16(num, VirtIOMMIOQueue), | |
570 | VMSTATE_BOOL(enabled, VirtIOMMIOQueue), | |
571 | VMSTATE_UINT32_ARRAY(desc, VirtIOMMIOQueue, 2), | |
572 | VMSTATE_UINT32_ARRAY(avail, VirtIOMMIOQueue, 2), | |
573 | VMSTATE_UINT32_ARRAY(used, VirtIOMMIOQueue, 2), | |
574 | VMSTATE_END_OF_LIST() | |
575 | } | |
576 | }; | |
577 | ||
578 | static const VMStateDescription vmstate_virtio_mmio_state_sub = { | |
579 | .name = "virtio_mmio/state", | |
580 | .version_id = 1, | |
581 | .minimum_version_id = 1, | |
582 | .fields = (VMStateField[]) { | |
583 | VMSTATE_UINT32_ARRAY(guest_features, VirtIOMMIOProxy, 2), | |
584 | VMSTATE_STRUCT_ARRAY(vqs, VirtIOMMIOProxy, VIRTIO_QUEUE_MAX, 0, | |
585 | vmstate_virtio_mmio_queue_state, | |
586 | VirtIOMMIOQueue), | |
587 | VMSTATE_END_OF_LIST() | |
588 | } | |
589 | }; | |
590 | ||
591 | static const VMStateDescription vmstate_virtio_mmio = { | |
592 | .name = "virtio_mmio", | |
593 | .version_id = 1, | |
594 | .minimum_version_id = 1, | |
44e687a4 SL |
595 | .fields = (VMStateField[]) { |
596 | VMSTATE_END_OF_LIST() | |
597 | }, | |
598 | .subsections = (const VMStateDescription * []) { | |
599 | &vmstate_virtio_mmio_state_sub, | |
600 | NULL | |
601 | } | |
602 | }; | |
603 | ||
604 | static void virtio_mmio_save_extra_state(DeviceState *opaque, QEMUFile *f) | |
605 | { | |
606 | VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque); | |
607 | ||
608 | vmstate_save_state(f, &vmstate_virtio_mmio, proxy, NULL); | |
609 | } | |
610 | ||
611 | static int virtio_mmio_load_extra_state(DeviceState *opaque, QEMUFile *f) | |
612 | { | |
613 | VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque); | |
614 | ||
615 | return vmstate_load_state(f, &vmstate_virtio_mmio, proxy, 1); | |
616 | } | |
617 | ||
618 | static bool virtio_mmio_has_extra_state(DeviceState *opaque) | |
619 | { | |
620 | VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque); | |
621 | ||
622 | return !proxy->legacy; | |
623 | } | |
624 | ||
4b52530b PM |
625 | static void virtio_mmio_reset(DeviceState *d) |
626 | { | |
627 | VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d); | |
44e687a4 | 628 | int i; |
4b52530b PM |
629 | |
630 | virtio_bus_reset(&proxy->bus); | |
631 | proxy->host_features_sel = 0; | |
632 | proxy->guest_features_sel = 0; | |
633 | proxy->guest_page_shift = 0; | |
44e687a4 SL |
634 | |
635 | if (!proxy->legacy) { | |
636 | proxy->guest_features[0] = proxy->guest_features[1] = 0; | |
637 | ||
638 | for (i = 0; i < VIRTIO_QUEUE_MAX; i++) { | |
639 | proxy->vqs[i].enabled = 0; | |
640 | proxy->vqs[i].num = 0; | |
641 | proxy->vqs[i].desc[0] = proxy->vqs[i].desc[1] = 0; | |
642 | proxy->vqs[i].avail[0] = proxy->vqs[i].avail[1] = 0; | |
643 | proxy->vqs[i].used[0] = proxy->vqs[i].used[1] = 0; | |
644 | } | |
645 | } | |
4b52530b PM |
646 | } |
647 | ||
434027ba YSP |
648 | static int virtio_mmio_set_guest_notifier(DeviceState *d, int n, bool assign, |
649 | bool with_irqfd) | |
650 | { | |
651 | VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d); | |
652 | VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); | |
653 | VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev); | |
654 | VirtQueue *vq = virtio_get_queue(vdev, n); | |
655 | EventNotifier *notifier = virtio_queue_get_guest_notifier(vq); | |
656 | ||
657 | if (assign) { | |
658 | int r = event_notifier_init(notifier, 0); | |
659 | if (r < 0) { | |
660 | return r; | |
661 | } | |
662 | virtio_queue_set_guest_notifier_fd_handler(vq, true, with_irqfd); | |
663 | } else { | |
664 | virtio_queue_set_guest_notifier_fd_handler(vq, false, with_irqfd); | |
665 | event_notifier_cleanup(notifier); | |
666 | } | |
667 | ||
2858bc68 | 668 | if (vdc->guest_notifier_mask && vdev->use_guest_notifier_mask) { |
434027ba YSP |
669 | vdc->guest_notifier_mask(vdev, n, !assign); |
670 | } | |
671 | ||
672 | return 0; | |
673 | } | |
674 | ||
675 | static int virtio_mmio_set_guest_notifiers(DeviceState *d, int nvqs, | |
676 | bool assign) | |
677 | { | |
678 | VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d); | |
679 | VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); | |
680 | /* TODO: need to check if kvm-arm supports irqfd */ | |
681 | bool with_irqfd = false; | |
682 | int r, n; | |
683 | ||
684 | nvqs = MIN(nvqs, VIRTIO_QUEUE_MAX); | |
685 | ||
686 | for (n = 0; n < nvqs; n++) { | |
687 | if (!virtio_queue_get_num(vdev, n)) { | |
688 | break; | |
689 | } | |
690 | ||
691 | r = virtio_mmio_set_guest_notifier(d, n, assign, with_irqfd); | |
692 | if (r < 0) { | |
693 | goto assign_error; | |
694 | } | |
695 | } | |
696 | ||
697 | return 0; | |
698 | ||
699 | assign_error: | |
700 | /* We get here on assignment failure. Recover by undoing for VQs 0 .. n. */ | |
701 | assert(assign); | |
702 | while (--n >= 0) { | |
703 | virtio_mmio_set_guest_notifier(d, n, !assign, false); | |
704 | } | |
705 | return r; | |
706 | } | |
707 | ||
44e687a4 SL |
708 | static void virtio_mmio_pre_plugged(DeviceState *d, Error **errp) |
709 | { | |
710 | VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d); | |
711 | VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); | |
712 | ||
713 | if (!proxy->legacy) { | |
714 | virtio_add_feature(&vdev->host_features, VIRTIO_F_VERSION_1); | |
715 | } | |
716 | } | |
717 | ||
4b52530b PM |
718 | /* virtio-mmio device */ |
719 | ||
f58b39d2 LE |
720 | static Property virtio_mmio_properties[] = { |
721 | DEFINE_PROP_BOOL("format_transport_address", VirtIOMMIOProxy, | |
722 | format_transport_address, true), | |
44e687a4 | 723 | DEFINE_PROP_BOOL("force-legacy", VirtIOMMIOProxy, legacy, true), |
b8893a3c PD |
724 | DEFINE_PROP_BIT("ioeventfd", VirtIOMMIOProxy, flags, |
725 | VIRTIO_IOMMIO_FLAG_USE_IOEVENTFD_BIT, true), | |
f58b39d2 LE |
726 | DEFINE_PROP_END_OF_LIST(), |
727 | }; | |
728 | ||
4b52530b PM |
729 | static void virtio_mmio_realizefn(DeviceState *d, Error **errp) |
730 | { | |
731 | VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d); | |
732 | SysBusDevice *sbd = SYS_BUS_DEVICE(d); | |
733 | ||
d637e1dc | 734 | qbus_init(&proxy->bus, sizeof(proxy->bus), TYPE_VIRTIO_MMIO_BUS, d, NULL); |
4b52530b | 735 | sysbus_init_irq(sbd, &proxy->irq); |
b8893a3c PD |
736 | |
737 | if (!kvm_eventfds_enabled()) { | |
738 | proxy->flags &= ~VIRTIO_IOMMIO_FLAG_USE_IOEVENTFD; | |
739 | } | |
740 | ||
3909c079 PD |
741 | /* fd-based ioevents can't be synchronized in record/replay */ |
742 | if (replay_mode != REPLAY_MODE_NONE) { | |
743 | proxy->flags &= ~VIRTIO_IOMMIO_FLAG_USE_IOEVENTFD; | |
744 | } | |
745 | ||
44e687a4 SL |
746 | if (proxy->legacy) { |
747 | memory_region_init_io(&proxy->iomem, OBJECT(d), | |
748 | &virtio_legacy_mem_ops, proxy, | |
749 | TYPE_VIRTIO_MMIO, 0x200); | |
750 | } else { | |
751 | memory_region_init_io(&proxy->iomem, OBJECT(d), | |
752 | &virtio_mem_ops, proxy, | |
753 | TYPE_VIRTIO_MMIO, 0x200); | |
754 | } | |
4b52530b PM |
755 | sysbus_init_mmio(sbd, &proxy->iomem); |
756 | } | |
757 | ||
758 | static void virtio_mmio_class_init(ObjectClass *klass, void *data) | |
759 | { | |
760 | DeviceClass *dc = DEVICE_CLASS(klass); | |
761 | ||
762 | dc->realize = virtio_mmio_realizefn; | |
763 | dc->reset = virtio_mmio_reset; | |
125ee0ed | 764 | set_bit(DEVICE_CATEGORY_MISC, dc->categories); |
4f67d30b | 765 | device_class_set_props(dc, virtio_mmio_properties); |
4b52530b PM |
766 | } |
767 | ||
768 | static const TypeInfo virtio_mmio_info = { | |
769 | .name = TYPE_VIRTIO_MMIO, | |
770 | .parent = TYPE_SYS_BUS_DEVICE, | |
771 | .instance_size = sizeof(VirtIOMMIOProxy), | |
772 | .class_init = virtio_mmio_class_init, | |
773 | }; | |
774 | ||
775 | /* virtio-mmio-bus. */ | |
776 | ||
f58b39d2 LE |
777 | static char *virtio_mmio_bus_get_dev_path(DeviceState *dev) |
778 | { | |
779 | BusState *virtio_mmio_bus; | |
780 | VirtIOMMIOProxy *virtio_mmio_proxy; | |
781 | char *proxy_path; | |
f58b39d2 | 782 | char *path; |
819b3496 | 783 | MemoryRegionSection section; |
f58b39d2 LE |
784 | |
785 | virtio_mmio_bus = qdev_get_parent_bus(dev); | |
786 | virtio_mmio_proxy = VIRTIO_MMIO(virtio_mmio_bus->parent); | |
787 | proxy_path = qdev_get_dev_path(DEVICE(virtio_mmio_proxy)); | |
788 | ||
789 | /* | |
790 | * If @format_transport_address is false, then we just perform the same as | |
791 | * virtio_bus_get_dev_path(): we delegate the address formatting for the | |
792 | * device on the virtio-mmio bus to the bus that the virtio-mmio proxy | |
793 | * (i.e., the device that implements the virtio-mmio bus) resides on. In | |
794 | * this case the base address of the virtio-mmio transport will be | |
795 | * invisible. | |
796 | */ | |
797 | if (!virtio_mmio_proxy->format_transport_address) { | |
798 | return proxy_path; | |
799 | } | |
800 | ||
801 | /* Otherwise, we append the base address of the transport. */ | |
819b3496 | 802 | section = memory_region_find(&virtio_mmio_proxy->iomem, 0, 0x200); |
803 | assert(section.mr); | |
f58b39d2 LE |
804 | |
805 | if (proxy_path) { | |
806 | path = g_strdup_printf("%s/virtio-mmio@" TARGET_FMT_plx, proxy_path, | |
819b3496 | 807 | section.offset_within_address_space); |
f58b39d2 LE |
808 | } else { |
809 | path = g_strdup_printf("virtio-mmio@" TARGET_FMT_plx, | |
819b3496 | 810 | section.offset_within_address_space); |
f58b39d2 | 811 | } |
819b3496 | 812 | memory_region_unref(section.mr); |
813 | ||
f58b39d2 LE |
814 | g_free(proxy_path); |
815 | return path; | |
816 | } | |
817 | ||
7abba7c6 CL |
818 | static void virtio_mmio_vmstate_change(DeviceState *d, bool running) |
819 | { | |
820 | VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d); | |
821 | ||
822 | if (running) { | |
823 | virtio_mmio_start_ioeventfd(proxy); | |
824 | } else { | |
825 | virtio_mmio_stop_ioeventfd(proxy); | |
826 | } | |
827 | } | |
828 | ||
4b52530b PM |
829 | static void virtio_mmio_bus_class_init(ObjectClass *klass, void *data) |
830 | { | |
831 | BusClass *bus_class = BUS_CLASS(klass); | |
832 | VirtioBusClass *k = VIRTIO_BUS_CLASS(klass); | |
833 | ||
834 | k->notify = virtio_mmio_update_irq; | |
835 | k->save_config = virtio_mmio_save_config; | |
836 | k->load_config = virtio_mmio_load_config; | |
44e687a4 SL |
837 | k->save_extra_state = virtio_mmio_save_extra_state; |
838 | k->load_extra_state = virtio_mmio_load_extra_state; | |
839 | k->has_extra_state = virtio_mmio_has_extra_state; | |
434027ba | 840 | k->set_guest_notifiers = virtio_mmio_set_guest_notifiers; |
8e93cef1 | 841 | k->ioeventfd_enabled = virtio_mmio_ioeventfd_enabled; |
c0971bcb | 842 | k->ioeventfd_assign = virtio_mmio_ioeventfd_assign; |
44e687a4 | 843 | k->pre_plugged = virtio_mmio_pre_plugged; |
7abba7c6 | 844 | k->vmstate_change = virtio_mmio_vmstate_change; |
4b52530b PM |
845 | k->has_variable_vring_alignment = true; |
846 | bus_class->max_dev = 1; | |
f58b39d2 | 847 | bus_class->get_dev_path = virtio_mmio_bus_get_dev_path; |
4b52530b PM |
848 | } |
849 | ||
850 | static const TypeInfo virtio_mmio_bus_info = { | |
851 | .name = TYPE_VIRTIO_MMIO_BUS, | |
852 | .parent = TYPE_VIRTIO_BUS, | |
853 | .instance_size = sizeof(VirtioBusState), | |
854 | .class_init = virtio_mmio_bus_class_init, | |
855 | }; | |
856 | ||
857 | static void virtio_mmio_register_types(void) | |
858 | { | |
859 | type_register_static(&virtio_mmio_bus_info); | |
860 | type_register_static(&virtio_mmio_info); | |
861 | } | |
862 | ||
863 | type_init(virtio_mmio_register_types) |