qemu/hw/arm/xlnx-versal-virt.c
<<
>>
Prefs
   1/*
   2 * Xilinx Versal Virtual board.
   3 *
   4 * Copyright (c) 2018 Xilinx Inc.
   5 * Written by Edgar E. Iglesias
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License version 2 or
   9 * (at your option) any later version.
  10 */
  11
  12#include "qemu/osdep.h"
  13#include "qemu/log.h"
  14#include "qemu/error-report.h"
  15#include "qapi/error.h"
  16#include "sysemu/device_tree.h"
  17#include "exec/address-spaces.h"
  18#include "hw/boards.h"
  19#include "hw/sysbus.h"
  20#include "hw/arm/sysbus-fdt.h"
  21#include "hw/arm/fdt.h"
  22#include "cpu.h"
  23#include "hw/arm/xlnx-versal.h"
  24
  25#define TYPE_XLNX_VERSAL_VIRT_MACHINE MACHINE_TYPE_NAME("xlnx-versal-virt")
  26#define XLNX_VERSAL_VIRT_MACHINE(obj) \
  27    OBJECT_CHECK(VersalVirt, (obj), TYPE_XLNX_VERSAL_VIRT_MACHINE)
  28
  29typedef struct VersalVirt {
  30    MachineState parent_obj;
  31
  32    Versal soc;
  33    MemoryRegion mr_ddr;
  34
  35    void *fdt;
  36    int fdt_size;
  37    struct {
  38        uint32_t gic;
  39        uint32_t ethernet_phy[2];
  40        uint32_t clk_125Mhz;
  41        uint32_t clk_25Mhz;
  42    } phandle;
  43    struct arm_boot_info binfo;
  44
  45    struct {
  46        bool secure;
  47    } cfg;
  48} VersalVirt;
  49
  50static void fdt_create(VersalVirt *s)
  51{
  52    MachineClass *mc = MACHINE_GET_CLASS(s);
  53    int i;
  54
  55    s->fdt = create_device_tree(&s->fdt_size);
  56    if (!s->fdt) {
  57        error_report("create_device_tree() failed");
  58        exit(1);
  59    }
  60
  61    /* Allocate all phandles.  */
  62    s->phandle.gic = qemu_fdt_alloc_phandle(s->fdt);
  63    for (i = 0; i < ARRAY_SIZE(s->phandle.ethernet_phy); i++) {
  64        s->phandle.ethernet_phy[i] = qemu_fdt_alloc_phandle(s->fdt);
  65    }
  66    s->phandle.clk_25Mhz = qemu_fdt_alloc_phandle(s->fdt);
  67    s->phandle.clk_125Mhz = qemu_fdt_alloc_phandle(s->fdt);
  68
  69    /* Create /chosen node for load_dtb.  */
  70    qemu_fdt_add_subnode(s->fdt, "/chosen");
  71
  72    /* Header */
  73    qemu_fdt_setprop_cell(s->fdt, "/", "interrupt-parent", s->phandle.gic);
  74    qemu_fdt_setprop_cell(s->fdt, "/", "#size-cells", 0x2);
  75    qemu_fdt_setprop_cell(s->fdt, "/", "#address-cells", 0x2);
  76    qemu_fdt_setprop_string(s->fdt, "/", "model", mc->desc);
  77    qemu_fdt_setprop_string(s->fdt, "/", "compatible", "xlnx-versal-virt");
  78}
  79
  80static void fdt_add_clk_node(VersalVirt *s, const char *name,
  81                             unsigned int freq_hz, uint32_t phandle)
  82{
  83    qemu_fdt_add_subnode(s->fdt, name);
  84    qemu_fdt_setprop_cell(s->fdt, name, "phandle", phandle);
  85    qemu_fdt_setprop_cell(s->fdt, name, "clock-frequency", freq_hz);
  86    qemu_fdt_setprop_cell(s->fdt, name, "#clock-cells", 0x0);
  87    qemu_fdt_setprop_string(s->fdt, name, "compatible", "fixed-clock");
  88    qemu_fdt_setprop(s->fdt, name, "u-boot,dm-pre-reloc", NULL, 0);
  89}
  90
  91static void fdt_add_cpu_nodes(VersalVirt *s, uint32_t psci_conduit)
  92{
  93    int i;
  94
  95    qemu_fdt_add_subnode(s->fdt, "/cpus");
  96    qemu_fdt_setprop_cell(s->fdt, "/cpus", "#size-cells", 0x0);
  97    qemu_fdt_setprop_cell(s->fdt, "/cpus", "#address-cells", 1);
  98
  99    for (i = XLNX_VERSAL_NR_ACPUS - 1; i >= 0; i--) {
 100        char *name = g_strdup_printf("/cpus/cpu@%d", i);
 101        ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(i));
 102
 103        qemu_fdt_add_subnode(s->fdt, name);
 104        qemu_fdt_setprop_cell(s->fdt, name, "reg", armcpu->mp_affinity);
 105        if (psci_conduit != QEMU_PSCI_CONDUIT_DISABLED) {
 106            qemu_fdt_setprop_string(s->fdt, name, "enable-method", "psci");
 107        }
 108        qemu_fdt_setprop_string(s->fdt, name, "device_type", "cpu");
 109        qemu_fdt_setprop_string(s->fdt, name, "compatible",
 110                                armcpu->dtb_compatible);
 111        g_free(name);
 112    }
 113}
 114
 115static void fdt_add_gic_nodes(VersalVirt *s)
 116{
 117    char *nodename;
 118
 119    nodename = g_strdup_printf("/gic@%x", MM_GIC_APU_DIST_MAIN);
 120    qemu_fdt_add_subnode(s->fdt, nodename);
 121    qemu_fdt_setprop_cell(s->fdt, nodename, "phandle", s->phandle.gic);
 122    qemu_fdt_setprop_cells(s->fdt, nodename, "interrupts",
 123                           GIC_FDT_IRQ_TYPE_PPI, VERSAL_GIC_MAINT_IRQ,
 124                           GIC_FDT_IRQ_FLAGS_LEVEL_HI);
 125    qemu_fdt_setprop(s->fdt, nodename, "interrupt-controller", NULL, 0);
 126    qemu_fdt_setprop_sized_cells(s->fdt, nodename, "reg",
 127                                 2, MM_GIC_APU_DIST_MAIN,
 128                                 2, MM_GIC_APU_DIST_MAIN_SIZE,
 129                                 2, MM_GIC_APU_REDIST_0,
 130                                 2, MM_GIC_APU_REDIST_0_SIZE);
 131    qemu_fdt_setprop_cell(s->fdt, nodename, "#interrupt-cells", 3);
 132    qemu_fdt_setprop_string(s->fdt, nodename, "compatible", "arm,gic-v3");
 133}
 134
 135static void fdt_add_timer_nodes(VersalVirt *s)
 136{
 137    const char compat[] = "arm,armv8-timer";
 138    uint32_t irqflags = GIC_FDT_IRQ_FLAGS_LEVEL_HI;
 139
 140    qemu_fdt_add_subnode(s->fdt, "/timer");
 141    qemu_fdt_setprop_cells(s->fdt, "/timer", "interrupts",
 142            GIC_FDT_IRQ_TYPE_PPI, VERSAL_TIMER_S_EL1_IRQ, irqflags,
 143            GIC_FDT_IRQ_TYPE_PPI, VERSAL_TIMER_NS_EL1_IRQ, irqflags,
 144            GIC_FDT_IRQ_TYPE_PPI, VERSAL_TIMER_VIRT_IRQ, irqflags,
 145            GIC_FDT_IRQ_TYPE_PPI, VERSAL_TIMER_NS_EL2_IRQ, irqflags);
 146    qemu_fdt_setprop(s->fdt, "/timer", "compatible",
 147                     compat, sizeof(compat));
 148}
 149
 150static void fdt_add_uart_nodes(VersalVirt *s)
 151{
 152    uint64_t addrs[] = { MM_UART1, MM_UART0 };
 153    unsigned int irqs[] = { VERSAL_UART1_IRQ_0, VERSAL_UART0_IRQ_0 };
 154    const char compat[] = "arm,pl011\0arm,sbsa-uart";
 155    const char clocknames[] = "uartclk\0apb_pclk";
 156    int i;
 157
 158    for (i = 0; i < ARRAY_SIZE(addrs); i++) {
 159        char *name = g_strdup_printf("/uart@%" PRIx64, addrs[i]);
 160        qemu_fdt_add_subnode(s->fdt, name);
 161        qemu_fdt_setprop_cell(s->fdt, name, "current-speed", 115200);
 162        qemu_fdt_setprop_cells(s->fdt, name, "clocks",
 163                               s->phandle.clk_125Mhz, s->phandle.clk_125Mhz);
 164        qemu_fdt_setprop(s->fdt, name, "clock-names",
 165                         clocknames, sizeof(clocknames));
 166
 167        qemu_fdt_setprop_cells(s->fdt, name, "interrupts",
 168                               GIC_FDT_IRQ_TYPE_SPI, irqs[i],
 169                               GIC_FDT_IRQ_FLAGS_LEVEL_HI);
 170        qemu_fdt_setprop_sized_cells(s->fdt, name, "reg",
 171                                     2, addrs[i], 2, 0x1000);
 172        qemu_fdt_setprop(s->fdt, name, "compatible",
 173                         compat, sizeof(compat));
 174        qemu_fdt_setprop(s->fdt, name, "u-boot,dm-pre-reloc", NULL, 0);
 175
 176        if (addrs[i] == MM_UART0) {
 177            /* Select UART0.  */
 178            qemu_fdt_setprop_string(s->fdt, "/chosen", "stdout-path", name);
 179        }
 180        g_free(name);
 181    }
 182}
 183
 184static void fdt_add_fixed_link_nodes(VersalVirt *s, char *gemname,
 185                                     uint32_t phandle)
 186{
 187    char *name = g_strdup_printf("%s/fixed-link", gemname);
 188
 189    qemu_fdt_add_subnode(s->fdt, name);
 190    qemu_fdt_setprop_cell(s->fdt, name, "phandle", phandle);
 191    qemu_fdt_setprop(s->fdt, name, "full-duplex", NULL, 0);
 192    qemu_fdt_setprop_cell(s->fdt, name, "speed", 1000);
 193    g_free(name);
 194}
 195
 196static void fdt_add_gem_nodes(VersalVirt *s)
 197{
 198    uint64_t addrs[] = { MM_GEM1, MM_GEM0 };
 199    unsigned int irqs[] = { VERSAL_GEM1_IRQ_0, VERSAL_GEM0_IRQ_0 };
 200    const char clocknames[] = "pclk\0hclk\0tx_clk\0rx_clk";
 201    const char compat_gem[] = "cdns,zynqmp-gem\0cdns,gem";
 202    int i;
 203
 204    for (i = 0; i < ARRAY_SIZE(addrs); i++) {
 205        char *name = g_strdup_printf("/ethernet@%" PRIx64, addrs[i]);
 206        qemu_fdt_add_subnode(s->fdt, name);
 207
 208        fdt_add_fixed_link_nodes(s, name, s->phandle.ethernet_phy[i]);
 209        qemu_fdt_setprop_string(s->fdt, name, "phy-mode", "rgmii-id");
 210        qemu_fdt_setprop_cell(s->fdt, name, "phy-handle",
 211                              s->phandle.ethernet_phy[i]);
 212        qemu_fdt_setprop_cells(s->fdt, name, "clocks",
 213                               s->phandle.clk_25Mhz, s->phandle.clk_25Mhz,
 214                               s->phandle.clk_25Mhz, s->phandle.clk_25Mhz);
 215        qemu_fdt_setprop(s->fdt, name, "clock-names",
 216                         clocknames, sizeof(clocknames));
 217        qemu_fdt_setprop_cells(s->fdt, name, "interrupts",
 218                               GIC_FDT_IRQ_TYPE_SPI, irqs[i],
 219                               GIC_FDT_IRQ_FLAGS_LEVEL_HI,
 220                               GIC_FDT_IRQ_TYPE_SPI, irqs[i],
 221                               GIC_FDT_IRQ_FLAGS_LEVEL_HI);
 222        qemu_fdt_setprop_sized_cells(s->fdt, name, "reg",
 223                                     2, addrs[i], 2, 0x1000);
 224        qemu_fdt_setprop(s->fdt, name, "compatible",
 225                         compat_gem, sizeof(compat_gem));
 226        qemu_fdt_setprop_cell(s->fdt, name, "#address-cells", 1);
 227        qemu_fdt_setprop_cell(s->fdt, name, "#size-cells", 0);
 228        g_free(name);
 229    }
 230}
 231
 232static void fdt_nop_memory_nodes(void *fdt, Error **errp)
 233{
 234    Error *err = NULL;
 235    char **node_path;
 236    int n = 0;
 237
 238    node_path = qemu_fdt_node_unit_path(fdt, "memory", &err);
 239    if (err) {
 240        error_propagate(errp, err);
 241        return;
 242    }
 243    while (node_path[n]) {
 244        if (g_str_has_prefix(node_path[n], "/memory")) {
 245            qemu_fdt_nop_node(fdt, node_path[n]);
 246        }
 247        n++;
 248    }
 249    g_strfreev(node_path);
 250}
 251
 252static void fdt_add_memory_nodes(VersalVirt *s, void *fdt, uint64_t ram_size)
 253{
 254    /* Describes the various split DDR access regions.  */
 255    static const struct {
 256        uint64_t base;
 257        uint64_t size;
 258    } addr_ranges[] = {
 259        { MM_TOP_DDR, MM_TOP_DDR_SIZE },
 260        { MM_TOP_DDR_2, MM_TOP_DDR_2_SIZE },
 261        { MM_TOP_DDR_3, MM_TOP_DDR_3_SIZE },
 262        { MM_TOP_DDR_4, MM_TOP_DDR_4_SIZE }
 263    };
 264    uint64_t mem_reg_prop[8] = {0};
 265    uint64_t size = ram_size;
 266    Error *err = NULL;
 267    char *name;
 268    int i;
 269
 270    fdt_nop_memory_nodes(fdt, &err);
 271    if (err) {
 272        error_report_err(err);
 273        return;
 274    }
 275
 276    name = g_strdup_printf("/memory@%x", MM_TOP_DDR);
 277    for (i = 0; i < ARRAY_SIZE(addr_ranges) && size; i++) {
 278        uint64_t mapsize;
 279
 280        mapsize = size < addr_ranges[i].size ? size : addr_ranges[i].size;
 281
 282        mem_reg_prop[i * 2] = addr_ranges[i].base;
 283        mem_reg_prop[i * 2 + 1] = mapsize;
 284        size -= mapsize;
 285    }
 286    qemu_fdt_add_subnode(fdt, name);
 287    qemu_fdt_setprop_string(fdt, name, "device_type", "memory");
 288
 289    switch (i) {
 290    case 1:
 291        qemu_fdt_setprop_sized_cells(fdt, name, "reg",
 292                                     2, mem_reg_prop[0],
 293                                     2, mem_reg_prop[1]);
 294        break;
 295    case 2:
 296        qemu_fdt_setprop_sized_cells(fdt, name, "reg",
 297                                     2, mem_reg_prop[0],
 298                                     2, mem_reg_prop[1],
 299                                     2, mem_reg_prop[2],
 300                                     2, mem_reg_prop[3]);
 301        break;
 302    case 3:
 303        qemu_fdt_setprop_sized_cells(fdt, name, "reg",
 304                                     2, mem_reg_prop[0],
 305                                     2, mem_reg_prop[1],
 306                                     2, mem_reg_prop[2],
 307                                     2, mem_reg_prop[3],
 308                                     2, mem_reg_prop[4],
 309                                     2, mem_reg_prop[5]);
 310        break;
 311    case 4:
 312        qemu_fdt_setprop_sized_cells(fdt, name, "reg",
 313                                     2, mem_reg_prop[0],
 314                                     2, mem_reg_prop[1],
 315                                     2, mem_reg_prop[2],
 316                                     2, mem_reg_prop[3],
 317                                     2, mem_reg_prop[4],
 318                                     2, mem_reg_prop[5],
 319                                     2, mem_reg_prop[6],
 320                                     2, mem_reg_prop[7]);
 321        break;
 322    default:
 323        g_assert_not_reached();
 324    }
 325    g_free(name);
 326}
 327
 328static void versal_virt_modify_dtb(const struct arm_boot_info *binfo,
 329                                    void *fdt)
 330{
 331    VersalVirt *s = container_of(binfo, VersalVirt, binfo);
 332
 333    fdt_add_memory_nodes(s, fdt, binfo->ram_size);
 334}
 335
 336static void *versal_virt_get_dtb(const struct arm_boot_info *binfo,
 337                                  int *fdt_size)
 338{
 339    const VersalVirt *board = container_of(binfo, VersalVirt, binfo);
 340
 341    *fdt_size = board->fdt_size;
 342    return board->fdt;
 343}
 344
 345#define NUM_VIRTIO_TRANSPORT 32
 346static void create_virtio_regions(VersalVirt *s)
 347{
 348    int virtio_mmio_size = 0x200;
 349    int i;
 350
 351    for (i = 0; i < NUM_VIRTIO_TRANSPORT; i++) {
 352        char *name = g_strdup_printf("virtio%d", i);;
 353        hwaddr base = MM_TOP_RSVD + i * virtio_mmio_size;
 354        int irq = VERSAL_RSVD_HIGH_IRQ_FIRST + i;
 355        MemoryRegion *mr;
 356        DeviceState *dev;
 357        qemu_irq pic_irq;
 358
 359        pic_irq = qdev_get_gpio_in(DEVICE(&s->soc.fpd.apu.gic), irq);
 360        dev = qdev_create(NULL, "virtio-mmio");
 361        object_property_add_child(OBJECT(&s->soc), name, OBJECT(dev),
 362                                  &error_fatal);
 363        qdev_init_nofail(dev);
 364        sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic_irq);
 365        mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0);
 366        memory_region_add_subregion(&s->soc.mr_ps, base, mr);
 367        sysbus_create_simple("virtio-mmio", base, pic_irq);
 368    }
 369
 370    for (i = 0; i < NUM_VIRTIO_TRANSPORT; i++) {
 371        hwaddr base = MM_TOP_RSVD + i * virtio_mmio_size;
 372        int irq = VERSAL_RSVD_HIGH_IRQ_FIRST + i;
 373        char *name = g_strdup_printf("/virtio_mmio@%" PRIx64, base);
 374
 375        qemu_fdt_add_subnode(s->fdt, name);
 376        qemu_fdt_setprop(s->fdt, name, "dma-coherent", NULL, 0);
 377        qemu_fdt_setprop_cells(s->fdt, name, "interrupts",
 378                               GIC_FDT_IRQ_TYPE_SPI, irq,
 379                               GIC_FDT_IRQ_FLAGS_EDGE_LO_HI);
 380        qemu_fdt_setprop_sized_cells(s->fdt, name, "reg",
 381                                     2, base, 2, virtio_mmio_size);
 382        qemu_fdt_setprop_string(s->fdt, name, "compatible", "virtio,mmio");
 383        g_free(name);
 384    }
 385}
 386
 387static void versal_virt_init(MachineState *machine)
 388{
 389    VersalVirt *s = XLNX_VERSAL_VIRT_MACHINE(machine);
 390    int psci_conduit = QEMU_PSCI_CONDUIT_DISABLED;
 391
 392    /*
 393     * If the user provides an Operating System to be loaded, we expect them
 394     * to use the -kernel command line option.
 395     *
 396     * Users can load firmware or boot-loaders with the -device loader options.
 397     *
 398     * When loading an OS, we generate a dtb and let arm_load_kernel() select
 399     * where it gets loaded. This dtb will be passed to the kernel in x0.
 400     *
 401     * If there's no -kernel option, we generate a DTB and place it at 0x1000
 402     * for the bootloaders or firmware to pick up.
 403     *
 404     * If users want to provide their own DTB, they can use the -dtb option.
 405     * These dtb's will have their memory nodes modified to match QEMU's
 406     * selected ram_size option before they get passed to the kernel or fw.
 407     *
 408     * When loading an OS, we turn on QEMU's PSCI implementation with SMC
 409     * as the PSCI conduit. When there's no -kernel, we assume the user
 410     * provides EL3 firmware to handle PSCI.
 411     */
 412    if (machine->kernel_filename) {
 413        psci_conduit = QEMU_PSCI_CONDUIT_SMC;
 414    }
 415
 416    memory_region_allocate_system_memory(&s->mr_ddr, NULL, "ddr",
 417                                         machine->ram_size);
 418
 419    sysbus_init_child_obj(OBJECT(machine), "xlnx-ve", &s->soc,
 420                          sizeof(s->soc), TYPE_XLNX_VERSAL);
 421    object_property_set_link(OBJECT(&s->soc), OBJECT(&s->mr_ddr),
 422                             "ddr", &error_abort);
 423    object_property_set_int(OBJECT(&s->soc), psci_conduit,
 424                            "psci-conduit", &error_abort);
 425    object_property_set_bool(OBJECT(&s->soc), true, "realized", &error_fatal);
 426
 427    fdt_create(s);
 428    create_virtio_regions(s);
 429    fdt_add_gem_nodes(s);
 430    fdt_add_uart_nodes(s);
 431    fdt_add_gic_nodes(s);
 432    fdt_add_timer_nodes(s);
 433    fdt_add_cpu_nodes(s, psci_conduit);
 434    fdt_add_clk_node(s, "/clk125", 125000000, s->phandle.clk_125Mhz);
 435    fdt_add_clk_node(s, "/clk25", 25000000, s->phandle.clk_25Mhz);
 436
 437    /* Make the APU cpu address space visible to virtio and other
 438     * modules unaware of muliple address-spaces.  */
 439    memory_region_add_subregion_overlap(get_system_memory(),
 440                                        0, &s->soc.fpd.apu.mr, 0);
 441
 442    s->binfo.ram_size = machine->ram_size;
 443    s->binfo.kernel_filename = machine->kernel_filename;
 444    s->binfo.kernel_cmdline = machine->kernel_cmdline;
 445    s->binfo.initrd_filename = machine->initrd_filename;
 446    s->binfo.loader_start = 0x0;
 447    s->binfo.get_dtb = versal_virt_get_dtb;
 448    s->binfo.modify_dtb = versal_virt_modify_dtb;
 449    if (machine->kernel_filename) {
 450        arm_load_kernel(s->soc.fpd.apu.cpu[0], &s->binfo);
 451    } else {
 452        AddressSpace *as = arm_boot_address_space(s->soc.fpd.apu.cpu[0],
 453                                                  &s->binfo);
 454        /* Some boot-loaders (e.g u-boot) don't like blobs at address 0 (NULL).
 455         * Offset things by 4K.  */
 456        s->binfo.loader_start = 0x1000;
 457        s->binfo.dtb_limit = 0x1000000;
 458        if (arm_load_dtb(s->binfo.loader_start,
 459                         &s->binfo, s->binfo.dtb_limit, as) < 0) {
 460            exit(EXIT_FAILURE);
 461        }
 462    }
 463}
 464
 465static void versal_virt_machine_instance_init(Object *obj)
 466{
 467}
 468
 469static void versal_virt_machine_class_init(ObjectClass *oc, void *data)
 470{
 471    MachineClass *mc = MACHINE_CLASS(oc);
 472
 473    mc->desc = "Xilinx Versal Virtual development board";
 474    mc->init = versal_virt_init;
 475    mc->max_cpus = XLNX_VERSAL_NR_ACPUS;
 476    mc->default_cpus = XLNX_VERSAL_NR_ACPUS;
 477    mc->no_cdrom = true;
 478}
 479
 480static const TypeInfo versal_virt_machine_init_typeinfo = {
 481    .name       = TYPE_XLNX_VERSAL_VIRT_MACHINE,
 482    .parent     = TYPE_MACHINE,
 483    .class_init = versal_virt_machine_class_init,
 484    .instance_init = versal_virt_machine_instance_init,
 485    .instance_size = sizeof(VersalVirt),
 486};
 487
 488static void versal_virt_machine_init_register_types(void)
 489{
 490    type_register_static(&versal_virt_machine_init_typeinfo);
 491}
 492
 493type_init(versal_virt_machine_init_register_types)
 494
 495