]>
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 | ||
22 | #include "hw/sysbus.h" | |
23 | #include "hw/virtio/virtio.h" | |
24 | #include "qemu/host-utils.h" | |
25 | #include "hw/virtio/virtio-bus.h" | |
26 | ||
27 | /* #define DEBUG_VIRTIO_MMIO */ | |
28 | ||
29 | #ifdef DEBUG_VIRTIO_MMIO | |
30 | ||
31 | #define DPRINTF(fmt, ...) \ | |
32 | do { printf("virtio_mmio: " fmt , ## __VA_ARGS__); } while (0) | |
33 | #else | |
34 | #define DPRINTF(fmt, ...) do {} while (0) | |
35 | #endif | |
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 | ||
52 | /* Memory mapped register offsets */ | |
53 | #define VIRTIO_MMIO_MAGIC 0x0 | |
54 | #define VIRTIO_MMIO_VERSION 0x4 | |
55 | #define VIRTIO_MMIO_DEVICEID 0x8 | |
56 | #define VIRTIO_MMIO_VENDORID 0xc | |
57 | #define VIRTIO_MMIO_HOSTFEATURES 0x10 | |
58 | #define VIRTIO_MMIO_HOSTFEATURESSEL 0x14 | |
59 | #define VIRTIO_MMIO_GUESTFEATURES 0x20 | |
60 | #define VIRTIO_MMIO_GUESTFEATURESSEL 0x24 | |
61 | #define VIRTIO_MMIO_GUESTPAGESIZE 0x28 | |
62 | #define VIRTIO_MMIO_QUEUESEL 0x30 | |
63 | #define VIRTIO_MMIO_QUEUENUMMAX 0x34 | |
64 | #define VIRTIO_MMIO_QUEUENUM 0x38 | |
65 | #define VIRTIO_MMIO_QUEUEALIGN 0x3c | |
66 | #define VIRTIO_MMIO_QUEUEPFN 0x40 | |
67 | #define VIRTIO_MMIO_QUEUENOTIFY 0x50 | |
68 | #define VIRTIO_MMIO_INTERRUPTSTATUS 0x60 | |
69 | #define VIRTIO_MMIO_INTERRUPTACK 0x64 | |
70 | #define VIRTIO_MMIO_STATUS 0x70 | |
71 | /* Device specific config space starts here */ | |
72 | #define VIRTIO_MMIO_CONFIG 0x100 | |
73 | ||
74 | #define VIRT_MAGIC 0x74726976 /* 'virt' */ | |
75 | #define VIRT_VERSION 1 | |
76 | #define VIRT_VENDOR 0x554D4551 /* 'QEMU' */ | |
77 | ||
78 | typedef struct { | |
79 | /* Generic */ | |
80 | SysBusDevice parent_obj; | |
81 | MemoryRegion iomem; | |
82 | qemu_irq irq; | |
83 | uint32_t host_features; | |
84 | /* Guest accessible state needing migration and reset */ | |
85 | uint32_t host_features_sel; | |
86 | uint32_t guest_features_sel; | |
87 | uint32_t guest_page_shift; | |
88 | /* virtio-bus */ | |
89 | VirtioBusState bus; | |
90 | } VirtIOMMIOProxy; | |
91 | ||
e5f72039 AF |
92 | static void virtio_mmio_bus_new(VirtioBusState *bus, size_t bus_size, |
93 | VirtIOMMIOProxy *dev); | |
4b52530b PM |
94 | |
95 | static uint64_t virtio_mmio_read(void *opaque, hwaddr offset, unsigned size) | |
96 | { | |
97 | VirtIOMMIOProxy *proxy = (VirtIOMMIOProxy *)opaque; | |
98 | VirtIODevice *vdev = proxy->bus.vdev; | |
99 | ||
100 | DPRINTF("virtio_mmio_read offset 0x%x\n", (int)offset); | |
101 | ||
102 | if (!vdev) { | |
103 | /* If no backend is present, we treat most registers as | |
104 | * read-as-zero, except for the magic number, version and | |
105 | * vendor ID. This is not strictly sanctioned by the virtio | |
106 | * spec, but it allows us to provide transports with no backend | |
107 | * plugged in which don't confuse Linux's virtio code: the | |
108 | * probe won't complain about the bad magic number, but the | |
109 | * device ID of zero means no backend will claim it. | |
110 | */ | |
111 | switch (offset) { | |
112 | case VIRTIO_MMIO_MAGIC: | |
113 | return VIRT_MAGIC; | |
114 | case VIRTIO_MMIO_VERSION: | |
115 | return VIRT_VERSION; | |
116 | case VIRTIO_MMIO_VENDORID: | |
117 | return VIRT_VENDOR; | |
118 | default: | |
119 | return 0; | |
120 | } | |
121 | } | |
122 | ||
123 | if (offset >= VIRTIO_MMIO_CONFIG) { | |
124 | offset -= VIRTIO_MMIO_CONFIG; | |
125 | switch (size) { | |
126 | case 1: | |
127 | return virtio_config_readb(vdev, offset); | |
128 | case 2: | |
129 | return virtio_config_readw(vdev, offset); | |
130 | case 4: | |
131 | return virtio_config_readl(vdev, offset); | |
132 | default: | |
133 | abort(); | |
134 | } | |
135 | } | |
136 | if (size != 4) { | |
137 | DPRINTF("wrong size access to register!\n"); | |
138 | return 0; | |
139 | } | |
140 | switch (offset) { | |
141 | case VIRTIO_MMIO_MAGIC: | |
142 | return VIRT_MAGIC; | |
143 | case VIRTIO_MMIO_VERSION: | |
144 | return VIRT_VERSION; | |
145 | case VIRTIO_MMIO_DEVICEID: | |
146 | return vdev->device_id; | |
147 | case VIRTIO_MMIO_VENDORID: | |
148 | return VIRT_VENDOR; | |
149 | case VIRTIO_MMIO_HOSTFEATURES: | |
150 | if (proxy->host_features_sel) { | |
151 | return 0; | |
152 | } | |
153 | return proxy->host_features; | |
154 | case VIRTIO_MMIO_QUEUENUMMAX: | |
f7b803b3 PM |
155 | if (!virtio_queue_get_num(vdev, vdev->queue_sel)) { |
156 | return 0; | |
157 | } | |
4b52530b PM |
158 | return VIRTQUEUE_MAX_SIZE; |
159 | case VIRTIO_MMIO_QUEUEPFN: | |
160 | return virtio_queue_get_addr(vdev, vdev->queue_sel) | |
161 | >> proxy->guest_page_shift; | |
162 | case VIRTIO_MMIO_INTERRUPTSTATUS: | |
163 | return vdev->isr; | |
164 | case VIRTIO_MMIO_STATUS: | |
165 | return vdev->status; | |
166 | case VIRTIO_MMIO_HOSTFEATURESSEL: | |
167 | case VIRTIO_MMIO_GUESTFEATURES: | |
168 | case VIRTIO_MMIO_GUESTFEATURESSEL: | |
169 | case VIRTIO_MMIO_GUESTPAGESIZE: | |
170 | case VIRTIO_MMIO_QUEUESEL: | |
171 | case VIRTIO_MMIO_QUEUENUM: | |
172 | case VIRTIO_MMIO_QUEUEALIGN: | |
173 | case VIRTIO_MMIO_QUEUENOTIFY: | |
174 | case VIRTIO_MMIO_INTERRUPTACK: | |
175 | DPRINTF("read of write-only register\n"); | |
176 | return 0; | |
177 | default: | |
178 | DPRINTF("bad register offset\n"); | |
179 | return 0; | |
180 | } | |
181 | return 0; | |
182 | } | |
183 | ||
184 | static void virtio_mmio_write(void *opaque, hwaddr offset, uint64_t value, | |
185 | unsigned size) | |
186 | { | |
187 | VirtIOMMIOProxy *proxy = (VirtIOMMIOProxy *)opaque; | |
188 | VirtIODevice *vdev = proxy->bus.vdev; | |
189 | ||
190 | DPRINTF("virtio_mmio_write offset 0x%x value 0x%" PRIx64 "\n", | |
191 | (int)offset, value); | |
192 | ||
193 | if (!vdev) { | |
194 | /* If no backend is present, we just make all registers | |
195 | * write-ignored. This allows us to provide transports with | |
196 | * no backend plugged in. | |
197 | */ | |
198 | return; | |
199 | } | |
200 | ||
201 | if (offset >= VIRTIO_MMIO_CONFIG) { | |
202 | offset -= VIRTIO_MMIO_CONFIG; | |
203 | switch (size) { | |
204 | case 1: | |
205 | virtio_config_writeb(vdev, offset, value); | |
206 | break; | |
207 | case 2: | |
208 | virtio_config_writew(vdev, offset, value); | |
209 | break; | |
210 | case 4: | |
211 | virtio_config_writel(vdev, offset, value); | |
212 | break; | |
213 | default: | |
214 | abort(); | |
215 | } | |
216 | return; | |
217 | } | |
218 | if (size != 4) { | |
219 | DPRINTF("wrong size access to register!\n"); | |
220 | return; | |
221 | } | |
222 | switch (offset) { | |
223 | case VIRTIO_MMIO_HOSTFEATURESSEL: | |
224 | proxy->host_features_sel = value; | |
225 | break; | |
226 | case VIRTIO_MMIO_GUESTFEATURES: | |
227 | if (!proxy->guest_features_sel) { | |
228 | virtio_set_features(vdev, value); | |
229 | } | |
230 | break; | |
231 | case VIRTIO_MMIO_GUESTFEATURESSEL: | |
232 | proxy->guest_features_sel = value; | |
233 | break; | |
234 | case VIRTIO_MMIO_GUESTPAGESIZE: | |
235 | proxy->guest_page_shift = ctz32(value); | |
236 | if (proxy->guest_page_shift > 31) { | |
237 | proxy->guest_page_shift = 0; | |
238 | } | |
239 | DPRINTF("guest page size %" PRIx64 " shift %d\n", value, | |
240 | proxy->guest_page_shift); | |
241 | break; | |
242 | case VIRTIO_MMIO_QUEUESEL: | |
243 | if (value < VIRTIO_PCI_QUEUE_MAX) { | |
244 | vdev->queue_sel = value; | |
245 | } | |
246 | break; | |
247 | case VIRTIO_MMIO_QUEUENUM: | |
248 | DPRINTF("mmio_queue write %d max %d\n", (int)value, VIRTQUEUE_MAX_SIZE); | |
249 | virtio_queue_set_num(vdev, vdev->queue_sel, value); | |
250 | break; | |
251 | case VIRTIO_MMIO_QUEUEALIGN: | |
252 | virtio_queue_set_align(vdev, vdev->queue_sel, value); | |
253 | break; | |
254 | case VIRTIO_MMIO_QUEUEPFN: | |
255 | if (value == 0) { | |
256 | virtio_reset(vdev); | |
257 | } else { | |
258 | virtio_queue_set_addr(vdev, vdev->queue_sel, | |
259 | value << proxy->guest_page_shift); | |
260 | } | |
261 | break; | |
262 | case VIRTIO_MMIO_QUEUENOTIFY: | |
263 | if (value < VIRTIO_PCI_QUEUE_MAX) { | |
264 | virtio_queue_notify(vdev, value); | |
265 | } | |
266 | break; | |
267 | case VIRTIO_MMIO_INTERRUPTACK: | |
268 | vdev->isr &= ~value; | |
269 | virtio_update_irq(vdev); | |
270 | break; | |
271 | case VIRTIO_MMIO_STATUS: | |
272 | virtio_set_status(vdev, value & 0xff); | |
273 | if (vdev->status == 0) { | |
274 | virtio_reset(vdev); | |
275 | } | |
276 | break; | |
277 | case VIRTIO_MMIO_MAGIC: | |
278 | case VIRTIO_MMIO_VERSION: | |
279 | case VIRTIO_MMIO_DEVICEID: | |
280 | case VIRTIO_MMIO_VENDORID: | |
281 | case VIRTIO_MMIO_HOSTFEATURES: | |
282 | case VIRTIO_MMIO_QUEUENUMMAX: | |
283 | case VIRTIO_MMIO_INTERRUPTSTATUS: | |
284 | DPRINTF("write to readonly register\n"); | |
285 | break; | |
286 | ||
287 | default: | |
288 | DPRINTF("bad register offset\n"); | |
289 | } | |
290 | } | |
291 | ||
292 | static const MemoryRegionOps virtio_mem_ops = { | |
293 | .read = virtio_mmio_read, | |
294 | .write = virtio_mmio_write, | |
295 | .endianness = DEVICE_NATIVE_ENDIAN, | |
296 | }; | |
297 | ||
298 | static void virtio_mmio_update_irq(DeviceState *opaque, uint16_t vector) | |
299 | { | |
300 | VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque); | |
301 | int level; | |
302 | ||
303 | if (!proxy->bus.vdev) { | |
304 | return; | |
305 | } | |
306 | level = (proxy->bus.vdev->isr != 0); | |
307 | DPRINTF("virtio_mmio setting IRQ %d\n", level); | |
308 | qemu_set_irq(proxy->irq, level); | |
309 | } | |
310 | ||
311 | static unsigned int virtio_mmio_get_features(DeviceState *opaque) | |
312 | { | |
313 | VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque); | |
314 | ||
315 | return proxy->host_features; | |
316 | } | |
317 | ||
318 | static int virtio_mmio_load_config(DeviceState *opaque, QEMUFile *f) | |
319 | { | |
320 | VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque); | |
321 | ||
322 | proxy->host_features_sel = qemu_get_be32(f); | |
323 | proxy->guest_features_sel = qemu_get_be32(f); | |
324 | proxy->guest_page_shift = qemu_get_be32(f); | |
325 | return 0; | |
326 | } | |
327 | ||
328 | static void virtio_mmio_save_config(DeviceState *opaque, QEMUFile *f) | |
329 | { | |
330 | VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque); | |
331 | ||
332 | qemu_put_be32(f, proxy->host_features_sel); | |
333 | qemu_put_be32(f, proxy->guest_features_sel); | |
334 | qemu_put_be32(f, proxy->guest_page_shift); | |
335 | } | |
336 | ||
337 | static void virtio_mmio_reset(DeviceState *d) | |
338 | { | |
339 | VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d); | |
340 | ||
341 | virtio_bus_reset(&proxy->bus); | |
342 | proxy->host_features_sel = 0; | |
343 | proxy->guest_features_sel = 0; | |
344 | proxy->guest_page_shift = 0; | |
345 | } | |
346 | ||
347 | /* virtio-mmio device */ | |
348 | ||
349 | /* This is called by virtio-bus just after the device is plugged. */ | |
350 | static void virtio_mmio_device_plugged(DeviceState *opaque) | |
351 | { | |
352 | VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque); | |
353 | ||
354 | proxy->host_features |= (0x1 << VIRTIO_F_NOTIFY_ON_EMPTY); | |
355 | proxy->host_features = virtio_bus_get_vdev_features(&proxy->bus, | |
356 | proxy->host_features); | |
357 | } | |
358 | ||
359 | static void virtio_mmio_realizefn(DeviceState *d, Error **errp) | |
360 | { | |
361 | VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d); | |
362 | SysBusDevice *sbd = SYS_BUS_DEVICE(d); | |
363 | ||
e5f72039 | 364 | virtio_mmio_bus_new(&proxy->bus, sizeof(proxy->bus), proxy); |
4b52530b PM |
365 | sysbus_init_irq(sbd, &proxy->irq); |
366 | memory_region_init_io(&proxy->iomem, OBJECT(d), &virtio_mem_ops, proxy, | |
367 | TYPE_VIRTIO_MMIO, 0x200); | |
368 | sysbus_init_mmio(sbd, &proxy->iomem); | |
369 | } | |
370 | ||
371 | static void virtio_mmio_class_init(ObjectClass *klass, void *data) | |
372 | { | |
373 | DeviceClass *dc = DEVICE_CLASS(klass); | |
374 | ||
375 | dc->realize = virtio_mmio_realizefn; | |
376 | dc->reset = virtio_mmio_reset; | |
125ee0ed | 377 | set_bit(DEVICE_CATEGORY_MISC, dc->categories); |
4b52530b PM |
378 | } |
379 | ||
380 | static const TypeInfo virtio_mmio_info = { | |
381 | .name = TYPE_VIRTIO_MMIO, | |
382 | .parent = TYPE_SYS_BUS_DEVICE, | |
383 | .instance_size = sizeof(VirtIOMMIOProxy), | |
384 | .class_init = virtio_mmio_class_init, | |
385 | }; | |
386 | ||
387 | /* virtio-mmio-bus. */ | |
388 | ||
e5f72039 AF |
389 | static void virtio_mmio_bus_new(VirtioBusState *bus, size_t bus_size, |
390 | VirtIOMMIOProxy *dev) | |
4b52530b PM |
391 | { |
392 | DeviceState *qdev = DEVICE(dev); | |
393 | BusState *qbus; | |
394 | ||
fb17dfe0 | 395 | qbus_create_inplace(bus, bus_size, TYPE_VIRTIO_MMIO_BUS, qdev, NULL); |
4b52530b PM |
396 | qbus = BUS(bus); |
397 | qbus->allow_hotplug = 0; | |
398 | } | |
399 | ||
400 | static void virtio_mmio_bus_class_init(ObjectClass *klass, void *data) | |
401 | { | |
402 | BusClass *bus_class = BUS_CLASS(klass); | |
403 | VirtioBusClass *k = VIRTIO_BUS_CLASS(klass); | |
404 | ||
405 | k->notify = virtio_mmio_update_irq; | |
406 | k->save_config = virtio_mmio_save_config; | |
407 | k->load_config = virtio_mmio_load_config; | |
408 | k->get_features = virtio_mmio_get_features; | |
409 | k->device_plugged = virtio_mmio_device_plugged; | |
410 | k->has_variable_vring_alignment = true; | |
411 | bus_class->max_dev = 1; | |
412 | } | |
413 | ||
414 | static const TypeInfo virtio_mmio_bus_info = { | |
415 | .name = TYPE_VIRTIO_MMIO_BUS, | |
416 | .parent = TYPE_VIRTIO_BUS, | |
417 | .instance_size = sizeof(VirtioBusState), | |
418 | .class_init = virtio_mmio_bus_class_init, | |
419 | }; | |
420 | ||
421 | static void virtio_mmio_register_types(void) | |
422 | { | |
423 | type_register_static(&virtio_mmio_bus_info); | |
424 | type_register_static(&virtio_mmio_info); | |
425 | } | |
426 | ||
427 | type_init(virtio_mmio_register_types) |