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