]>
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" |
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 PM |
52 | #define VIRT_MAGIC 0x74726976 /* 'virt' */ |
53 | #define VIRT_VERSION 1 | |
54 | #define VIRT_VENDOR 0x554D4551 /* 'QEMU' */ | |
55 | ||
56 | typedef struct { | |
57 | /* Generic */ | |
58 | SysBusDevice parent_obj; | |
59 | MemoryRegion iomem; | |
60 | qemu_irq irq; | |
4b52530b PM |
61 | /* Guest accessible state needing migration and reset */ |
62 | uint32_t host_features_sel; | |
63 | uint32_t guest_features_sel; | |
64 | uint32_t guest_page_shift; | |
65 | /* virtio-bus */ | |
66 | VirtioBusState bus; | |
f58b39d2 | 67 | bool format_transport_address; |
4b52530b PM |
68 | } VirtIOMMIOProxy; |
69 | ||
8e93cef1 | 70 | static bool virtio_mmio_ioeventfd_enabled(DeviceState *d) |
c0971bcb | 71 | { |
8e93cef1 | 72 | return kvm_eventfds_enabled(); |
434027ba YSP |
73 | } |
74 | ||
c0971bcb CH |
75 | static int virtio_mmio_ioeventfd_assign(DeviceState *d, |
76 | EventNotifier *notifier, | |
77 | int n, bool assign) | |
434027ba | 78 | { |
c0971bcb | 79 | VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d); |
434027ba | 80 | |
c0971bcb | 81 | if (assign) { |
7e71da7f | 82 | memory_region_add_eventfd(&proxy->iomem, VIRTIO_MMIO_QUEUE_NOTIFY, 4, |
c0971bcb CH |
83 | true, n, notifier); |
84 | } else { | |
7e71da7f | 85 | memory_region_del_eventfd(&proxy->iomem, VIRTIO_MMIO_QUEUE_NOTIFY, 4, |
c0971bcb | 86 | true, n, notifier); |
434027ba | 87 | } |
c0971bcb CH |
88 | return 0; |
89 | } | |
434027ba | 90 | |
c0971bcb CH |
91 | static void virtio_mmio_start_ioeventfd(VirtIOMMIOProxy *proxy) |
92 | { | |
93 | virtio_bus_start_ioeventfd(&proxy->bus); | |
94 | } | |
434027ba | 95 | |
c0971bcb CH |
96 | static void virtio_mmio_stop_ioeventfd(VirtIOMMIOProxy *proxy) |
97 | { | |
98 | virtio_bus_stop_ioeventfd(&proxy->bus); | |
434027ba YSP |
99 | } |
100 | ||
4b52530b PM |
101 | static uint64_t virtio_mmio_read(void *opaque, hwaddr offset, unsigned size) |
102 | { | |
103 | VirtIOMMIOProxy *proxy = (VirtIOMMIOProxy *)opaque; | |
06d3dff0 | 104 | VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); |
4b52530b | 105 | |
da1804d1 | 106 | trace_virtio_mmio_read(offset); |
4b52530b PM |
107 | |
108 | if (!vdev) { | |
109 | /* If no backend is present, we treat most registers as | |
110 | * read-as-zero, except for the magic number, version and | |
111 | * vendor ID. This is not strictly sanctioned by the virtio | |
112 | * spec, but it allows us to provide transports with no backend | |
113 | * plugged in which don't confuse Linux's virtio code: the | |
114 | * probe won't complain about the bad magic number, but the | |
115 | * device ID of zero means no backend will claim it. | |
116 | */ | |
117 | switch (offset) { | |
7e71da7f | 118 | case VIRTIO_MMIO_MAGIC_VALUE: |
4b52530b PM |
119 | return VIRT_MAGIC; |
120 | case VIRTIO_MMIO_VERSION: | |
121 | return VIRT_VERSION; | |
7e71da7f | 122 | case VIRTIO_MMIO_VENDOR_ID: |
4b52530b PM |
123 | return VIRT_VENDOR; |
124 | default: | |
125 | return 0; | |
126 | } | |
127 | } | |
128 | ||
129 | if (offset >= VIRTIO_MMIO_CONFIG) { | |
130 | offset -= VIRTIO_MMIO_CONFIG; | |
131 | switch (size) { | |
132 | case 1: | |
133 | return virtio_config_readb(vdev, offset); | |
134 | case 2: | |
135 | return virtio_config_readw(vdev, offset); | |
136 | case 4: | |
137 | return virtio_config_readl(vdev, offset); | |
138 | default: | |
139 | abort(); | |
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: | |
152 | return VIRT_VERSION; | |
7e71da7f | 153 | case VIRTIO_MMIO_DEVICE_ID: |
4b52530b | 154 | return vdev->device_id; |
7e71da7f | 155 | case VIRTIO_MMIO_VENDOR_ID: |
4b52530b | 156 | return VIRT_VENDOR; |
7e71da7f | 157 | case VIRTIO_MMIO_DEVICE_FEATURES: |
4b52530b PM |
158 | if (proxy->host_features_sel) { |
159 | return 0; | |
160 | } | |
6b8f1020 | 161 | return vdev->host_features; |
7e71da7f | 162 | case VIRTIO_MMIO_QUEUE_NUM_MAX: |
f7b803b3 PM |
163 | if (!virtio_queue_get_num(vdev, vdev->queue_sel)) { |
164 | return 0; | |
165 | } | |
4b52530b | 166 | return VIRTQUEUE_MAX_SIZE; |
7e71da7f | 167 | case VIRTIO_MMIO_QUEUE_PFN: |
4b52530b PM |
168 | return virtio_queue_get_addr(vdev, vdev->queue_sel) |
169 | >> proxy->guest_page_shift; | |
7e71da7f | 170 | case VIRTIO_MMIO_INTERRUPT_STATUS: |
0687c37c | 171 | return atomic_read(&vdev->isr); |
4b52530b PM |
172 | case VIRTIO_MMIO_STATUS: |
173 | return vdev->status; | |
7e71da7f MT |
174 | case VIRTIO_MMIO_DEVICE_FEATURES_SEL: |
175 | case VIRTIO_MMIO_DRIVER_FEATURES: | |
176 | case VIRTIO_MMIO_DRIVER_FEATURES_SEL: | |
177 | case VIRTIO_MMIO_GUEST_PAGE_SIZE: | |
178 | case VIRTIO_MMIO_QUEUE_SEL: | |
179 | case VIRTIO_MMIO_QUEUE_NUM: | |
180 | case VIRTIO_MMIO_QUEUE_ALIGN: | |
181 | case VIRTIO_MMIO_QUEUE_NOTIFY: | |
182 | case VIRTIO_MMIO_INTERRUPT_ACK: | |
da1804d1 BL |
183 | qemu_log_mask(LOG_GUEST_ERROR, |
184 | "%s: read of write-only register\n", | |
185 | __func__); | |
4b52530b PM |
186 | return 0; |
187 | default: | |
da1804d1 | 188 | qemu_log_mask(LOG_GUEST_ERROR, "%s: bad register offset\n", __func__); |
4b52530b PM |
189 | return 0; |
190 | } | |
191 | return 0; | |
192 | } | |
193 | ||
194 | static void virtio_mmio_write(void *opaque, hwaddr offset, uint64_t value, | |
195 | unsigned size) | |
196 | { | |
197 | VirtIOMMIOProxy *proxy = (VirtIOMMIOProxy *)opaque; | |
06d3dff0 | 198 | VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); |
4b52530b | 199 | |
da1804d1 | 200 | trace_virtio_mmio_write_offset(offset, value); |
4b52530b PM |
201 | |
202 | if (!vdev) { | |
203 | /* If no backend is present, we just make all registers | |
204 | * write-ignored. This allows us to provide transports with | |
205 | * no backend plugged in. | |
206 | */ | |
207 | return; | |
208 | } | |
209 | ||
210 | if (offset >= VIRTIO_MMIO_CONFIG) { | |
211 | offset -= VIRTIO_MMIO_CONFIG; | |
212 | switch (size) { | |
213 | case 1: | |
214 | virtio_config_writeb(vdev, offset, value); | |
215 | break; | |
216 | case 2: | |
217 | virtio_config_writew(vdev, offset, value); | |
218 | break; | |
219 | case 4: | |
220 | virtio_config_writel(vdev, offset, value); | |
221 | break; | |
222 | default: | |
223 | abort(); | |
224 | } | |
225 | return; | |
226 | } | |
227 | if (size != 4) { | |
da1804d1 BL |
228 | qemu_log_mask(LOG_GUEST_ERROR, |
229 | "%s: wrong size access to register!\n", | |
230 | __func__); | |
4b52530b PM |
231 | return; |
232 | } | |
233 | switch (offset) { | |
7e71da7f | 234 | case VIRTIO_MMIO_DEVICE_FEATURES_SEL: |
4b52530b PM |
235 | proxy->host_features_sel = value; |
236 | break; | |
7e71da7f | 237 | case VIRTIO_MMIO_DRIVER_FEATURES: |
4b52530b PM |
238 | if (!proxy->guest_features_sel) { |
239 | virtio_set_features(vdev, value); | |
240 | } | |
241 | break; | |
7e71da7f | 242 | case VIRTIO_MMIO_DRIVER_FEATURES_SEL: |
4b52530b PM |
243 | proxy->guest_features_sel = value; |
244 | break; | |
7e71da7f | 245 | case VIRTIO_MMIO_GUEST_PAGE_SIZE: |
4b52530b PM |
246 | proxy->guest_page_shift = ctz32(value); |
247 | if (proxy->guest_page_shift > 31) { | |
248 | proxy->guest_page_shift = 0; | |
249 | } | |
da1804d1 | 250 | trace_virtio_mmio_guest_page(value, proxy->guest_page_shift); |
4b52530b | 251 | break; |
7e71da7f | 252 | case VIRTIO_MMIO_QUEUE_SEL: |
87b3bd1c | 253 | if (value < VIRTIO_QUEUE_MAX) { |
4b52530b PM |
254 | vdev->queue_sel = value; |
255 | } | |
256 | break; | |
7e71da7f | 257 | case VIRTIO_MMIO_QUEUE_NUM: |
da1804d1 | 258 | trace_virtio_mmio_queue_write(value, VIRTQUEUE_MAX_SIZE); |
4b52530b | 259 | virtio_queue_set_num(vdev, vdev->queue_sel, value); |
ab223c95 CH |
260 | /* Note: only call this function for legacy devices */ |
261 | virtio_queue_update_rings(vdev, vdev->queue_sel); | |
4b52530b | 262 | break; |
7e71da7f | 263 | case VIRTIO_MMIO_QUEUE_ALIGN: |
ab223c95 | 264 | /* Note: this is only valid for legacy devices */ |
4b52530b PM |
265 | virtio_queue_set_align(vdev, vdev->queue_sel, value); |
266 | break; | |
7e71da7f | 267 | case VIRTIO_MMIO_QUEUE_PFN: |
4b52530b PM |
268 | if (value == 0) { |
269 | virtio_reset(vdev); | |
270 | } else { | |
271 | virtio_queue_set_addr(vdev, vdev->queue_sel, | |
272 | value << proxy->guest_page_shift); | |
273 | } | |
274 | break; | |
7e71da7f | 275 | case VIRTIO_MMIO_QUEUE_NOTIFY: |
87b3bd1c | 276 | if (value < VIRTIO_QUEUE_MAX) { |
4b52530b PM |
277 | virtio_queue_notify(vdev, value); |
278 | } | |
279 | break; | |
7e71da7f | 280 | case VIRTIO_MMIO_INTERRUPT_ACK: |
0687c37c | 281 | atomic_and(&vdev->isr, ~value); |
4b52530b PM |
282 | virtio_update_irq(vdev); |
283 | break; | |
284 | case VIRTIO_MMIO_STATUS: | |
434027ba YSP |
285 | if (!(value & VIRTIO_CONFIG_S_DRIVER_OK)) { |
286 | virtio_mmio_stop_ioeventfd(proxy); | |
287 | } | |
288 | ||
4b52530b | 289 | virtio_set_status(vdev, value & 0xff); |
434027ba YSP |
290 | |
291 | if (value & VIRTIO_CONFIG_S_DRIVER_OK) { | |
292 | virtio_mmio_start_ioeventfd(proxy); | |
293 | } | |
294 | ||
4b52530b PM |
295 | if (vdev->status == 0) { |
296 | virtio_reset(vdev); | |
297 | } | |
298 | break; | |
7e71da7f | 299 | case VIRTIO_MMIO_MAGIC_VALUE: |
4b52530b | 300 | case VIRTIO_MMIO_VERSION: |
7e71da7f MT |
301 | case VIRTIO_MMIO_DEVICE_ID: |
302 | case VIRTIO_MMIO_VENDOR_ID: | |
303 | case VIRTIO_MMIO_DEVICE_FEATURES: | |
304 | case VIRTIO_MMIO_QUEUE_NUM_MAX: | |
305 | case VIRTIO_MMIO_INTERRUPT_STATUS: | |
da1804d1 BL |
306 | qemu_log_mask(LOG_GUEST_ERROR, |
307 | "%s: write to readonly register\n", | |
308 | __func__); | |
4b52530b PM |
309 | break; |
310 | ||
311 | default: | |
da1804d1 | 312 | qemu_log_mask(LOG_GUEST_ERROR, "%s: bad register offset\n", __func__); |
4b52530b PM |
313 | } |
314 | } | |
315 | ||
316 | static const MemoryRegionOps virtio_mem_ops = { | |
317 | .read = virtio_mmio_read, | |
318 | .write = virtio_mmio_write, | |
319 | .endianness = DEVICE_NATIVE_ENDIAN, | |
320 | }; | |
321 | ||
322 | static void virtio_mmio_update_irq(DeviceState *opaque, uint16_t vector) | |
323 | { | |
324 | VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque); | |
06d3dff0 | 325 | VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); |
4b52530b PM |
326 | int level; |
327 | ||
06d3dff0 | 328 | if (!vdev) { |
4b52530b PM |
329 | return; |
330 | } | |
0687c37c | 331 | level = (atomic_read(&vdev->isr) != 0); |
da1804d1 | 332 | trace_virtio_mmio_setting_irq(level); |
4b52530b PM |
333 | qemu_set_irq(proxy->irq, level); |
334 | } | |
335 | ||
4b52530b PM |
336 | static int virtio_mmio_load_config(DeviceState *opaque, QEMUFile *f) |
337 | { | |
338 | VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque); | |
339 | ||
340 | proxy->host_features_sel = qemu_get_be32(f); | |
341 | proxy->guest_features_sel = qemu_get_be32(f); | |
342 | proxy->guest_page_shift = qemu_get_be32(f); | |
343 | return 0; | |
344 | } | |
345 | ||
346 | static void virtio_mmio_save_config(DeviceState *opaque, QEMUFile *f) | |
347 | { | |
348 | VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque); | |
349 | ||
350 | qemu_put_be32(f, proxy->host_features_sel); | |
351 | qemu_put_be32(f, proxy->guest_features_sel); | |
352 | qemu_put_be32(f, proxy->guest_page_shift); | |
353 | } | |
354 | ||
355 | static void virtio_mmio_reset(DeviceState *d) | |
356 | { | |
357 | VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d); | |
358 | ||
434027ba | 359 | virtio_mmio_stop_ioeventfd(proxy); |
4b52530b PM |
360 | virtio_bus_reset(&proxy->bus); |
361 | proxy->host_features_sel = 0; | |
362 | proxy->guest_features_sel = 0; | |
363 | proxy->guest_page_shift = 0; | |
364 | } | |
365 | ||
434027ba YSP |
366 | static int virtio_mmio_set_guest_notifier(DeviceState *d, int n, bool assign, |
367 | bool with_irqfd) | |
368 | { | |
369 | VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d); | |
370 | VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); | |
371 | VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev); | |
372 | VirtQueue *vq = virtio_get_queue(vdev, n); | |
373 | EventNotifier *notifier = virtio_queue_get_guest_notifier(vq); | |
374 | ||
375 | if (assign) { | |
376 | int r = event_notifier_init(notifier, 0); | |
377 | if (r < 0) { | |
378 | return r; | |
379 | } | |
380 | virtio_queue_set_guest_notifier_fd_handler(vq, true, with_irqfd); | |
381 | } else { | |
382 | virtio_queue_set_guest_notifier_fd_handler(vq, false, with_irqfd); | |
383 | event_notifier_cleanup(notifier); | |
384 | } | |
385 | ||
2858bc68 | 386 | if (vdc->guest_notifier_mask && vdev->use_guest_notifier_mask) { |
434027ba YSP |
387 | vdc->guest_notifier_mask(vdev, n, !assign); |
388 | } | |
389 | ||
390 | return 0; | |
391 | } | |
392 | ||
393 | static int virtio_mmio_set_guest_notifiers(DeviceState *d, int nvqs, | |
394 | bool assign) | |
395 | { | |
396 | VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d); | |
397 | VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); | |
398 | /* TODO: need to check if kvm-arm supports irqfd */ | |
399 | bool with_irqfd = false; | |
400 | int r, n; | |
401 | ||
402 | nvqs = MIN(nvqs, VIRTIO_QUEUE_MAX); | |
403 | ||
404 | for (n = 0; n < nvqs; n++) { | |
405 | if (!virtio_queue_get_num(vdev, n)) { | |
406 | break; | |
407 | } | |
408 | ||
409 | r = virtio_mmio_set_guest_notifier(d, n, assign, with_irqfd); | |
410 | if (r < 0) { | |
411 | goto assign_error; | |
412 | } | |
413 | } | |
414 | ||
415 | return 0; | |
416 | ||
417 | assign_error: | |
418 | /* We get here on assignment failure. Recover by undoing for VQs 0 .. n. */ | |
419 | assert(assign); | |
420 | while (--n >= 0) { | |
421 | virtio_mmio_set_guest_notifier(d, n, !assign, false); | |
422 | } | |
423 | return r; | |
424 | } | |
425 | ||
4b52530b PM |
426 | /* virtio-mmio device */ |
427 | ||
f58b39d2 LE |
428 | static Property virtio_mmio_properties[] = { |
429 | DEFINE_PROP_BOOL("format_transport_address", VirtIOMMIOProxy, | |
430 | format_transport_address, true), | |
431 | DEFINE_PROP_END_OF_LIST(), | |
432 | }; | |
433 | ||
4b52530b PM |
434 | static void virtio_mmio_realizefn(DeviceState *d, Error **errp) |
435 | { | |
436 | VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d); | |
437 | SysBusDevice *sbd = SYS_BUS_DEVICE(d); | |
438 | ||
2f4f6035 IM |
439 | qbus_create_inplace(&proxy->bus, sizeof(proxy->bus), TYPE_VIRTIO_MMIO_BUS, |
440 | d, NULL); | |
4b52530b PM |
441 | sysbus_init_irq(sbd, &proxy->irq); |
442 | memory_region_init_io(&proxy->iomem, OBJECT(d), &virtio_mem_ops, proxy, | |
443 | TYPE_VIRTIO_MMIO, 0x200); | |
444 | sysbus_init_mmio(sbd, &proxy->iomem); | |
445 | } | |
446 | ||
447 | static void virtio_mmio_class_init(ObjectClass *klass, void *data) | |
448 | { | |
449 | DeviceClass *dc = DEVICE_CLASS(klass); | |
450 | ||
451 | dc->realize = virtio_mmio_realizefn; | |
452 | dc->reset = virtio_mmio_reset; | |
125ee0ed | 453 | set_bit(DEVICE_CATEGORY_MISC, dc->categories); |
f58b39d2 | 454 | dc->props = virtio_mmio_properties; |
4b52530b PM |
455 | } |
456 | ||
457 | static const TypeInfo virtio_mmio_info = { | |
458 | .name = TYPE_VIRTIO_MMIO, | |
459 | .parent = TYPE_SYS_BUS_DEVICE, | |
460 | .instance_size = sizeof(VirtIOMMIOProxy), | |
461 | .class_init = virtio_mmio_class_init, | |
462 | }; | |
463 | ||
464 | /* virtio-mmio-bus. */ | |
465 | ||
f58b39d2 LE |
466 | static char *virtio_mmio_bus_get_dev_path(DeviceState *dev) |
467 | { | |
468 | BusState *virtio_mmio_bus; | |
469 | VirtIOMMIOProxy *virtio_mmio_proxy; | |
470 | char *proxy_path; | |
471 | SysBusDevice *proxy_sbd; | |
472 | char *path; | |
473 | ||
474 | virtio_mmio_bus = qdev_get_parent_bus(dev); | |
475 | virtio_mmio_proxy = VIRTIO_MMIO(virtio_mmio_bus->parent); | |
476 | proxy_path = qdev_get_dev_path(DEVICE(virtio_mmio_proxy)); | |
477 | ||
478 | /* | |
479 | * If @format_transport_address is false, then we just perform the same as | |
480 | * virtio_bus_get_dev_path(): we delegate the address formatting for the | |
481 | * device on the virtio-mmio bus to the bus that the virtio-mmio proxy | |
482 | * (i.e., the device that implements the virtio-mmio bus) resides on. In | |
483 | * this case the base address of the virtio-mmio transport will be | |
484 | * invisible. | |
485 | */ | |
486 | if (!virtio_mmio_proxy->format_transport_address) { | |
487 | return proxy_path; | |
488 | } | |
489 | ||
490 | /* Otherwise, we append the base address of the transport. */ | |
491 | proxy_sbd = SYS_BUS_DEVICE(virtio_mmio_proxy); | |
492 | assert(proxy_sbd->num_mmio == 1); | |
493 | assert(proxy_sbd->mmio[0].memory == &virtio_mmio_proxy->iomem); | |
494 | ||
495 | if (proxy_path) { | |
496 | path = g_strdup_printf("%s/virtio-mmio@" TARGET_FMT_plx, proxy_path, | |
497 | proxy_sbd->mmio[0].addr); | |
498 | } else { | |
499 | path = g_strdup_printf("virtio-mmio@" TARGET_FMT_plx, | |
500 | proxy_sbd->mmio[0].addr); | |
501 | } | |
502 | g_free(proxy_path); | |
503 | return path; | |
504 | } | |
505 | ||
4b52530b PM |
506 | static void virtio_mmio_bus_class_init(ObjectClass *klass, void *data) |
507 | { | |
508 | BusClass *bus_class = BUS_CLASS(klass); | |
509 | VirtioBusClass *k = VIRTIO_BUS_CLASS(klass); | |
510 | ||
511 | k->notify = virtio_mmio_update_irq; | |
512 | k->save_config = virtio_mmio_save_config; | |
513 | k->load_config = virtio_mmio_load_config; | |
434027ba | 514 | k->set_guest_notifiers = virtio_mmio_set_guest_notifiers; |
8e93cef1 | 515 | k->ioeventfd_enabled = virtio_mmio_ioeventfd_enabled; |
c0971bcb | 516 | k->ioeventfd_assign = virtio_mmio_ioeventfd_assign; |
4b52530b PM |
517 | k->has_variable_vring_alignment = true; |
518 | bus_class->max_dev = 1; | |
f58b39d2 | 519 | bus_class->get_dev_path = virtio_mmio_bus_get_dev_path; |
4b52530b PM |
520 | } |
521 | ||
522 | static const TypeInfo virtio_mmio_bus_info = { | |
523 | .name = TYPE_VIRTIO_MMIO_BUS, | |
524 | .parent = TYPE_VIRTIO_BUS, | |
525 | .instance_size = sizeof(VirtioBusState), | |
526 | .class_init = virtio_mmio_bus_class_init, | |
527 | }; | |
528 | ||
529 | static void virtio_mmio_register_types(void) | |
530 | { | |
531 | type_register_static(&virtio_mmio_bus_info); | |
532 | type_register_static(&virtio_mmio_info); | |
533 | } | |
534 | ||
535 | type_init(virtio_mmio_register_types) |