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    g_free(nodename);
 134}
 135
 136static void fdt_add_timer_nodes(VersalVirt *s)
 137{
 138    const char compat[] = "arm,armv8-timer";
 139    uint32_t irqflags = GIC_FDT_IRQ_FLAGS_LEVEL_HI;
 140
 141    qemu_fdt_add_subnode(s->fdt, "/timer");
 142    qemu_fdt_setprop_cells(s->fdt, "/timer", "interrupts",
 143            GIC_FDT_IRQ_TYPE_PPI, VERSAL_TIMER_S_EL1_IRQ, irqflags,
 144            GIC_FDT_IRQ_TYPE_PPI, VERSAL_TIMER_NS_EL1_IRQ, irqflags,
 145            GIC_FDT_IRQ_TYPE_PPI, VERSAL_TIMER_VIRT_IRQ, irqflags,
 146            GIC_FDT_IRQ_TYPE_PPI, VERSAL_TIMER_NS_EL2_IRQ, irqflags);
 147    qemu_fdt_setprop(s->fdt, "/timer", "compatible",
 148                     compat, sizeof(compat));
 149}
 150
 151static void fdt_add_uart_nodes(VersalVirt *s)
 152{
 153    uint64_t addrs[] = { MM_UART1, MM_UART0 };
 154    unsigned int irqs[] = { VERSAL_UART1_IRQ_0, VERSAL_UART0_IRQ_0 };
 155    const char compat[] = "arm,pl011\0arm,sbsa-uart";
 156    const char clocknames[] = "uartclk\0apb_pclk";
 157    int i;
 158
 159    for (i = 0; i < ARRAY_SIZE(addrs); i++) {
 160        char *name = g_strdup_printf("/uart@%" PRIx64, addrs[i]);
 161        qemu_fdt_add_subnode(s->fdt, name);
 162        qemu_fdt_setprop_cell(s->fdt, name, "current-speed", 115200);
 163        qemu_fdt_setprop_cells(s->fdt, name, "clocks",
 164                               s->phandle.clk_125Mhz, s->phandle.clk_125Mhz);
 165        qemu_fdt_setprop(s->fdt, name, "clock-names",
 166                         clocknames, sizeof(clocknames));
 167
 168        qemu_fdt_setprop_cells(s->fdt, name, "interrupts",
 169                               GIC_FDT_IRQ_TYPE_SPI, irqs[i],
 170                               GIC_FDT_IRQ_FLAGS_LEVEL_HI);
 171        qemu_fdt_setprop_sized_cells(s->fdt, name, "reg",
 172                                     2, addrs[i], 2, 0x1000);
 173        qemu_fdt_setprop(s->fdt, name, "compatible",
 174                         compat, sizeof(compat));
 175        qemu_fdt_setprop(s->fdt, name, "u-boot,dm-pre-reloc", NULL, 0);
 176
 177        if (addrs[i] == MM_UART0) {
 178            /* Select UART0.  */
 179            qemu_fdt_setprop_string(s->fdt, "/chosen", "stdout-path", name);
 180        }
 181        g_free(name);
 182    }
 183}
 184
 185static void fdt_add_fixed_link_nodes(VersalVirt *s, char *gemname,
 186                                     uint32_t phandle)
 187{
 188    char *name = g_strdup_printf("%s/fixed-link", gemname);
 189
 190    qemu_fdt_add_subnode(s->fdt, name);
 191    qemu_fdt_setprop_cell(s->fdt, name, "phandle", phandle);
 192    qemu_fdt_setprop(s->fdt, name, "full-duplex", NULL, 0);
 193    qemu_fdt_setprop_cell(s->fdt, name, "speed", 1000);
 194    g_free(name);
 195}
 196
 197static void fdt_add_gem_nodes(VersalVirt *s)
 198{
 199    uint64_t addrs[] = { MM_GEM1, MM_GEM0 };
 200    unsigned int irqs[] = { VERSAL_GEM1_IRQ_0, VERSAL_GEM0_IRQ_0 };
 201    const char clocknames[] = "pclk\0hclk\0tx_clk\0rx_clk";
 202    const char compat_gem[] = "cdns,zynqmp-gem\0cdns,gem";
 203    int i;
 204
 205    for (i = 0; i < ARRAY_SIZE(addrs); i++) {
 206        char *name = g_strdup_printf("/ethernet@%" PRIx64, addrs[i]);
 207        qemu_fdt_add_subnode(s->fdt, name);
 208
 209        fdt_add_fixed_link_nodes(s, name, s->phandle.ethernet_phy[i]);
 210        qemu_fdt_setprop_string(s->fdt, name, "phy-mode", "rgmii-id");
 211        qemu_fdt_setprop_cell(s->fdt, name, "phy-handle",
 212                              s->phandle.ethernet_phy[i]);
 213        qemu_fdt_setprop_cells(s->fdt, name, "clocks",
 214                               s->phandle.clk_25Mhz, s->phandle.clk_25Mhz,
 215                               s->phandle.clk_25Mhz, s->phandle.clk_25Mhz);
 216        qemu_fdt_setprop(s->fdt, name, "clock-names",
 217                         clocknames, sizeof(clocknames));
 218        qemu_fdt_setprop_cells(s->fdt, name, "interrupts",
 219                               GIC_FDT_IRQ_TYPE_SPI, irqs[i],
 220                               GIC_FDT_IRQ_FLAGS_LEVEL_HI,
 221                               GIC_FDT_IRQ_TYPE_SPI, irqs[i],
 222                               GIC_FDT_IRQ_FLAGS_LEVEL_HI);
 223        qemu_fdt_setprop_sized_cells(s->fdt, name, "reg",
 224                                     2, addrs[i], 2, 0x1000);
 225        qemu_fdt_setprop(s->fdt, name, "compatible",
 226                         compat_gem, sizeof(compat_gem));
 227        qemu_fdt_setprop_cell(s->fdt, name, "#address-cells", 1);
 228        qemu_fdt_setprop_cell(s->fdt, name, "#size-cells", 0);
 229        g_free(name);
 230    }
 231}
 232
 233static void fdt_nop_memory_nodes(void *fdt, Error **errp)
 234{
 235    Error *err = NULL;
 236    char **node_path;
 237    int n = 0;
 238
 239    node_path = qemu_fdt_node_unit_path(fdt, "memory", &err);
 240    if (err) {
 241        error_propagate(errp, err);
 242        return;
 243    }
 244    while (node_path[n]) {
 245        if (g_str_has_prefix(node_path[n], "/memory")) {
 246            qemu_fdt_nop_node(fdt, node_path[n]);
 247        }
 248        n++;
 249    }
 250    g_strfreev(node_path);
 251}
 252
 253static void fdt_add_memory_nodes(VersalVirt *s, void *fdt, uint64_t ram_size)
 254{
 255    /* Describes the various split DDR access regions.  */
 256    static const struct {
 257        uint64_t base;
 258        uint64_t size;
 259    } addr_ranges[] = {
 260        { MM_TOP_DDR, MM_TOP_DDR_SIZE },
 261        { MM_TOP_DDR_2, MM_TOP_DDR_2_SIZE },
 262        { MM_TOP_DDR_3, MM_TOP_DDR_3_SIZE },
 263        { MM_TOP_DDR_4, MM_TOP_DDR_4_SIZE }
 264    };
 265    uint64_t mem_reg_prop[8] = {0};
 266    uint64_t size = ram_size;
 267    Error *err = NULL;
 268    char *name;
 269    int i;
 270
 271    fdt_nop_memory_nodes(fdt, &err);
 272    if (err) {
 273        error_report_err(err);
 274        return;
 275    }
 276
 277    name = g_strdup_printf("/memory@%x", MM_TOP_DDR);
 278    for (i = 0; i < ARRAY_SIZE(addr_ranges) && size; i++) {
 279        uint64_t mapsize;
 280
 281        mapsize = size < addr_ranges[i].size ? size : addr_ranges[i].size;
 282
 283        mem_reg_prop[i * 2] = addr_ranges[i].base;
 284        mem_reg_prop[i * 2 + 1] = mapsize;
 285        size -= mapsize;
 286    }
 287    qemu_fdt_add_subnode(fdt, name);
 288    qemu_fdt_setprop_string(fdt, name, "device_type", "memory");
 289
 290    switch (i) {
 291    case 1:
 292        qemu_fdt_setprop_sized_cells(fdt, name, "reg",
 293                                     2, mem_reg_prop[0],
 294                                     2, mem_reg_prop[1]);
 295        break;
 296    case 2:
 297        qemu_fdt_setprop_sized_cells(fdt, name, "reg",
 298                                     2, mem_reg_prop[0],
 299                                     2, mem_reg_prop[1],
 300                                     2, mem_reg_prop[2],
 301                                     2, mem_reg_prop[3]);
 302        break;
 303    case 3:
 304        qemu_fdt_setprop_sized_cells(fdt, name, "reg",
 305                                     2, mem_reg_prop[0],
 306                                     2, mem_reg_prop[1],
 307                                     2, mem_reg_prop[2],
 308                                     2, mem_reg_prop[3],
 309                                     2, mem_reg_prop[4],
 310                                     2, mem_reg_prop[5]);
 311        break;
 312    case 4:
 313        qemu_fdt_setprop_sized_cells(fdt, name, "reg",
 314                                     2, mem_reg_prop[0],
 315                                     2, mem_reg_prop[1],
 316                                     2, mem_reg_prop[2],
 317                                     2, mem_reg_prop[3],
 318                                     2, mem_reg_prop[4],
 319                                     2, mem_reg_prop[5],
 320                                     2, mem_reg_prop[6],
 321                                     2, mem_reg_prop[7]);
 322        break;
 323    default:
 324        g_assert_not_reached();
 325    }
 326    g_free(name);
 327}
 328
 329static void versal_virt_modify_dtb(const struct arm_boot_info *binfo,
 330                                    void *fdt)
 331{
 332    VersalVirt *s = container_of(binfo, VersalVirt, binfo);
 333
 334    fdt_add_memory_nodes(s, fdt, binfo->ram_size);
 335}
 336
 337static void *versal_virt_get_dtb(const struct arm_boot_info *binfo,
 338                                  int *fdt_size)
 339{
 340    const VersalVirt *board = container_of(binfo, VersalVirt, binfo);
 341
 342    *fdt_size = board->fdt_size;
 343    return board->fdt;
 344}
 345
 346#define NUM_VIRTIO_TRANSPORT 8
 347static void create_virtio_regions(VersalVirt *s)
 348{
 349    int virtio_mmio_size = 0x200;
 350    int i;
 351
 352    for (i = 0; i < NUM_VIRTIO_TRANSPORT; i++) {
 353        char *name = g_strdup_printf("virtio%d", i);;
 354        hwaddr base = MM_TOP_RSVD + i * virtio_mmio_size;
 355        int irq = VERSAL_RSVD_IRQ_FIRST + i;
 356        MemoryRegion *mr;
 357        DeviceState *dev;
 358        qemu_irq pic_irq;
 359
 360        pic_irq = qdev_get_gpio_in(DEVICE(&s->soc.fpd.apu.gic), irq);
 361        dev = qdev_create(NULL, "virtio-mmio");
 362        object_property_add_child(OBJECT(&s->soc), name, OBJECT(dev),
 363                                  &error_fatal);
 364        qdev_init_nofail(dev);
 365        sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic_irq);
 366        mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0);
 367        memory_region_add_subregion(&s->soc.mr_ps, base, mr);
 368        g_free(name);
 369    }
 370
 371    for (i = 0; i < NUM_VIRTIO_TRANSPORT; i++) {
 372        hwaddr base = MM_TOP_RSVD + i * virtio_mmio_size;
 373        int irq = VERSAL_RSVD_IRQ_FIRST + i;
 374        char *name = g_strdup_printf("/virtio_mmio@%" PRIx64, base);
 375
 376        qemu_fdt_add_subnode(s->fdt, name);
 377        qemu_fdt_setprop(s->fdt, name, "dma-coherent", NULL, 0);
 378        qemu_fdt_setprop_cells(s->fdt, name, "interrupts",
 379                               GIC_FDT_IRQ_TYPE_SPI, irq,
 380                               GIC_FDT_IRQ_FLAGS_EDGE_LO_HI);
 381        qemu_fdt_setprop_sized_cells(s->fdt, name, "reg",
 382                                     2, base, 2, virtio_mmio_size);
 383        qemu_fdt_setprop_string(s->fdt, name, "compatible", "virtio,mmio");
 384        g_free(name);
 385    }
 386}
 387
 388static void versal_virt_init(MachineState *machine)
 389{
 390    VersalVirt *s = XLNX_VERSAL_VIRT_MACHINE(machine);
 391    int psci_conduit = QEMU_PSCI_CONDUIT_DISABLED;
 392
 393    /*
 394     * If the user provides an Operating System to be loaded, we expect them
 395     * to use the -kernel command line option.
 396     *
 397     * Users can load firmware or boot-loaders with the -device loader options.
 398     *
 399     * When loading an OS, we generate a dtb and let arm_load_kernel() select
 400     * where it gets loaded. This dtb will be passed to the kernel in x0.
 401     *
 402     * If there's no -kernel option, we generate a DTB and place it at 0x1000
 403     * for the bootloaders or firmware to pick up.
 404     *
 405     * If users want to provide their own DTB, they can use the -dtb option.
 406     * These dtb's will have their memory nodes modified to match QEMU's
 407     * selected ram_size option before they get passed to the kernel or fw.
 408     *
 409     * When loading an OS, we turn on QEMU's PSCI implementation with SMC
 410     * as the PSCI conduit. When there's no -kernel, we assume the user
 411     * provides EL3 firmware to handle PSCI.
 412     */
 413    if (machine->kernel_filename) {
 414        psci_conduit = QEMU_PSCI_CONDUIT_SMC;
 415    }
 416
 417    memory_region_allocate_system_memory(&s->mr_ddr, NULL, "ddr",
 418                                         machine->ram_size);
 419
 420    sysbus_init_child_obj(OBJECT(machine), "xlnx-ve", &s->soc,
 421                          sizeof(s->soc), TYPE_XLNX_VERSAL);
 422    object_property_set_link(OBJECT(&s->soc), OBJECT(&s->mr_ddr),
 423                             "ddr", &error_abort);
 424    object_property_set_int(OBJECT(&s->soc), psci_conduit,
 425                            "psci-conduit", &error_abort);
 426    object_property_set_bool(OBJECT(&s->soc), true, "realized", &error_fatal);
 427
 428    fdt_create(s);
 429    create_virtio_regions(s);
 430    fdt_add_gem_nodes(s);
 431    fdt_add_uart_nodes(s);
 432    fdt_add_gic_nodes(s);
 433    fdt_add_timer_nodes(s);
 434    fdt_add_cpu_nodes(s, psci_conduit);
 435    fdt_add_clk_node(s, "/clk125", 125000000, s->phandle.clk_125Mhz);
 436    fdt_add_clk_node(s, "/clk25", 25000000, s->phandle.clk_25Mhz);
 437
 438    /* Make the APU cpu address space visible to virtio and other
 439     * modules unaware of muliple address-spaces.  */
 440    memory_region_add_subregion_overlap(get_system_memory(),
 441                                        0, &s->soc.fpd.apu.mr, 0);
 442
 443    s->binfo.ram_size = machine->ram_size;
 444    s->binfo.kernel_filename = machine->kernel_filename;
 445    s->binfo.kernel_cmdline = machine->kernel_cmdline;
 446    s->binfo.initrd_filename = machine->initrd_filename;
 447    s->binfo.loader_start = 0x0;
 448    s->binfo.get_dtb = versal_virt_get_dtb;
 449    s->binfo.modify_dtb = versal_virt_modify_dtb;
 450    if (machine->kernel_filename) {
 451        arm_load_kernel(s->soc.fpd.apu.cpu[0], &s->binfo);
 452    } else {
 453        AddressSpace *as = arm_boot_address_space(s->soc.fpd.apu.cpu[0],
 454                                                  &s->binfo);
 455        /* Some boot-loaders (e.g u-boot) don't like blobs at address 0 (NULL).
 456         * Offset things by 4K.  */
 457        s->binfo.loader_start = 0x1000;
 458        s->binfo.dtb_limit = 0x1000000;
 459        if (arm_load_dtb(s->binfo.loader_start,
 460                         &s->binfo, s->binfo.dtb_limit, as) < 0) {
 461            exit(EXIT_FAILURE);
 462        }
 463    }
 464}
 465
 466static void versal_virt_machine_instance_init(Object *obj)
 467{
 468}
 469
 470static void versal_virt_machine_class_init(ObjectClass *oc, void *data)
 471{
 472    MachineClass *mc = MACHINE_CLASS(oc);
 473
 474    mc->desc = "Xilinx Versal Virtual development board";
 475    mc->init = versal_virt_init;
 476    mc->max_cpus = XLNX_VERSAL_NR_ACPUS;
 477    mc->default_cpus = XLNX_VERSAL_NR_ACPUS;
 478    mc->no_cdrom = true;
 479}
 480
 481static const TypeInfo versal_virt_machine_init_typeinfo = {
 482    .name       = TYPE_XLNX_VERSAL_VIRT_MACHINE,
 483    .parent     = TYPE_MACHINE,
 484    .class_init = versal_virt_machine_class_init,
 485    .instance_init = versal_virt_machine_instance_init,
 486    .instance_size = sizeof(VersalVirt),
 487};
 488
 489static void versal_virt_machine_init_register_types(void)
 490{
 491    type_register_static(&versal_virt_machine_init_typeinfo);
 492}
 493
 494type_init(versal_virt_machine_init_register_types)
 495
 496