1 /* 2 * AMD/Xilinx Versal family Virtual board. 3 * 4 * Copyright (c) 2018 Xilinx Inc. 5 * Copyright (c) 2025 Advanced Micro Devices, Inc. 6 * Written by Edgar E. Iglesias 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License version 2 or 10 * (at your option) any later version. 11 */ 12 13 #include "qemu/osdep.h" 14 #include "qemu/error-report.h" 15 #include "qapi/error.h" 16 #include "system/device_tree.h" 17 #include "system/address-spaces.h" 18 #include "hw/block/flash.h" 19 #include "hw/boards.h" 20 #include "hw/sysbus.h" 21 #include "hw/arm/fdt.h" 22 #include "hw/arm/xlnx-versal.h" 23 #include "hw/arm/boot.h" 24 #include "hw/arm/machines-qom.h" 25 #include "qom/object.h" 26 #include "target/arm/cpu.h" 27 28 #define TYPE_XLNX_VERSAL_VIRT_BASE_MACHINE \ 29 MACHINE_TYPE_NAME("amd-versal-virt-base") 30 OBJECT_DECLARE_TYPE(VersalVirt, VersalVirtClass, XLNX_VERSAL_VIRT_BASE_MACHINE) 31 32 #define TYPE_XLNX_VERSAL_VIRT_MACHINE MACHINE_TYPE_NAME("amd-versal-virt") 33 #define TYPE_XLNX_VERSAL2_VIRT_MACHINE MACHINE_TYPE_NAME("amd-versal2-virt") 34 35 #define XLNX_VERSAL_NUM_OSPI_FLASH 4 36 37 struct VersalVirt { 38 MachineState parent_obj; 39 40 Versal soc; 41 42 void *fdt; 43 int fdt_size; 44 struct arm_boot_info binfo; 45 46 CanBusState **canbus; 47 48 struct { 49 char *ospi_model; 50 } cfg; 51 }; 52 53 struct VersalVirtClass { 54 MachineClass parent_class; 55 56 VersalVersion version; 57 }; 58 59 static void fdt_create(VersalVirt *s) 60 { 61 MachineClass *mc = MACHINE_GET_CLASS(s); 62 VersalVirtClass *vvc = XLNX_VERSAL_VIRT_BASE_MACHINE_GET_CLASS(s); 63 const char versal_compat[] = "amd-versal-virt\0xlnx-versal-virt"; 64 const char versal2_compat[] = "amd-versal2-virt"; 65 66 s->fdt = create_device_tree(&s->fdt_size); 67 if (!s->fdt) { 68 error_report("create_device_tree() failed"); 69 exit(1); 70 } 71 72 /* Create /chosen node for load_dtb. */ 73 qemu_fdt_add_subnode(s->fdt, "/chosen"); 74 qemu_fdt_add_subnode(s->fdt, "/aliases"); 75 76 /* Header */ 77 qemu_fdt_setprop_string(s->fdt, "/", "model", mc->desc); 78 79 switch (vvc->version) { 80 case VERSAL_VER_VERSAL: 81 qemu_fdt_setprop(s->fdt, "/", "compatible", versal_compat, 82 sizeof(versal_compat)); 83 break; 84 85 case VERSAL_VER_VERSAL2: 86 qemu_fdt_setprop(s->fdt, "/", "compatible", versal2_compat, 87 sizeof(versal2_compat)); 88 break; 89 } 90 } 91 92 static void fdt_nop_memory_nodes(void *fdt, Error **errp) 93 { 94 Error *err = NULL; 95 char **node_path; 96 int n = 0; 97 98 node_path = qemu_fdt_node_unit_path(fdt, "memory", &err); 99 if (err) { 100 error_propagate(errp, err); 101 return; 102 } 103 while (node_path[n]) { 104 if (g_str_has_prefix(node_path[n], "/memory")) { 105 qemu_fdt_nop_node(fdt, node_path[n]); 106 } 107 n++; 108 } 109 g_strfreev(node_path); 110 } 111 112 static void versal_virt_modify_dtb(const struct arm_boot_info *binfo, 113 void *fdt) 114 { 115 VersalVirt *s = container_of(binfo, VersalVirt, binfo); 116 117 fdt_nop_memory_nodes(s->fdt, &error_abort); 118 versal_fdt_add_memory_nodes(&s->soc, binfo->ram_size); 119 } 120 121 static void *versal_virt_get_dtb(const struct arm_boot_info *binfo, 122 int *fdt_size) 123 { 124 const VersalVirt *board = container_of(binfo, VersalVirt, binfo); 125 126 *fdt_size = board->fdt_size; 127 return board->fdt; 128 } 129 130 #define NUM_VIRTIO_TRANSPORT 8 131 static void create_virtio_regions(VersalVirt *s) 132 { 133 int virtio_mmio_size = 0x200; 134 int i; 135 136 for (i = 0; i < NUM_VIRTIO_TRANSPORT; i++) { 137 hwaddr base = versal_get_reserved_mmio_addr(&s->soc) 138 + i * virtio_mmio_size; 139 g_autofree char *node = g_strdup_printf("/virtio_mmio@%" PRIx64, base); 140 int dtb_irq; 141 MemoryRegion *mr; 142 DeviceState *dev; 143 qemu_irq pic_irq; 144 145 pic_irq = versal_get_reserved_irq(&s->soc, i, &dtb_irq); 146 dev = qdev_new("virtio-mmio"); 147 object_property_add_child(OBJECT(s), "virtio-mmio[*]", OBJECT(dev)); 148 sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); 149 sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic_irq); 150 mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0); 151 memory_region_add_subregion(&s->soc.mr_ps, base, mr); 152 153 qemu_fdt_add_subnode(s->fdt, node); 154 qemu_fdt_setprop(s->fdt, node, "dma-coherent", NULL, 0); 155 qemu_fdt_setprop_cells(s->fdt, node, "interrupts", 156 GIC_FDT_IRQ_TYPE_SPI, dtb_irq, 157 GIC_FDT_IRQ_FLAGS_EDGE_LO_HI); 158 qemu_fdt_setprop_sized_cells(s->fdt, node, "reg", 159 2, base, 2, virtio_mmio_size); 160 qemu_fdt_setprop_string(s->fdt, node, "compatible", "virtio,mmio"); 161 } 162 } 163 164 static void bbram_attach_drive(VersalVirt *s) 165 { 166 DriveInfo *dinfo; 167 BlockBackend *blk; 168 169 dinfo = drive_get_by_index(IF_PFLASH, 0); 170 blk = dinfo ? blk_by_legacy_dinfo(dinfo) : NULL; 171 if (blk) { 172 versal_bbram_attach_drive(&s->soc, blk); 173 } 174 } 175 176 static void efuse_attach_drive(VersalVirt *s) 177 { 178 DriveInfo *dinfo; 179 BlockBackend *blk; 180 181 dinfo = drive_get_by_index(IF_PFLASH, 1); 182 blk = dinfo ? blk_by_legacy_dinfo(dinfo) : NULL; 183 if (blk) { 184 versal_efuse_attach_drive(&s->soc, blk); 185 } 186 } 187 188 static void sd_plug_card(VersalVirt *s, int idx, DriveInfo *di) 189 { 190 BlockBackend *blk = di ? blk_by_legacy_dinfo(di) : NULL; 191 192 versal_sdhci_plug_card(&s->soc, idx, blk); 193 } 194 195 static char *versal_get_ospi_model(Object *obj, Error **errp) 196 { 197 VersalVirt *s = XLNX_VERSAL_VIRT_BASE_MACHINE(obj); 198 199 return g_strdup(s->cfg.ospi_model); 200 } 201 202 static void versal_set_ospi_model(Object *obj, const char *value, Error **errp) 203 { 204 VersalVirt *s = XLNX_VERSAL_VIRT_BASE_MACHINE(obj); 205 206 g_free(s->cfg.ospi_model); 207 s->cfg.ospi_model = g_strdup(value); 208 } 209 210 211 static void versal_virt_init(MachineState *machine) 212 { 213 VersalVirt *s = XLNX_VERSAL_VIRT_BASE_MACHINE(machine); 214 VersalVirtClass *vvc = XLNX_VERSAL_VIRT_BASE_MACHINE_GET_CLASS(machine); 215 int psci_conduit = QEMU_PSCI_CONDUIT_DISABLED; 216 int i; 217 218 /* 219 * If the user provides an Operating System to be loaded, we expect them 220 * to use the -kernel command line option. 221 * 222 * Users can load firmware or boot-loaders with the -device loader options. 223 * 224 * When loading an OS, we generate a dtb and let arm_load_kernel() select 225 * where it gets loaded. This dtb will be passed to the kernel in x0. 226 * 227 * If there's no -kernel option, we generate a DTB and place it at 0x1000 228 * for the bootloaders or firmware to pick up. 229 * 230 * If users want to provide their own DTB, they can use the -dtb option. 231 * These dtb's will have their memory nodes modified to match QEMU's 232 * selected ram_size option before they get passed to the kernel or fw. 233 * 234 * When loading an OS, we turn on QEMU's PSCI implementation with SMC 235 * as the PSCI conduit. When there's no -kernel, we assume the user 236 * provides EL3 firmware to handle PSCI. 237 * 238 * Even if the user provides a kernel filename, arm_load_kernel() 239 * may suppress PSCI if it's going to boot that guest code at EL3. 240 */ 241 if (machine->kernel_filename) { 242 psci_conduit = QEMU_PSCI_CONDUIT_SMC; 243 } 244 245 object_initialize_child(OBJECT(machine), "xlnx-versal", &s->soc, 246 versal_get_class(vvc->version)); 247 object_property_set_link(OBJECT(&s->soc), "ddr", OBJECT(machine->ram), 248 &error_abort); 249 250 for (i = 0; i < versal_get_num_can(vvc->version); i++) { 251 g_autofree char *prop_name = g_strdup_printf("canbus%d", i); 252 253 object_property_set_link(OBJECT(&s->soc), prop_name, 254 OBJECT(s->canbus[i]), 255 &error_abort); 256 } 257 258 fdt_create(s); 259 versal_set_fdt(&s->soc, s->fdt); 260 sysbus_realize(SYS_BUS_DEVICE(&s->soc), &error_fatal); 261 create_virtio_regions(s); 262 263 /* 264 * Map the SoC address space onto system memory. This will allow virtio and 265 * other modules unaware of multiple address-spaces to work. 266 */ 267 memory_region_add_subregion(get_system_memory(), 0, &s->soc.mr_ps); 268 269 /* Attach bbram backend, if given */ 270 bbram_attach_drive(s); 271 272 /* Attach efuse backend, if given */ 273 efuse_attach_drive(s); 274 275 /* Plug SD cards */ 276 for (i = 0; i < versal_get_num_sdhci(vvc->version); i++) { 277 sd_plug_card(s, i, drive_get(IF_SD, 0, i)); 278 } 279 280 s->binfo.ram_size = machine->ram_size; 281 s->binfo.loader_start = 0x0; 282 s->binfo.get_dtb = versal_virt_get_dtb; 283 s->binfo.modify_dtb = versal_virt_modify_dtb; 284 s->binfo.psci_conduit = psci_conduit; 285 if (!machine->kernel_filename) { 286 /* Some boot-loaders (e.g u-boot) don't like blobs at address 0 (NULL). 287 * Offset things by 4K. */ 288 s->binfo.loader_start = 0x1000; 289 s->binfo.dtb_limit = 0x1000000; 290 } 291 arm_load_kernel(ARM_CPU(versal_get_boot_cpu(&s->soc)), machine, &s->binfo); 292 293 for (i = 0; i < XLNX_VERSAL_NUM_OSPI_FLASH; i++) { 294 ObjectClass *flash_klass; 295 DriveInfo *dinfo = drive_get(IF_MTD, 0, i); 296 BlockBackend *blk; 297 const char *mdl; 298 299 if (s->cfg.ospi_model) { 300 flash_klass = object_class_by_name(s->cfg.ospi_model); 301 if (!flash_klass || 302 object_class_is_abstract(flash_klass) || 303 !object_class_dynamic_cast(flash_klass, TYPE_M25P80)) { 304 error_report("'%s' is either abstract or" 305 " not a subtype of m25p80", s->cfg.ospi_model); 306 exit(1); 307 } 308 mdl = s->cfg.ospi_model; 309 } else { 310 mdl = "mt35xu01g"; 311 } 312 313 blk = dinfo ? blk_by_legacy_dinfo(dinfo) : NULL; 314 versal_ospi_create_flash(&s->soc, i, mdl, blk); 315 } 316 } 317 318 static void versal_virt_machine_instance_init(Object *obj) 319 { 320 VersalVirt *s = XLNX_VERSAL_VIRT_BASE_MACHINE(obj); 321 VersalVirtClass *vvc = XLNX_VERSAL_VIRT_BASE_MACHINE_GET_CLASS(s); 322 size_t i, num_can; 323 324 num_can = versal_get_num_can(vvc->version); 325 s->canbus = g_new0(CanBusState *, num_can); 326 327 /* 328 * User can set canbusx properties to can-bus object and optionally connect 329 * to socketcan interface via command line. 330 */ 331 for (i = 0; i < num_can; i++) { 332 g_autofree char *prop_name = g_strdup_printf("canbus%zu", i); 333 334 object_property_add_link(obj, prop_name, TYPE_CAN_BUS, 335 (Object **) &s->canbus[i], 336 object_property_allow_set_link, 0); 337 } 338 } 339 340 static void versal_virt_machine_finalize(Object *obj) 341 { 342 VersalVirt *s = XLNX_VERSAL_VIRT_BASE_MACHINE(obj); 343 344 g_free(s->cfg.ospi_model); 345 g_free(s->canbus); 346 } 347 348 static void versal_virt_machine_class_init_common(ObjectClass *oc) 349 { 350 MachineClass *mc = MACHINE_CLASS(oc); 351 VersalVirtClass *vvc = XLNX_VERSAL_VIRT_BASE_MACHINE_CLASS(mc); 352 int num_cpu = versal_get_num_cpu(vvc->version); 353 354 mc->no_cdrom = true; 355 mc->auto_create_sdcard = true; 356 mc->default_ram_id = "ddr"; 357 mc->min_cpus = num_cpu; 358 mc->max_cpus = num_cpu; 359 mc->default_cpus = num_cpu; 360 mc->init = versal_virt_init; 361 362 object_class_property_add_str(oc, "ospi-flash", versal_get_ospi_model, 363 versal_set_ospi_model); 364 object_class_property_set_description(oc, "ospi-flash", 365 "Change the OSPI Flash model"); 366 } 367 368 static void versal_virt_machine_class_init(ObjectClass *oc, const void *data) 369 { 370 MachineClass *mc = MACHINE_CLASS(oc); 371 VersalVirtClass *vvc = XLNX_VERSAL_VIRT_BASE_MACHINE_CLASS(oc); 372 373 mc->desc = "AMD Versal Virtual development board"; 374 mc->alias = "xlnx-versal-virt"; 375 vvc->version = VERSAL_VER_VERSAL; 376 377 versal_virt_machine_class_init_common(oc); 378 } 379 380 static void versal2_virt_machine_class_init(ObjectClass *oc, const void *data) 381 { 382 MachineClass *mc = MACHINE_CLASS(oc); 383 VersalVirtClass *vvc = XLNX_VERSAL_VIRT_BASE_MACHINE_CLASS(oc); 384 385 mc->desc = "AMD Versal Gen 2 Virtual development board"; 386 vvc->version = VERSAL_VER_VERSAL2; 387 388 versal_virt_machine_class_init_common(oc); 389 } 390 391 static const TypeInfo versal_virt_base_machine_init_typeinfo = { 392 .name = TYPE_XLNX_VERSAL_VIRT_BASE_MACHINE, 393 .parent = TYPE_MACHINE, 394 .class_size = sizeof(VersalVirtClass), 395 .instance_init = versal_virt_machine_instance_init, 396 .instance_size = sizeof(VersalVirt), 397 .instance_finalize = versal_virt_machine_finalize, 398 .abstract = true, 399 }; 400 401 static const TypeInfo versal_virt_machine_init_typeinfo = { 402 .name = TYPE_XLNX_VERSAL_VIRT_MACHINE, 403 .parent = TYPE_XLNX_VERSAL_VIRT_BASE_MACHINE, 404 .class_init = versal_virt_machine_class_init, 405 .interfaces = aarch64_machine_interfaces, 406 }; 407 408 static const TypeInfo versal2_virt_machine_init_typeinfo = { 409 .name = TYPE_XLNX_VERSAL2_VIRT_MACHINE, 410 .parent = TYPE_XLNX_VERSAL_VIRT_BASE_MACHINE, 411 .class_init = versal2_virt_machine_class_init, 412 .interfaces = aarch64_machine_interfaces, 413 }; 414 415 static void versal_virt_machine_init_register_types(void) 416 { 417 type_register_static(&versal_virt_base_machine_init_typeinfo); 418 type_register_static(&versal_virt_machine_init_typeinfo); 419 type_register_static(&versal2_virt_machine_init_typeinfo); 420 } 421 422 type_init(versal_virt_machine_init_register_types) 423