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