qemu/hw/microblaze/microblaze_generic_fdt.c
<<
>>
Prefs
   1/*
   2 * Model of Petalogix linux reference design for all boards
   3 *
   4 * Copyright (c) 2009 Edgar E. Iglesias.
   5 * Copyright (c) 2009 Michal Simek.
   6 * Copyright (c) 2012 Peter A.G. Crosthwaite (peter.croshtwaite@petalogix.com)
   7 * Copyright (c) 2012 Petalogix Pty Ltd.
   8 *
   9 * Permission is hereby granted, free of charge, to any person obtaining a copy
  10 * of this software and associated documentation files (the "Software"), to deal
  11 * in the Software without restriction, including without limitation the rights
  12 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13 * copies of the Software, and to permit persons to whom the Software is
  14 * furnished to do so, subject to the following conditions:
  15 *
  16 * The above copyright notice and this permission notice shall be included in
  17 * all copies or substantial portions of the Software.
  18 *
  19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  22 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  25 * THE SOFTWARE.
  26 */
  27
  28#include "qemu/osdep.h"
  29#include "cpu.h"
  30#include "hw/sysbus.h"
  31#include "qemu/log.h"
  32#include "net/net.h"
  33#include "hw/block/flash.h"
  34#include "sysemu/sysemu.h"
  35#include "hw/boards.h"
  36#include "sysemu/device_tree.h"
  37#include "exec/memory.h"
  38#include "exec/address-spaces.h"
  39#include "qemu/error-report.h"
  40#include "qapi/error.h"
  41#include "sysemu/qtest.h"
  42#include "qemu/config-file.h"
  43
  44#include "hw/fdt_generic_util.h"
  45#include "hw/fdt_generic_devices.h"
  46
  47#include "boot.h"
  48
  49#define VAL(name) qemu_fdt_getprop_cell(fdt_g, node_path, name, 0, false, NULL)
  50
  51#define IS_PETALINUX_MACHINE \
  52    (!strcmp(MACHINE_GET_CLASS(machine)->name, MACHINE_NAME "-plnx"))
  53
  54#define QTEST_RUNNING (qtest_enabled() && qtest_driver())
  55
  56/* FIXME: delete */
  57static void *fdt_g;
  58
  59static void
  60microblaze_generic_fdt_reset(MicroBlazeCPU *cpu)
  61{
  62    CPUMBState *env = &cpu->env;
  63
  64    char node_path[DT_PATH_LENGTH];
  65    qemu_devtree_get_node_by_name(fdt_g, node_path, "cpu");
  66    int t;
  67    int use_exc = 0;
  68
  69    env->pvr.regs[0] = 0;
  70    env->pvr.regs[2] = PVR2_D_OPB_MASK \
  71                        | PVR2_D_LMB_MASK \
  72                        | PVR2_I_OPB_MASK \
  73                        | PVR2_I_LMB_MASK \
  74                        | 0;
  75
  76    /* Even if we don't have PVR's, we fill out everything
  77       because QEMU will internally follow what the pvr regs
  78       state about the HW.  */
  79
  80    if (VAL("xlnx,use-barrel")) {
  81        env->pvr.regs[0] |= PVR0_USE_BARREL_MASK;
  82        env->pvr.regs[2] |= PVR2_USE_BARREL_MASK;
  83    }
  84
  85    if (VAL("xlnx,use-div")) {
  86        env->pvr.regs[0] |= PVR0_USE_DIV_MASK;
  87        env->pvr.regs[2] |= PVR2_USE_DIV_MASK;
  88    }
  89
  90    t = VAL("xlnx,use-hw-mul");
  91    if (t) {
  92        env->pvr.regs[0] |= PVR0_USE_HW_MUL_MASK;
  93        env->pvr.regs[2] |= PVR2_USE_HW_MUL_MASK;
  94        if (t >= 2) {
  95            env->pvr.regs[2] |= PVR2_USE_MUL64_MASK;
  96        }
  97    }
  98
  99    if (VAL("xlnx,use-msr-instr")) {
 100        env->pvr.regs[2] |= PVR2_USE_MSR_INSTR;
 101    }
 102
 103    if (VAL("xlnx,use-pcmp-instr")) {
 104        env->pvr.regs[2] |= PVR2_USE_PCMP_INSTR;
 105    }
 106
 107    if (VAL("xlnx,opcode-0x0-illegal")) {
 108        env->pvr.regs[2] |= PVR2_OPCODE_0x0_ILL_MASK;
 109    }
 110
 111    if (VAL("xlnx,unaligned-exceptions")) {
 112        env->pvr.regs[2] |= PVR2_UNALIGNED_EXC_MASK;
 113        use_exc = 1;
 114    }
 115
 116    if (VAL("xlnx,ill-opcode-exception")) {
 117        env->pvr.regs[2] |= PVR2_ILL_OPCODE_EXC_MASK;
 118        use_exc = 1;
 119    }
 120
 121    if (VAL("xlnx,iopb-bus-exception")) {
 122        env->pvr.regs[2] |= PVR2_IOPB_BUS_EXC_MASK;
 123        use_exc = 1;
 124    }
 125
 126    if (VAL("xlnx,dopb-bus-exception")) {
 127        env->pvr.regs[2] |= PVR2_DOPB_BUS_EXC_MASK;
 128        use_exc = 1;
 129    }
 130
 131    if (VAL("xlnx,div-zero-exception")) {
 132        env->pvr.regs[2] |= PVR2_DIV_ZERO_EXC_MASK;
 133        use_exc = 1;
 134    }
 135
 136    env->pvr.regs[0] |= VAL("xlnx,pvr-user1") & 0xff;
 137    env->pvr.regs[1] = VAL("xlnx,pvr-user2");
 138
 139    /* MMU regs.  */
 140    t = VAL("xlnx,use-mmu");
 141    if (use_exc || t) {
 142        env->pvr.regs[0] |= PVR0_USE_EXC_MASK ;
 143    }
 144
 145    env->pvr.regs[11] = t << 30;
 146    t = VAL("xlnx,mmu-zones");
 147    env->pvr.regs[11] |= t << 17;
 148    env->mmu.c_mmu_zones = t;
 149
 150    t = VAL("xlnx,mmu-tlb-access");
 151    env->mmu.c_mmu_tlb_access = t;
 152    env->pvr.regs[11] |= t << 22;
 153
 154    {
 155        char *str;
 156        const struct {
 157            const char *name;
 158            unsigned int arch;
 159        } arch_lookup[] = {
 160            {"virtex2", 0x4},
 161            {"virtex2pro", 0x5},
 162            {"spartan3", 0x6},
 163            {"virtex4", 0x7},
 164            {"virtex5", 0x8},
 165            {"spartan3e", 0x9},
 166            {"spartan3a", 0xa},
 167            {"spartan3an", 0xb},
 168            {"spartan3adsp", 0xc},
 169            {"spartan6", 0xd},
 170            {"virtex6", 0xe},
 171            {"virtex7", 0xf},
 172            {"kintex7", 0x10},
 173            {"artix7", 0x11},
 174            {"zynq7000", 0x12},
 175            {"spartan2", 0xf0},
 176            {NULL, 0},
 177        };
 178        unsigned int i = 0;
 179
 180        str = qemu_fdt_getprop(fdt_g, node_path, "xlnx,family", NULL, false, NULL);
 181        while (arch_lookup[i].name && str) {
 182            if (strcmp(arch_lookup[i].name, str) == 0) {
 183                break;
 184            }
 185            i++;
 186        }
 187        if (!str || !arch_lookup[i].arch) {
 188            env->pvr.regs[10] = 0x0c000000; /* spartan 3a dsp family.  */
 189        } else {
 190            env->pvr.regs[10] = arch_lookup[i].arch << 24;
 191        }
 192        g_free(str);
 193    }
 194
 195    {
 196        env->pvr.regs[4] = PVR4_USE_ICACHE_MASK
 197                           | (21 << 26) /* Tag size.  */
 198                           | (4 << 21)
 199                           | (11 << 16);
 200        env->pvr.regs[6] = VAL("d-cache-baseaddr");
 201        env->pvr.regs[7] = VAL("d-cache-highaddr");
 202        env->pvr.regs[5] = PVR5_USE_DCACHE_MASK
 203                           | (21 << 26) /* Tag size.  */
 204                           | (4 << 21)
 205                           | (11 << 16);
 206
 207        if (VAL("xlnx,dcache-use-writeback")) {
 208            env->pvr.regs[5] |= PVR5_DCACHE_WRITEBACK_MASK;
 209        }
 210
 211        env->pvr.regs[8] = VAL("i-cache-baseaddr");
 212        env->pvr.regs[9] = VAL("i-cache-highaddr");
 213    }
 214    if (VAL("qemu,halt")) {
 215        cpu_interrupt(ENV_GET_CPU(env), CPU_INTERRUPT_HALT);
 216    }
 217}
 218
 219static void secondary_cpu_reset(void *opaque)
 220{
 221    MicroBlazeCPU *cpu = MICROBLAZE_CPU(opaque);
 222
 223    /* Configure secondary cores.  */
 224    microblaze_generic_fdt_reset(cpu);
 225}
 226
 227#define LMB_BRAM_SIZE  (128 * 1024)
 228
 229#define MACHINE_NAME "microblaze-fdt"
 230
 231#ifdef TARGET_WORDS_BIGENDIAN
 232int endian = 1;
 233#else
 234int endian;
 235#endif
 236
 237static void
 238microblaze_generic_fdt_init(MachineState *machine)
 239{
 240    CPUState *cpu;
 241    ram_addr_t ram_kernel_base = 0, ram_kernel_size = 0;
 242    void *fdt = NULL;
 243    const char *dtb_arg, *hw_dtb_arg;
 244    QemuOpts *machine_opts;
 245    int fdt_size;
 246
 247    /* for memory node */
 248    char node_path[DT_PATH_LENGTH];
 249    FDTMachineInfo *fdti;
 250    MemoryRegion *main_mem;
 251
 252    /* For DMA node */
 253    char dma_path[DT_PATH_LENGTH] = { 0 };
 254    uint32_t memory_phandle;
 255
 256    /* For Ethernet nodes */
 257    char **eth_paths;
 258    char *phy_path;
 259    char *mdio_path;
 260    uint32_t n_eth;
 261    uint32_t prop_val;
 262
 263    machine_opts = qemu_opts_find(qemu_find_opts("machine"), 0);
 264    if (!machine_opts) {
 265        goto no_dtb_arg;
 266    }
 267    dtb_arg = qemu_opt_get(machine_opts, "dtb");
 268    hw_dtb_arg = qemu_opt_get(machine_opts, "hw-dtb");
 269    if (!dtb_arg && !hw_dtb_arg) {
 270        goto no_dtb_arg;
 271    }
 272
 273    /* If the user only provided a -dtb, use it as the hw description.  */
 274    if (!hw_dtb_arg) {
 275        hw_dtb_arg = dtb_arg;
 276    }
 277
 278    fdt = load_device_tree(hw_dtb_arg, &fdt_size);
 279    if (!fdt) {
 280        hw_error("Error: Unable to load Device Tree %s\n", hw_dtb_arg);
 281        return;
 282    }
 283
 284    if (IS_PETALINUX_MACHINE) {
 285        /* Mark the simple-bus as incompatible as it breaks the Microblaze
 286         * PetaLinux boot
 287         */
 288        add_to_compat_table(NULL, "compatible:simple-bus", NULL);
 289    }
 290
 291    /* find memory node or add new one if needed */
 292    while (qemu_devtree_get_node_by_name(fdt, node_path, "memory")) {
 293        qemu_fdt_add_subnode(fdt, "/memory@0");
 294        qemu_fdt_setprop_cells(fdt, "/memory@0", "reg", 0, machine->ram_size);
 295    }
 296
 297    if (!qemu_fdt_getprop(fdt, "/memory", "compatible", NULL, 0, NULL)) {
 298        qemu_fdt_setprop_string(fdt, "/memory", "compatible",
 299                                "qemu:memory-region");
 300        qemu_fdt_setprop_cells(fdt, "/memory", "qemu,ram", 1);
 301    }
 302
 303    if (IS_PETALINUX_MACHINE) {
 304        /* If using a *-plnx machine, the AXI DMA memory links are not included
 305         * in the DTB by default. To avoid seg faults, add the links in here if
 306         * they have not already been added by the user
 307         */
 308        qemu_devtree_get_node_by_name(fdt, dma_path, "dma");
 309
 310        if (strcmp(dma_path, "") != 0) {
 311            memory_phandle = qemu_fdt_check_phandle(fdt, node_path);
 312
 313            if (!memory_phandle) {
 314                memory_phandle = qemu_fdt_alloc_phandle(fdt);
 315
 316                qemu_fdt_setprop_cells(fdt, "/memory", "linux,phandle",
 317                                       memory_phandle);
 318                qemu_fdt_setprop_cells(fdt, "/memory", "phandle",
 319                                       memory_phandle);
 320            }
 321
 322            if (!qemu_fdt_getprop(fdt, dma_path, "sg", NULL, 0, NULL)) {
 323                qemu_fdt_setprop_phandle(fdt, dma_path, "sg", node_path);
 324            }
 325
 326            if (!qemu_fdt_getprop(fdt, dma_path, "s2mm", NULL, 0, NULL)) {
 327                qemu_fdt_setprop_phandle(fdt, dma_path, "s2mm", node_path);
 328            }
 329
 330            if (!qemu_fdt_getprop(fdt, dma_path, "mm2s", NULL, 0, NULL)) {
 331                qemu_fdt_setprop_phandle(fdt, dma_path, "mm2s", node_path);
 332            }
 333        }
 334
 335        /* Copy phyaddr value from phy node reg property */
 336        n_eth = qemu_devtree_get_n_nodes_by_name(fdt, &eth_paths, "ethernet");
 337
 338        while (n_eth--) {
 339            mdio_path = qemu_devtree_get_child_by_name(fdt, eth_paths[n_eth],
 340                                                       "mdio");
 341            if (mdio_path) {
 342                phy_path = qemu_devtree_get_child_by_name(fdt, mdio_path,
 343                                                          "phy");
 344                if (phy_path) {
 345                    prop_val = qemu_fdt_getprop_cell(fdt, phy_path, "reg", 0,
 346                                                     NULL, &error_abort);
 347                    qemu_fdt_setprop_cell(fdt, eth_paths[n_eth], "xlnx,phyaddr",
 348                                          prop_val);
 349                    g_free(phy_path);
 350                } else {
 351                    qemu_log_mask(LOG_GUEST_ERROR, "phy not found in %s",
 352                                  mdio_path);
 353                }
 354                g_free(mdio_path);
 355            }
 356            g_free(eth_paths[n_eth]);
 357        }
 358        g_free(eth_paths);
 359    }
 360
 361    /* Instantiate peripherals from the FDT.  */
 362    fdti = fdt_generic_create_machine(fdt, NULL);
 363    main_mem = MEMORY_REGION(object_resolve_path(node_path, NULL));
 364
 365    ram_kernel_base = object_property_get_int(OBJECT(main_mem), "addr", NULL);
 366    ram_kernel_size = object_property_get_int(OBJECT(main_mem), "size", NULL);
 367
 368    if (!memory_region_is_mapped(main_mem)) {
 369        /* If the memory region is not mapped, map it here.
 370         * It has to be mapped somewhere, so guess that the base address
 371         * is where the kernel starts
 372         */
 373        memory_region_add_subregion(get_system_memory(), ram_kernel_base,
 374                                    main_mem);
 375
 376        if (ram_kernel_base && IS_PETALINUX_MACHINE) {
 377            /* If the memory added is at an offset from zero QEMU will error
 378             * when an ISR/exception is triggered. Add a small amount of hack
 379             * RAM to handle this.
 380             */
 381            MemoryRegion *hack_ram = g_new(MemoryRegion, 1);
 382            memory_region_init_ram_nomigrate(hack_ram, NULL, "hack_ram",
 383                                             0x1000, &error_abort);
 384            vmstate_register_ram_global(hack_ram);
 385            memory_region_add_subregion(get_system_memory(), 0, hack_ram);
 386        }
 387    }
 388
 389    fdt_init_destroy_fdti(fdti);
 390
 391    fdt_g = fdt;
 392    microblaze_load_kernel(MICROBLAZE_CPU(first_cpu), ram_kernel_base,
 393                           ram_kernel_size, machine->initrd_filename, NULL,
 394                           microblaze_generic_fdt_reset, fdt, fdt_size);
 395
 396    /* Register FDT to prop mapper for secondary cores.  */
 397    cpu = CPU_NEXT(first_cpu);
 398    while (cpu) {
 399        qemu_register_reset(secondary_cpu_reset, cpu);
 400        cpu = CPU_NEXT(cpu);
 401    }
 402
 403    return;
 404no_dtb_arg:
 405    if (!QTEST_RUNNING) {
 406        hw_error("DTB must be specified for %s machine model\n", MACHINE_NAME);
 407    }
 408    return;
 409}
 410
 411static void microblaze_generic_fdt_machine_init(MachineClass *mc)
 412{
 413    mc->desc = "Microblaze device tree driven machine model";
 414    mc->init = microblaze_generic_fdt_init;
 415}
 416
 417static void microblaze_generic_fdt_plnx_machine_init(MachineClass *mc)
 418{
 419    mc->desc = "Microblaze device tree driven machine model for PetaLinux";
 420    mc->init = microblaze_generic_fdt_init;
 421}
 422
 423fdt_register_compatibility_opaque(pflash_cfi01_fdt_init, "compatible:cfi-flash",
 424                                  0, &endian);
 425
 426DEFINE_MACHINE(MACHINE_NAME, microblaze_generic_fdt_machine_init)
 427DEFINE_MACHINE(MACHINE_NAME "-plnx", microblaze_generic_fdt_plnx_machine_init)
 428