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 "qapi/error.h"
12 #include "qapi/visitor.h"
13 #include "hw/boards.h"
14 #include "hw/irq.h"
15 #include "hw/sysbus.h"
16 #include "sysemu/sysemu.h"
17 #include "sysemu/tpm.h"
18 #include "sysemu/tpm_backend.h"
19 #include "hw/xen/xen-pvh-common.h"
20 #include "trace.h"
21
22 static const MemoryListener xen_memory_listener = {
23 .region_add = xen_region_add,
24 .region_del = xen_region_del,
25 .log_start = NULL,
26 .log_stop = NULL,
27 .log_sync = NULL,
28 .log_global_start = NULL,
29 .log_global_stop = NULL,
30 .priority = MEMORY_LISTENER_PRIORITY_ACCEL,
31 };
32
xen_pvh_init_ram(XenPVHMachineState * s,MemoryRegion * sysmem)33 static void xen_pvh_init_ram(XenPVHMachineState *s,
34 MemoryRegion *sysmem)
35 {
36 MachineState *ms = MACHINE(s);
37 ram_addr_t block_len, ram_size[2];
38
39 if (ms->ram_size <= s->cfg.ram_low.size) {
40 ram_size[0] = ms->ram_size;
41 ram_size[1] = 0;
42 block_len = s->cfg.ram_low.base + ram_size[0];
43 } else {
44 ram_size[0] = s->cfg.ram_low.size;
45 ram_size[1] = ms->ram_size - s->cfg.ram_low.size;
46 block_len = s->cfg.ram_high.base + ram_size[1];
47 }
48
49 memory_region_init_ram(&xen_memory, NULL, "xen.ram", block_len,
50 &error_fatal);
51
52 memory_region_init_alias(&s->ram.low, NULL, "xen.ram.lo", &xen_memory,
53 s->cfg.ram_low.base, ram_size[0]);
54 memory_region_add_subregion(sysmem, s->cfg.ram_low.base, &s->ram.low);
55 if (ram_size[1] > 0) {
56 memory_region_init_alias(&s->ram.high, NULL, "xen.ram.hi", &xen_memory,
57 s->cfg.ram_high.base, ram_size[1]);
58 memory_region_add_subregion(sysmem, s->cfg.ram_high.base, &s->ram.high);
59 }
60
61 /* Setup support for grants. */
62 memory_region_init_ram(&xen_grants, NULL, "xen.grants", block_len,
63 &error_fatal);
64 memory_region_add_subregion(sysmem, XEN_GRANT_ADDR_OFF, &xen_grants);
65 }
66
xen_set_irq(void * opaque,int irq,int level)67 static void xen_set_irq(void *opaque, int irq, int level)
68 {
69 if (xendevicemodel_set_irq_level(xen_dmod, xen_domid, irq, level)) {
70 error_report("xendevicemodel_set_irq_level failed");
71 }
72 }
73
xen_create_virtio_mmio_devices(XenPVHMachineState * s)74 static void xen_create_virtio_mmio_devices(XenPVHMachineState *s)
75 {
76 int i;
77
78 /*
79 * We create the transports in reverse order. Since qbus_realize()
80 * prepends (not appends) new child buses, the decrementing loop below will
81 * create a list of virtio-mmio buses with increasing base addresses.
82 *
83 * When a -device option is processed from the command line,
84 * qbus_find_recursive() picks the next free virtio-mmio bus in forwards
85 * order.
86 *
87 * This is what the Xen tools expect.
88 */
89 for (i = s->cfg.virtio_mmio_num - 1; i >= 0; i--) {
90 hwaddr base = s->cfg.virtio_mmio.base + i * s->cfg.virtio_mmio.size;
91 qemu_irq irq = qemu_allocate_irq(xen_set_irq, NULL,
92 s->cfg.virtio_mmio_irq_base + i);
93
94 sysbus_create_simple("virtio-mmio", base, irq);
95
96 trace_xen_create_virtio_mmio_devices(i,
97 s->cfg.virtio_mmio_irq_base + i,
98 base);
99 }
100 }
101
102 #ifdef CONFIG_TPM
xen_enable_tpm(XenPVHMachineState * s)103 static void xen_enable_tpm(XenPVHMachineState *s)
104 {
105 Error *errp = NULL;
106 DeviceState *dev;
107 SysBusDevice *busdev;
108
109 TPMBackend *be = qemu_find_tpm_be("tpm0");
110 if (be == NULL) {
111 error_report("Couldn't find tmp0 backend");
112 return;
113 }
114 dev = qdev_new(TYPE_TPM_TIS_SYSBUS);
115 object_property_set_link(OBJECT(dev), "tpmdev", OBJECT(be), &errp);
116 object_property_set_str(OBJECT(dev), "tpmdev", be->id, &errp);
117 busdev = SYS_BUS_DEVICE(dev);
118 sysbus_realize_and_unref(busdev, &error_fatal);
119 sysbus_mmio_map(busdev, 0, s->cfg.tpm.base);
120
121 trace_xen_enable_tpm(s->cfg.tpm.base);
122 }
123 #endif
124
125 /*
126 * We use the GPEX PCIe controller with its internal INTX PCI interrupt
127 * swizzling. This swizzling is emulated in QEMU and routes all INTX
128 * interrupts from endpoints down to only 4 INTX interrupts.
129 * See include/hw/pci/pci.h : pci_swizzle()
130 */
xenpvh_gpex_init(XenPVHMachineState * s,XenPVHMachineClass * xpc,MemoryRegion * sysmem)131 static inline void xenpvh_gpex_init(XenPVHMachineState *s,
132 XenPVHMachineClass *xpc,
133 MemoryRegion *sysmem)
134 {
135 MemoryRegion *ecam_reg;
136 MemoryRegion *mmio_reg;
137 DeviceState *dev;
138 int i;
139
140 object_initialize_child(OBJECT(s), "gpex", &s->pci.gpex,
141 TYPE_GPEX_HOST);
142 dev = DEVICE(&s->pci.gpex);
143 sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
144
145 ecam_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0);
146 memory_region_add_subregion(sysmem, s->cfg.pci_ecam.base, ecam_reg);
147
148 mmio_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1);
149
150 if (s->cfg.pci_mmio.size) {
151 memory_region_init_alias(&s->pci.mmio_alias, OBJECT(dev), "pcie-mmio",
152 mmio_reg,
153 s->cfg.pci_mmio.base, s->cfg.pci_mmio.size);
154 memory_region_add_subregion(sysmem, s->cfg.pci_mmio.base,
155 &s->pci.mmio_alias);
156 }
157
158 if (s->cfg.pci_mmio_high.size) {
159 memory_region_init_alias(&s->pci.mmio_high_alias, OBJECT(dev),
160 "pcie-mmio-high",
161 mmio_reg, s->cfg.pci_mmio_high.base, s->cfg.pci_mmio_high.size);
162 memory_region_add_subregion(sysmem, s->cfg.pci_mmio_high.base,
163 &s->pci.mmio_high_alias);
164 }
165
166 /*
167 * PVH implementations with PCI enabled must provide set_pci_intx_irq()
168 * and optionally an implementation of set_pci_link_route().
169 */
170 assert(xpc->set_pci_intx_irq);
171
172 for (i = 0; i < GPEX_NUM_IRQS; i++) {
173 qemu_irq irq = qemu_allocate_irq(xpc->set_pci_intx_irq, s, i);
174
175 sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, irq);
176 gpex_set_irq_num(GPEX_HOST(dev), i, s->cfg.pci_intx_irq_base + i);
177 if (xpc->set_pci_link_route) {
178 xpc->set_pci_link_route(i, s->cfg.pci_intx_irq_base + i);
179 }
180 }
181 }
182
xen_pvh_init(MachineState * ms)183 static void xen_pvh_init(MachineState *ms)
184 {
185 XenPVHMachineState *s = XEN_PVH_MACHINE(ms);
186 XenPVHMachineClass *xpc = XEN_PVH_MACHINE_GET_CLASS(s);
187 MemoryRegion *sysmem = get_system_memory();
188
189 if (ms->ram_size == 0) {
190 warn_report("%s: ram size not specified. QEMU machine started"
191 " without IOREQ (no emulated devices including virtio)",
192 MACHINE_CLASS(object_get_class(OBJECT(ms)))->desc);
193 return;
194 }
195
196 xen_pvh_init_ram(s, sysmem);
197 xen_register_ioreq(&s->ioreq, ms->smp.max_cpus,
198 xpc->handle_bufioreq,
199 &xen_memory_listener);
200
201 if (s->cfg.virtio_mmio_num) {
202 xen_create_virtio_mmio_devices(s);
203 }
204
205 #ifdef CONFIG_TPM
206 if (xpc->has_tpm) {
207 if (s->cfg.tpm.base) {
208 xen_enable_tpm(s);
209 } else {
210 warn_report("tpm-base-addr is not set. TPM will not be enabled");
211 }
212 }
213 #endif
214
215 /* Non-zero pci-ecam-size enables PCI. */
216 if (s->cfg.pci_ecam.size) {
217 if (s->cfg.pci_ecam.size != 256 * MiB) {
218 error_report("pci-ecam-size only supports values 0 or 0x10000000");
219 exit(EXIT_FAILURE);
220 }
221 if (!s->cfg.pci_intx_irq_base) {
222 error_report("PCI enabled but pci-intx-irq-base not set");
223 exit(EXIT_FAILURE);
224 }
225
226 xenpvh_gpex_init(s, xpc, sysmem);
227 }
228
229 /* Call the implementation specific init. */
230 if (xpc->init) {
231 xpc->init(ms);
232 }
233 }
234
235 #define XEN_PVH_PROP_MEMMAP_SETTER(n, f) \
236 static void xen_pvh_set_ ## n ## _ ## f(Object *obj, Visitor *v, \
237 const char *name, void *opaque, \
238 Error **errp) \
239 { \
240 XenPVHMachineState *xp = XEN_PVH_MACHINE(obj); \
241 uint64_t value; \
242 \
243 if (!visit_type_size(v, name, &value, errp)) { \
244 return; \
245 } \
246 xp->cfg.n.f = value; \
247 }
248
249 #define XEN_PVH_PROP_MEMMAP_GETTER(n, f) \
250 static void xen_pvh_get_ ## n ## _ ## f(Object *obj, Visitor *v, \
251 const char *name, void *opaque, \
252 Error **errp) \
253 { \
254 XenPVHMachineState *xp = XEN_PVH_MACHINE(obj); \
255 uint64_t value = xp->cfg.n.f; \
256 \
257 visit_type_uint64(v, name, &value, errp); \
258 }
259
260 #define XEN_PVH_PROP_MEMMAP_BASE(n) \
261 XEN_PVH_PROP_MEMMAP_SETTER(n, base) \
262 XEN_PVH_PROP_MEMMAP_GETTER(n, base) \
263
264 #define XEN_PVH_PROP_MEMMAP_SIZE(n) \
265 XEN_PVH_PROP_MEMMAP_SETTER(n, size) \
266 XEN_PVH_PROP_MEMMAP_GETTER(n, size)
267
268 #define XEN_PVH_PROP_MEMMAP(n) \
269 XEN_PVH_PROP_MEMMAP_BASE(n) \
270 XEN_PVH_PROP_MEMMAP_SIZE(n)
271
272 XEN_PVH_PROP_MEMMAP(ram_low)
XEN_PVH_PROP_MEMMAP(ram_high)273 XEN_PVH_PROP_MEMMAP(ram_high)
274 /* TPM only has a base-addr option. */
275 XEN_PVH_PROP_MEMMAP_BASE(tpm)
276 XEN_PVH_PROP_MEMMAP(virtio_mmio)
277 XEN_PVH_PROP_MEMMAP(pci_ecam)
278 XEN_PVH_PROP_MEMMAP(pci_mmio)
279 XEN_PVH_PROP_MEMMAP(pci_mmio_high)
280
281 static void xen_pvh_set_pci_intx_irq_base(Object *obj, Visitor *v,
282 const char *name, void *opaque,
283 Error **errp)
284 {
285 XenPVHMachineState *xp = XEN_PVH_MACHINE(obj);
286 uint32_t value;
287
288 if (!visit_type_uint32(v, name, &value, errp)) {
289 return;
290 }
291
292 xp->cfg.pci_intx_irq_base = value;
293 }
294
xen_pvh_get_pci_intx_irq_base(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)295 static void xen_pvh_get_pci_intx_irq_base(Object *obj, Visitor *v,
296 const char *name, void *opaque,
297 Error **errp)
298 {
299 XenPVHMachineState *xp = XEN_PVH_MACHINE(obj);
300 uint32_t value = xp->cfg.pci_intx_irq_base;
301
302 visit_type_uint32(v, name, &value, errp);
303 }
304
xen_pvh_class_setup_common_props(XenPVHMachineClass * xpc)305 void xen_pvh_class_setup_common_props(XenPVHMachineClass *xpc)
306 {
307 ObjectClass *oc = OBJECT_CLASS(xpc);
308 MachineClass *mc = MACHINE_CLASS(xpc);
309
310 #define OC_MEMMAP_PROP_BASE(c, prop_name, name) \
311 do { \
312 object_class_property_add(c, prop_name "-base", "uint64_t", \
313 xen_pvh_get_ ## name ## _base, \
314 xen_pvh_set_ ## name ## _base, NULL, NULL); \
315 object_class_property_set_description(oc, prop_name "-base", \
316 "Set base address for " prop_name); \
317 } while (0)
318
319 #define OC_MEMMAP_PROP_SIZE(c, prop_name, name) \
320 do { \
321 object_class_property_add(c, prop_name "-size", "uint64_t", \
322 xen_pvh_get_ ## name ## _size, \
323 xen_pvh_set_ ## name ## _size, NULL, NULL); \
324 object_class_property_set_description(oc, prop_name "-size", \
325 "Set memory range size for " prop_name); \
326 } while (0)
327
328 #define OC_MEMMAP_PROP(c, prop_name, name) \
329 do { \
330 OC_MEMMAP_PROP_BASE(c, prop_name, name); \
331 OC_MEMMAP_PROP_SIZE(c, prop_name, name); \
332 } while (0)
333
334 /*
335 * We provide memmap properties to allow Xen to move things to other
336 * addresses for example when users need to accomodate the memory-map
337 * for 1:1 mapped devices/memory.
338 */
339 OC_MEMMAP_PROP(oc, "ram-low", ram_low);
340 OC_MEMMAP_PROP(oc, "ram-high", ram_high);
341
342 if (xpc->has_virtio_mmio) {
343 OC_MEMMAP_PROP(oc, "virtio-mmio", virtio_mmio);
344 }
345
346 if (xpc->has_pci) {
347 OC_MEMMAP_PROP(oc, "pci-ecam", pci_ecam);
348 OC_MEMMAP_PROP(oc, "pci-mmio", pci_mmio);
349 OC_MEMMAP_PROP(oc, "pci-mmio-high", pci_mmio_high);
350
351 object_class_property_add(oc, "pci-intx-irq-base", "uint32_t",
352 xen_pvh_get_pci_intx_irq_base,
353 xen_pvh_set_pci_intx_irq_base,
354 NULL, NULL);
355 object_class_property_set_description(oc, "pci-intx-irq-base",
356 "Set PCI INTX interrupt base line.");
357 }
358
359 #ifdef CONFIG_TPM
360 if (xpc->has_tpm) {
361 object_class_property_add(oc, "tpm-base-addr", "uint64_t",
362 xen_pvh_get_tpm_base,
363 xen_pvh_set_tpm_base,
364 NULL, NULL);
365 object_class_property_set_description(oc, "tpm-base-addr",
366 "Set Base address for TPM device.");
367
368 machine_class_allow_dynamic_sysbus_dev(mc, TYPE_TPM_TIS_SYSBUS);
369 }
370 #endif
371 }
372
xen_pvh_class_init(ObjectClass * oc,void * data)373 static void xen_pvh_class_init(ObjectClass *oc, void *data)
374 {
375 MachineClass *mc = MACHINE_CLASS(oc);
376
377 mc->init = xen_pvh_init;
378
379 mc->desc = "Xen PVH machine";
380 mc->max_cpus = 1;
381 mc->default_machine_opts = "accel=xen";
382 /* Set to zero to make sure that the real ram size is passed. */
383 mc->default_ram_size = 0;
384 }
385
386 static const TypeInfo xen_pvh_info = {
387 .name = TYPE_XEN_PVH_MACHINE,
388 .parent = TYPE_MACHINE,
389 .abstract = true,
390 .instance_size = sizeof(XenPVHMachineState),
391 .class_size = sizeof(XenPVHMachineClass),
392 .class_init = xen_pvh_class_init,
393 };
394
xen_pvh_register_types(void)395 static void xen_pvh_register_types(void)
396 {
397 type_register_static(&xen_pvh_info);
398 }
399
400 type_init(xen_pvh_register_types);
401