master
c 518 lines 17.7 KB
Raw
1 /*
2 * QEMU Xen PVH machine - common code.
3 *
4 * Copyright (c) 2024 Advanced Micro Devices, Inc.
5 *
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
9 #include "qemu/osdep.h"
10 #include "qemu/error-report.h"
11 #include "qemu/units.h"
12 #include "qapi/visitor.h"
13 #include "hw/core/boards.h"
14 #include "hw/core/irq.h"
15 #include "system/tpm.h"
16 #include "system/tpm_backend.h"
17 #include "system/runstate.h"
18 #include "hw/xen/xen-pvh-common.h"
19 #include "trace.h"
20
21 static const MemoryListener xen_memory_listener = {
22 .region_add = xen_region_add,
23 .region_del = xen_region_del,
24 .log_start = NULL,
25 .log_stop = NULL,
26 .log_sync = NULL,
27 .log_global_start = NULL,
28 .log_global_stop = NULL,
29 .priority = MEMORY_LISTENER_PRIORITY_ACCEL,
30 };
31
32 /*
33 * Map foreign RAM in bounded chunks so we don't build a PFN array for the
34 * entire guest size (which can be huge for large guests). We reserve a VA
35 * range once and then MAP_FIXED each chunk into place.
36 */
37 #define XEN_PVH_MAP_CHUNK_PAGES 65535
38
39 static void *xen_map_guest_ram(XenPVHMachineState *s,
40 uint64_t addr, uint64_t size)
41 {
42 size_t total_pages = size >> XC_PAGE_SHIFT;
43 size_t chunk_pages = MIN(XEN_PVH_MAP_CHUNK_PAGES, total_pages);
44 g_autofree xen_pfn_t *pfns = NULL;
45 void *base = NULL;
46 size_t offset;
47
48 if (!total_pages) {
49 goto done;
50 }
51
52 base = mmap(NULL, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
53 if (base == MAP_FAILED) {
54 base = NULL;
55 goto done;
56 }
57
58 pfns = g_new0(xen_pfn_t, chunk_pages);
59 if (!pfns) {
60 munmap(base, size);
61 base = NULL;
62 goto done;
63 }
64
65 for (offset = 0; offset < total_pages; offset += chunk_pages) {
66 size_t num_pages = MIN(chunk_pages, total_pages - offset);
67 void *mapped;
68 size_t i;
69
70 for (i = 0; i < num_pages; i++) {
71 pfns[i] = (addr >> XC_PAGE_SHIFT) + offset + i;
72 }
73
74 mapped = xenforeignmemory_map2(
75 xen_fmem, xen_domid,
76 (uint8_t *)base + (offset << XC_PAGE_SHIFT),
77 PROT_READ | PROT_WRITE, MAP_FIXED,
78 num_pages, pfns, NULL);
79 if (!mapped) {
80 munmap(base, size);
81 base = NULL;
82 goto done;
83 }
84 }
85 done:
86 if (!base) {
87 /* We can't recover from this. */
88 error_report("FATAL: Failed to foreign-map %" PRIx64 " - %" PRIx64,
89 addr, addr + size);
90 exit(EXIT_FAILURE);
91 }
92 return base;
93 }
94
95 static void xen_pvh_init_ram(XenPVHMachineState *s,
96 MemoryRegion *sysmem)
97 {
98 MachineState *ms = MACHINE(s);
99 ram_addr_t block_len, ram_size[2];
100
101 if (ms->ram_size <= s->cfg.ram_low.size) {
102 ram_size[0] = ms->ram_size;
103 ram_size[1] = 0;
104 block_len = s->cfg.ram_low.base + ram_size[0];
105 } else {
106 ram_size[0] = s->cfg.ram_low.size;
107 ram_size[1] = ms->ram_size - s->cfg.ram_low.size;
108 block_len = s->cfg.ram_high.base + ram_size[1];
109 }
110
111 if (s->cfg.mapcache) {
112 memory_region_init_ram(&xen_memory, NULL, "xen.ram",
113 block_len, &error_fatal);
114 memory_region_init_alias(&s->ram.low, NULL, "xen.ram.lo", &xen_memory,
115 s->cfg.ram_low.base, ram_size[0]);
116 if (ram_size[1] > 0) {
117 memory_region_init_alias(&s->ram.high, NULL, "xen.ram.hi",
118 &xen_memory,
119 s->cfg.ram_high.base, ram_size[1]);
120 }
121 } else {
122 void *p;
123
124 p = xen_map_guest_ram(s, s->cfg.ram_low.base, ram_size[0]);
125 memory_region_init_ram_ptr(&s->ram.low, NULL, "xen.ram.lo",
126 ram_size[0], p);
127 if (ram_size[1] > 0) {
128 p = xen_map_guest_ram(s, s->cfg.ram_high.base, ram_size[1]);
129 memory_region_init_ram_ptr(&s->ram.high, NULL, "xen.ram.hi",
130 ram_size[1], p);
131 }
132 }
133
134 /* Map them onto QEMU's address-space. */
135 memory_region_add_subregion(sysmem, s->cfg.ram_low.base, &s->ram.low);
136 if (ram_size[1] > 0) {
137 memory_region_add_subregion(sysmem, s->cfg.ram_high.base, &s->ram.high);
138 }
139
140 /* Grants are only supported when the mapcache is on. */
141 if (s->cfg.mapcache) {
142 /* Setup support for grants. */
143 memory_region_init_ram(&xen_grants, NULL, "xen.grants", block_len,
144 &error_fatal);
145 memory_region_add_subregion(sysmem, XEN_GRANT_ADDR_OFF, &xen_grants);
146 }
147 }
148
149 static void xen_set_irq(void *opaque, int irq, int level)
150 {
151 if (xendevicemodel_set_irq_level(xen_dmod, xen_domid, irq, level)) {
152 error_report("xendevicemodel_set_irq_level failed");
153 }
154 }
155
156 static void xen_create_virtio_mmio_devices(XenPVHMachineState *s)
157 {
158 int i;
159
160 /*
161 * We create the transports in reverse order. Since qbus_realize()
162 * prepends (not appends) new child buses, the decrementing loop below will
163 * create a list of virtio-mmio buses with increasing base addresses.
164 *
165 * When a -device option is processed from the command line,
166 * qbus_find_recursive() picks the next free virtio-mmio bus in forwards
167 * order.
168 *
169 * This is what the Xen tools expect.
170 */
171 for (i = s->cfg.virtio_mmio_num - 1; i >= 0; i--) {
172 hwaddr base = s->cfg.virtio_mmio.base + i * s->cfg.virtio_mmio.size;
173 qemu_irq irq = qemu_allocate_irq(xen_set_irq, NULL,
174 s->cfg.virtio_mmio_irq_base + i);
175
176 sysbus_create_simple("virtio-mmio", base, irq);
177
178 trace_xen_create_virtio_mmio_devices(i,
179 s->cfg.virtio_mmio_irq_base + i,
180 base);
181 }
182 }
183
184 #ifdef CONFIG_TPM
185 static void xen_enable_tpm(XenPVHMachineState *s)
186 {
187 Error *err = NULL;
188 DeviceState *dev;
189 SysBusDevice *busdev;
190
191 TPMBackend *be = qemu_find_tpm_be("tpm0");
192 if (be == NULL) {
193 error_report("Couldn't find tmp0 backend");
194 return;
195 }
196 dev = qdev_new(TYPE_TPM_TIS_SYSBUS);
197 /*
198 * FIXME This use of &err is is wrong. If both calls fail, the
199 * second will trip error_setv()'s assertion. If just one call
200 * fails, we leak an Error object. Setting the same property
201 * twice (first to a QOM path, then to an ID string) is almost
202 * certainly wrong, too.
203 */
204 object_property_set_link(OBJECT(dev), "tpmdev", OBJECT(be), &err);
205 object_property_set_str(OBJECT(dev), "tpmdev", be->id, &err);
206 busdev = SYS_BUS_DEVICE(dev);
207 sysbus_realize_and_unref(busdev, &error_fatal);
208 sysbus_mmio_map(busdev, 0, s->cfg.tpm.base);
209
210 trace_xen_enable_tpm(s->cfg.tpm.base);
211 }
212 #endif
213
214 /*
215 * We use the GPEX PCIe controller with its internal INTX PCI interrupt
216 * swizzling. This swizzling is emulated in QEMU and routes all INTX
217 * interrupts from endpoints down to only 4 INTX interrupts.
218 * See include/hw/pci/pci.h : pci_swizzle()
219 */
220 static inline void xenpvh_gpex_init(XenPVHMachineState *s,
221 XenPVHMachineClass *xpc,
222 MemoryRegion *sysmem)
223 {
224 MemoryRegion *ecam_reg;
225 MemoryRegion *mmio_reg;
226 DeviceState *dev;
227 int i;
228
229 object_initialize_child(OBJECT(s), "gpex", &s->pci.gpex,
230 TYPE_GPEX_HOST);
231 dev = DEVICE(&s->pci.gpex);
232 sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
233
234 ecam_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0);
235 memory_region_add_subregion(sysmem, s->cfg.pci_ecam.base, ecam_reg);
236
237 mmio_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1);
238
239 if (s->cfg.pci_mmio.size) {
240 memory_region_init_alias(&s->pci.mmio_alias, OBJECT(dev), "pcie-mmio",
241 mmio_reg,
242 s->cfg.pci_mmio.base, s->cfg.pci_mmio.size);
243 memory_region_add_subregion(sysmem, s->cfg.pci_mmio.base,
244 &s->pci.mmio_alias);
245 }
246
247 if (s->cfg.pci_mmio_high.size) {
248 memory_region_init_alias(&s->pci.mmio_high_alias, OBJECT(dev),
249 "pcie-mmio-high",
250 mmio_reg, s->cfg.pci_mmio_high.base, s->cfg.pci_mmio_high.size);
251 memory_region_add_subregion(sysmem, s->cfg.pci_mmio_high.base,
252 &s->pci.mmio_high_alias);
253 }
254
255 /*
256 * PVH implementations with PCI enabled must provide set_pci_intx_irq()
257 * and optionally an implementation of set_pci_link_route().
258 */
259 assert(xpc->set_pci_intx_irq);
260
261 for (i = 0; i < PCI_NUM_PINS; i++) {
262 qemu_irq irq = qemu_allocate_irq(xpc->set_pci_intx_irq, s, i);
263
264 sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, irq);
265 gpex_set_irq_num(GPEX_HOST(dev), i, s->cfg.pci_intx_irq_base + i);
266 if (xpc->set_pci_link_route) {
267 xpc->set_pci_link_route(i, s->cfg.pci_intx_irq_base + i);
268 }
269 }
270 }
271
272 static void xen_pvh_init(MachineState *ms)
273 {
274 XenPVHMachineState *s = XEN_PVH_MACHINE(ms);
275 XenPVHMachineClass *xpc = XEN_PVH_MACHINE_GET_CLASS(s);
276 MemoryRegion *sysmem = get_system_memory();
277
278 if (ms->ram_size == 0) {
279 warn_report("%s: ram size not specified. QEMU machine started"
280 " without IOREQ (no emulated devices including virtio)",
281 MACHINE_CLASS(object_get_class(OBJECT(ms)))->desc);
282 return;
283 }
284
285 xen_pvh_init_ram(s, sysmem);
286 xen_register_ioreq(&s->ioreq, ms->smp.max_cpus,
287 xpc->handle_bufioreq,
288 &xen_memory_listener,
289 s->cfg.mapcache);
290
291 if (s->cfg.virtio_mmio_num) {
292 xen_create_virtio_mmio_devices(s);
293 }
294
295 #ifdef CONFIG_TPM
296 if (xpc->has_tpm) {
297 if (s->cfg.tpm.base) {
298 xen_enable_tpm(s);
299 } else {
300 warn_report("tpm-base-addr is not set. TPM will not be enabled");
301 }
302 }
303 #endif
304
305 /* Non-zero pci-ecam-size enables PCI. */
306 if (s->cfg.pci_ecam.size) {
307 if (s->cfg.pci_ecam.size != 256 * MiB) {
308 error_report("pci-ecam-size only supports values 0 or 0x10000000");
309 exit(EXIT_FAILURE);
310 }
311 if (!s->cfg.pci_intx_irq_base) {
312 error_report("PCI enabled but pci-intx-irq-base not set");
313 exit(EXIT_FAILURE);
314 }
315
316 xenpvh_gpex_init(s, xpc, sysmem);
317 }
318
319 /* Call the implementation specific init. */
320 if (xpc->init) {
321 xpc->init(ms);
322 }
323 }
324
325 #define XEN_PVH_PROP_MEMMAP_SETTER(n, f) \
326 static void xen_pvh_set_ ## n ## _ ## f(Object *obj, Visitor *v, \
327 const char *name, void *opaque, \
328 Error **errp) \
329 { \
330 XenPVHMachineState *xp = XEN_PVH_MACHINE(obj); \
331 uint64_t value; \
332 \
333 if (!visit_type_size(v, name, &value, errp)) { \
334 return; \
335 } \
336 xp->cfg.n.f = value; \
337 }
338
339 #define XEN_PVH_PROP_MEMMAP_GETTER(n, f) \
340 static void xen_pvh_get_ ## n ## _ ## f(Object *obj, Visitor *v, \
341 const char *name, void *opaque, \
342 Error **errp) \
343 { \
344 XenPVHMachineState *xp = XEN_PVH_MACHINE(obj); \
345 uint64_t value = xp->cfg.n.f; \
346 \
347 visit_type_uint64(v, name, &value, errp); \
348 }
349
350 #define XEN_PVH_PROP_MEMMAP_BASE(n) \
351 XEN_PVH_PROP_MEMMAP_SETTER(n, base) \
352 XEN_PVH_PROP_MEMMAP_GETTER(n, base) \
353
354 #define XEN_PVH_PROP_MEMMAP_SIZE(n) \
355 XEN_PVH_PROP_MEMMAP_SETTER(n, size) \
356 XEN_PVH_PROP_MEMMAP_GETTER(n, size)
357
358 #define XEN_PVH_PROP_MEMMAP(n) \
359 XEN_PVH_PROP_MEMMAP_BASE(n) \
360 XEN_PVH_PROP_MEMMAP_SIZE(n)
361
362 XEN_PVH_PROP_MEMMAP(ram_low)
363 XEN_PVH_PROP_MEMMAP(ram_high)
364 /* TPM only has a base-addr option. */
365 XEN_PVH_PROP_MEMMAP_BASE(tpm)
366 XEN_PVH_PROP_MEMMAP(virtio_mmio)
367 XEN_PVH_PROP_MEMMAP(pci_ecam)
368 XEN_PVH_PROP_MEMMAP(pci_mmio)
369 XEN_PVH_PROP_MEMMAP(pci_mmio_high)
370
371 static void xen_pvh_set_mapcache(Object *obj, bool value, Error **errp)
372 {
373 XenPVHMachineState *xp = XEN_PVH_MACHINE(obj);
374
375 xp->cfg.mapcache = value;
376 }
377
378 static bool xen_pvh_get_mapcache(Object *obj, Error **errp)
379 {
380 XenPVHMachineState *xp = XEN_PVH_MACHINE(obj);
381
382 return xp->cfg.mapcache;
383 }
384
385 static void xen_pvh_set_pci_intx_irq_base(Object *obj, Visitor *v,
386 const char *name, void *opaque,
387 Error **errp)
388 {
389 XenPVHMachineState *xp = XEN_PVH_MACHINE(obj);
390 uint32_t value;
391
392 if (!visit_type_uint32(v, name, &value, errp)) {
393 return;
394 }
395
396 xp->cfg.pci_intx_irq_base = value;
397 }
398
399 static void xen_pvh_get_pci_intx_irq_base(Object *obj, Visitor *v,
400 const char *name, void *opaque,
401 Error **errp)
402 {
403 XenPVHMachineState *xp = XEN_PVH_MACHINE(obj);
404 uint32_t value = xp->cfg.pci_intx_irq_base;
405
406 visit_type_uint32(v, name, &value, errp);
407 }
408
409 void xen_pvh_class_setup_common_props(XenPVHMachineClass *xpc)
410 {
411 ObjectClass *oc = OBJECT_CLASS(xpc);
412 MachineClass *mc = MACHINE_CLASS(xpc);
413
414 #define OC_MEMMAP_PROP_BASE(c, prop_name, name) \
415 do { \
416 object_class_property_add(c, prop_name "-base", "uint64_t", \
417 xen_pvh_get_ ## name ## _base, \
418 xen_pvh_set_ ## name ## _base, NULL, NULL); \
419 object_class_property_set_description(oc, prop_name "-base", \
420 "Set base address for " prop_name); \
421 } while (0)
422
423 #define OC_MEMMAP_PROP_SIZE(c, prop_name, name) \
424 do { \
425 object_class_property_add(c, prop_name "-size", "uint64_t", \
426 xen_pvh_get_ ## name ## _size, \
427 xen_pvh_set_ ## name ## _size, NULL, NULL); \
428 object_class_property_set_description(oc, prop_name "-size", \
429 "Set memory range size for " prop_name); \
430 } while (0)
431
432 #define OC_MEMMAP_PROP(c, prop_name, name) \
433 do { \
434 OC_MEMMAP_PROP_BASE(c, prop_name, name); \
435 OC_MEMMAP_PROP_SIZE(c, prop_name, name); \
436 } while (0)
437
438 object_class_property_add_bool(oc, "mapcache", xen_pvh_get_mapcache,
439 xen_pvh_set_mapcache);
440 object_class_property_set_description(oc, "mapcache",
441 "Set on/off to enable/disable the "
442 "mapcache");
443
444 /*
445 * We provide memmap properties to allow Xen to move things to other
446 * addresses for example when users need to accomodate the memory-map
447 * for 1:1 mapped devices/memory.
448 */
449 OC_MEMMAP_PROP(oc, "ram-low", ram_low);
450 OC_MEMMAP_PROP(oc, "ram-high", ram_high);
451
452 if (xpc->has_virtio_mmio) {
453 OC_MEMMAP_PROP(oc, "virtio-mmio", virtio_mmio);
454 }
455
456 if (xpc->has_pci) {
457 OC_MEMMAP_PROP(oc, "pci-ecam", pci_ecam);
458 OC_MEMMAP_PROP(oc, "pci-mmio", pci_mmio);
459 OC_MEMMAP_PROP(oc, "pci-mmio-high", pci_mmio_high);
460
461 object_class_property_add(oc, "pci-intx-irq-base", "uint32_t",
462 xen_pvh_get_pci_intx_irq_base,
463 xen_pvh_set_pci_intx_irq_base,
464 NULL, NULL);
465 object_class_property_set_description(oc, "pci-intx-irq-base",
466 "Set PCI INTX interrupt base line.");
467 }
468
469 #ifdef CONFIG_TPM
470 if (xpc->has_tpm) {
471 object_class_property_add(oc, "tpm-base-addr", "uint64_t",
472 xen_pvh_get_tpm_base,
473 xen_pvh_set_tpm_base,
474 NULL, NULL);
475 object_class_property_set_description(oc, "tpm-base-addr",
476 "Set Base address for TPM device.");
477
478 machine_class_allow_dynamic_sysbus_dev(mc, TYPE_TPM_TIS_SYSBUS);
479 }
480 #endif
481 }
482
483 static void xen_pvh_instance_init(Object *obj)
484 {
485 XenPVHMachineState *xp = XEN_PVH_MACHINE(obj);
486
487 xp->cfg.mapcache = true;
488 }
489
490 static void xen_pvh_class_init(ObjectClass *oc, const void *data)
491 {
492 MachineClass *mc = MACHINE_CLASS(oc);
493
494 mc->init = xen_pvh_init;
495
496 mc->desc = "Xen PVH machine";
497 mc->max_cpus = 1;
498 mc->default_machine_opts = "accel=xen";
499 /* Set to zero to make sure that the real ram size is passed. */
500 mc->default_ram_size = 0;
501 }
502
503 static const TypeInfo xen_pvh_info = {
504 .name = TYPE_XEN_PVH_MACHINE,
505 .parent = TYPE_MACHINE,
506 .abstract = true,
507 .instance_size = sizeof(XenPVHMachineState),
508 .instance_init = xen_pvh_instance_init,
509 .class_size = sizeof(XenPVHMachineClass),
510 .class_init = xen_pvh_class_init,
511 };
512
513 static void xen_pvh_register_types(void)
514 {
515 type_register_static(&xen_pvh_info);
516 }
517
518 type_init(xen_pvh_register_types);