예제 #1
0
uint32_t qemu_fdt_getprop_cell(void *fdt, const char *node_path,
                               const char *property)
{
    int len;
    const uint32_t *p = qemu_fdt_getprop(fdt, node_path, property, &len);
    if (len != 4) {
        error_report("%s: %s/%s not 4 bytes long (not a cell?)",
                     __func__, node_path, property);
        exit(1);
    }
    return be32_to_cpu(*p);
}
예제 #2
0
파일: device_tree.c 프로젝트: JMR-b/qemu
uint32_t qemu_fdt_getprop_cell(void *fdt, const char *node_path,
                               const char *property, int *lenp, Error **errp)
{
    int len;
    const uint32_t *p;

    if (!lenp) {
        lenp = &len;
    }
    p = qemu_fdt_getprop(fdt, node_path, property, lenp, errp);
    if (!p) {
        return 0;
    } else if (*lenp != 4) {
        error_setg(errp, "%s: %s/%s not 4 bytes long (not a cell?)",
                   __func__, node_path, property);
        *lenp = -EINVAL;
        return 0;
    }
    return be32_to_cpu(*p);
}
예제 #3
0
static void fdt_init_node(void *args)
{

    struct FDTInitNodeArgs *a = args;
    char *node_path = a->node_path;
    FDTMachineInfo *fdti = a->fdti;
    g_free(a);

    char *all_compats = NULL, *compat, *node_name, *next_compat;
    int compat_len;

#ifdef FDT_GENERIC_UTIL_ERR_DEBUG
    static int entry_index;
    int this_entry = entry_index++;
#endif
    DB_PRINT("enter %d %s\n", this_entry, node_path);

    /* try instance binding first */
    node_name = qemu_fdt_get_node_name(fdti->fdt, node_path);
    if (!node_name) {
        fprintf(stderr, "FDT: ERROR: nameless node: %s\n", node_path);
    }
    if (!fdt_init_inst_bind(node_path, fdti, node_name)) {
        goto exit;
    }

    /* fallback to compatibility binding */
    all_compats = qemu_fdt_getprop(fdti->fdt, node_path,
        "compatible", &compat_len, false, NULL);
    if (!all_compats) {
        fprintf(stderr, "FDT: ERROR: no compatibility found for node %s/%s\n", node_path,
            node_name);
        DB_PRINT("exit %d\n", this_entry);
        fdti->routinesPending--;
        return;
    }
    compat = all_compats;

try_next_compat:
    if (compat_len == 0) {
        goto invalidate;
    }
    if (!fdt_init_compat(node_path, fdti, compat)) {
        goto exit;
    }
    if (!fdt_init_qdev(node_path, fdti, compat)) {
        goto exit;
    }
    next_compat = rawmemchr(compat, '\0');
    compat_len -= (next_compat + 1 - compat);
    if (compat_len > 0) {
        *next_compat = ' ';
    }
    compat = next_compat+1;
    goto try_next_compat;
invalidate:
    fprintf(stderr, "FDT: Unsupported peripheral invalidated %s compatibilities %s\n",
        node_name, all_compats);
    qemu_fdt_setprop_string(fdti->fdt, node_path, "compatible",
        "invalidated");
exit:

    DB_PRINT("exit %d\n", this_entry);

    if (!fdt_init_has_opaque(fdti, node_path)) {
        fdt_init_set_opaque(fdti, node_path, NULL);
    }
    g_free(node_path);
    g_free(all_compats);
    fdti->routinesPending--;
    return;
}
예제 #4
0
qemu_irq *fdt_get_irq_info(FDTMachineInfo *fdti, char *node_path, int irq_idx,
                          char *info, bool *map_mode) {
    void *fdt = fdti->fdt;
    uint32_t intc_phandle, intc_cells, cells[32];
    char intc_node_path[DT_PATH_LENGTH];
    qemu_irq *ret = NULL;
    int i;
    Error *errp = NULL;

    intc_phandle = qemu_fdt_getprop_cell(fdt, node_path, "interrupt-parent",
                                                                0, true, &errp);
    if (errp) {
        errp = NULL;
        intc_cells = qemu_fdt_getprop_cell(fdt, node_path,
                                           "#interrupt-cells", 0, true, &errp);
        if (errp) {
            goto fail;
        }
        *map_mode = true;
    } else {
        if (qemu_devtree_get_node_by_phandle(fdt, intc_node_path,
                                             intc_phandle)) {
            goto fail;
        }
        intc_cells = qemu_fdt_getprop_cell(fdt, intc_node_path,
                                           "#interrupt-cells", 0, true, &errp);
        if (errp) {
            goto fail;
        }
        *map_mode = false;
    }

    DB_PRINT_NP(2, "%s intc_phandle: %d\n", node_path, intc_phandle);

    for (i = 0; i < intc_cells; ++i) {
        cells[i] = qemu_fdt_getprop_cell(fdt, node_path, "interrupts",
                                         intc_cells * irq_idx + i, false, &errp);
        if (errp) {
            goto fail;
        }
    }

    if (*map_mode) {
        int k;
        ret = g_new0(qemu_irq, 1);
        int num_matches = 0;
        int len;
        uint32_t imap_mask[intc_cells];
        uint32_t *imap_p;
        uint32_t *imap;
        bool use_parent = false;

        for (k = 0; k < intc_cells; ++k) {
            imap_mask[k] = qemu_fdt_getprop_cell(fdt, node_path,
                                                 "interrupt-map-mask", k + 2,
                                                 true, &errp);
            if (errp) {
                goto fail;
            }
        }

        /* Check if the device has an interrupt-map property */
        imap = qemu_fdt_getprop(fdt, node_path, "interrupt-map", &len,
                                  use_parent, &errp);

        if (!imap || errp) {
            /* If the device doesn't have an interrupt-map, try again with
             * inheritance. This will return the parents interrupt-map
             */
            use_parent = true;
            errp = NULL;

            imap_p = qemu_fdt_getprop(fdt, node_path, "interrupt-map",
                                      &len, use_parent, &errp);
            if (!imap_cached) {
                memcpy(imap_cache, imap_p, len);
                imap_cached = true;
            }
            imap = imap_cache;

            if (errp) {
                goto fail;
            }
        }

        len /= sizeof(uint32_t);

        i = 0;
        assert(imap);
        while (i < len) {
            if (!use_parent) {
                /* Only re-sync the interrupt-map when the device has it's
                 * own map, to save time.
                 */
                imap = qemu_fdt_getprop(fdt, node_path, "interrupt-map", &len,
                                          use_parent, &errp);

                if (errp) {
                    goto fail;
                }

                len /= sizeof(uint32_t);
            }

            bool match = true;
            uint32_t new_intc_cells, new_cells[32];
            i++; i++; /* FIXME: do address cells properly */
            for (k = 0; k < intc_cells; ++k) {
                uint32_t  map_val = be32_to_cpu(imap[i++]);
                if ((cells[k] ^ map_val) & imap_mask[k]) {
                    match = false;
                }
            }
            /* when caching, we hackishly store the number of cells for
             * the parent in the MSB. +1, so zero MSB means non cachd
             * and the full lookup is needed.
             */
            intc_phandle = be32_to_cpu(imap[i++]);
            if (intc_phandle & (0xffu << 24)) {
                new_intc_cells = (intc_phandle >> 24) - 1;
            } else {
                if (qemu_devtree_get_node_by_phandle(fdt, intc_node_path,
                                                     intc_phandle)) {
                    goto fail;
                }
                new_intc_cells = qemu_fdt_getprop_cell(fdt, intc_node_path,
                                                       "#interrupt-cells", 0,
                                                       false, &errp);
                imap[i - 1] = cpu_to_be32(intc_phandle |
                                            (new_intc_cells + 1) << 24);
                if (errp) {
                    goto fail;
                }
            }
            for (k = 0; k < new_intc_cells; ++k) {
                new_cells[k] = be32_to_cpu(imap[i++]);
            }
            if (match) {
                num_matches++;
                ret = g_renew(qemu_irq, ret, num_matches + 1);
                if (intc_phandle & (0xffu << 24)) {
                    if (qemu_devtree_get_node_by_phandle(fdt, intc_node_path,
                                                         intc_phandle &
                                                         ((1 << 24) - 1))) {
                        goto fail;
                    }
                }

                DB_PRINT_NP(2, "Getting IRQ information: %s -> 0x%x (%s)\n",
                            node_path, intc_phandle, intc_node_path);

                memset(&ret[num_matches], 0, sizeof(*ret));
                fdt_get_irq_info_from_intc(fdti, &ret[num_matches-1], intc_node_path,
                                           new_cells, new_intc_cells, 1, &errp);
                if (info) {
                   sprintf(info, "%s", intc_node_path);
                   info += strlen(info) + 1;
                }
                if (errp) {
                    goto fail;
                }
            }
        }
예제 #5
0
static void fdt_init_node(void *args)
{
    struct FDTInitNodeArgs *a = args;
    char *node_path = a->node_path;
    FDTMachineInfo *fdti = a->fdti;
    g_free(a);

    simple_bus_fdt_init(node_path, fdti);

    char *all_compats = NULL, *compat, *node_name, *next_compat, *device_type;
    int compat_len;

    DB_PRINT_NP(1, "enter\n");

    /* try instance binding first */
    node_name = qemu_devtree_get_node_name(fdti->fdt, node_path);
    DB_PRINT_NP(1, "node with name: %s\n", node_name ? node_name : "(none)");
    if (!node_name) {
        printf("FDT: ERROR: nameless node: %s\n", node_path);
    }
    if (!fdt_init_inst_bind(node_path, fdti, node_name)) {
        DB_PRINT_NP(0, "instance bind successful\n");
        goto exit;
    }

    /* fallback to compatibility binding */
    all_compats = qemu_fdt_getprop(fdti->fdt, node_path, "compatible",
                                   &compat_len, false, NULL);
    if (!all_compats) {
        DB_PRINT_NP(0, "no compatibility found\n");
    }

    for (compat = all_compats; compat && compat_len; compat = next_compat+1) {
        char *compat_prefixed = g_strdup_printf("compatible:%s", compat);
        if (!fdt_init_compat(node_path, fdti, compat_prefixed)) {
            goto exit;
        }
        g_free(compat_prefixed);
        if (!fdt_init_qdev(node_path, fdti, compat)) {
            goto exit;
        }
        next_compat = rawmemchr(compat, '\0');
        compat_len -= (next_compat + 1 - compat);
        if (compat_len > 0) {
            *next_compat = ' ';
        }
    }

    device_type = qemu_fdt_getprop(fdti->fdt, node_path,
                                   "device_type", NULL, false, NULL);
    device_type = g_strdup_printf("device_type:%s", device_type);
    if (!fdt_init_compat(node_path, fdti, device_type)) {
        goto exit;
    }

    if (!all_compats) {
        goto exit;
    }
    DB_PRINT_NP(0, "FDT: Unsupported peripheral invalidated - "
                "compatibilities %s\n", all_compats);
    qemu_fdt_setprop_string(fdti->fdt, node_path, "compatible", "invalidated");
exit:

    DB_PRINT_NP(1, "exit\n");

    if (!fdt_init_has_opaque(fdti, node_path)) {
        fdt_init_set_opaque(fdti, node_path, NULL);
    }
    g_free(node_path);
    g_free(all_compats);
    return;
}
static void
microblaze_generic_fdt_reset(MicroBlazeCPU *cpu)
{
    CPUMBState *env = &cpu->env;

    char node_path[DT_PATH_LENGTH];
    qemu_fdt_get_node_by_name(fdt_g, node_path, "cpu");
    int t;
    int use_exc = 0;

    env->pvr.regs[0] = 0;
    env->pvr.regs[2] = PVR2_D_OPB_MASK \
                        | PVR2_D_LMB_MASK \
                        | PVR2_I_OPB_MASK \
                        | PVR2_I_LMB_MASK \
                        | 0;

    /* Even if we don't have PVR's, we fill out everything
       because QEMU will internally follow what the pvr regs
       state about the HW.  */

    if (VAL("xlnx,use-barrel")) {
        env->pvr.regs[0] |= PVR0_USE_BARREL_MASK;
        env->pvr.regs[2] |= PVR2_USE_BARREL_MASK;
    }

    if (VAL("xlnx,use-div")) {
        env->pvr.regs[0] |= PVR0_USE_DIV_MASK;
        env->pvr.regs[2] |= PVR2_USE_DIV_MASK;
    }

    t = VAL("xlnx,use-hw-mul");
    if (t) {
        env->pvr.regs[0] |= PVR0_USE_HW_MUL_MASK;
        env->pvr.regs[2] |= PVR2_USE_HW_MUL_MASK;
        if (t >= 2) {
            env->pvr.regs[2] |= PVR2_USE_MUL64_MASK;
        }
    }

    if (VAL("xlnx,use-msr-instr")) {
        env->pvr.regs[2] |= PVR2_USE_MSR_INSTR;
    }

    if (VAL("xlnx,use-pcmp-instr")) {
        env->pvr.regs[2] |= PVR2_USE_PCMP_INSTR;
    }

    if (VAL("xlnx,opcode-0x0-illegal")) {
        env->pvr.regs[2] |= PVR2_OPCODE_0x0_ILL_MASK;
    }

    if (VAL("xlnx,unaligned-exceptions")) {
        env->pvr.regs[2] |= PVR2_UNALIGNED_EXC_MASK;
        use_exc = 1;
    }

    if (VAL("xlnx,ill-opcode-exception")) {
        env->pvr.regs[2] |= PVR2_ILL_OPCODE_EXC_MASK;
        use_exc = 1;
    }

    if (VAL("xlnx,iopb-bus-exception")) {
        env->pvr.regs[2] |= PVR2_IOPB_BUS_EXC_MASK;
        use_exc = 1;
    }

    if (VAL("xlnx,dopb-bus-exception")) {
        env->pvr.regs[2] |= PVR2_DOPB_BUS_EXC_MASK;
        use_exc = 1;
    }

    if (VAL("xlnx,div-zero-exception")) {
        env->pvr.regs[2] |= PVR2_DIV_ZERO_EXC_MASK;
        use_exc = 1;
    }

    env->pvr.regs[0] |= VAL("xlnx,pvr-user1") & 0xff;
    env->pvr.regs[1] = VAL("xlnx,pvr-user2");

    /* MMU regs.  */
    t = VAL("xlnx,use-mmu");
    if (use_exc || t) {
        env->pvr.regs[0] |= PVR0_USE_EXC_MASK ;
    }

    env->pvr.regs[11] = t << 30;
    t = VAL("xlnx,mmu-zones");
    env->pvr.regs[11] |= t << 17;
    env->mmu.c_mmu_zones = t;

    t = VAL("xlnx,mmu-tlb-access");
    env->mmu.c_mmu_tlb_access = t;
    env->pvr.regs[11] |= t << 22;

    {
        char *str;
        const struct {
            const char *name;
            unsigned int arch;
        } arch_lookup[] = {
            {"virtex2", 0x4},
            {"virtex2pro", 0x5},
            {"spartan3", 0x6},
            {"virtex4", 0x7},
            {"virtex5", 0x8},
            {"spartan3e", 0x9},
            {"spartan3a", 0xa},
            {"spartan3an", 0xb},
            {"spartan3adsp", 0xc},
            {"spartan6", 0xd},
            {"virtex6", 0xe},
            {"virtex7", 0xf},
            {"kintex7", 0x10},
            {"artix7", 0x11},
            {"zynq7000", 0x12},
            {"spartan2", 0xf0},
            {NULL, 0},
        };
        unsigned int i = 0;

        str = qemu_fdt_getprop(fdt_g, node_path, "xlnx,family", NULL, false, NULL);
        while (arch_lookup[i].name && str) {
            if (strcmp(arch_lookup[i].name, str) == 0) {
                break;
            }
            i++;
        }
        if (!str || !arch_lookup[i].arch) {
            env->pvr.regs[10] = 0x0c000000; /* spartan 3a dsp family.  */
        } else {
            env->pvr.regs[10] = arch_lookup[i].arch << 24;
        }
        g_free(str);
    }

    {
        env->pvr.regs[4] = PVR4_USE_ICACHE_MASK
                           | (21 << 26) /* Tag size.  */
                           | (4 << 21)
                           | (11 << 16);
        env->pvr.regs[6] = VAL("d-cache-baseaddr");
        env->pvr.regs[7] = VAL("d-cache-highaddr");
        env->pvr.regs[5] = PVR5_USE_DCACHE_MASK
                           | (21 << 26) /* Tag size.  */
                           | (4 << 21)
                           | (11 << 16);

        if (VAL("xlnx,dcache-use-writeback")) {
            env->pvr.regs[5] |= PVR5_DCACHE_WRITEBACK_MASK;
        }

        env->pvr.regs[8] = VAL("i-cache-baseaddr");
        env->pvr.regs[9] = VAL("i-cache-highaddr");
    }
    if (VAL("qemu,halt")) {
        cpu_interrupt(ENV_GET_CPU(env), CPU_INTERRUPT_HALT);
    }
}
static void
microblaze_generic_fdt_init(MachineState *machine)
{
    CPUState *cpu;
    ram_addr_t ram_kernel_base = 0, ram_kernel_size = 0;
    void *fdt = NULL;
    const char *dtb_arg, *hw_dtb_arg;
    QemuOpts *machine_opts;
    int fdt_size;

    /* for memory node */
    char node_path[DT_PATH_LENGTH];
    FDTMachineInfo *fdti;
    MemoryRegion *main_mem;

    /* For DMA node */
    char dma_path[DT_PATH_LENGTH] = { 0 };
    uint32_t memory_phandle;

    /* For Ethernet nodes */
    char **eth_paths;
    char *phy_path;
    char *mdio_path;
    uint32_t n_eth;
    uint32_t prop_val;

    machine_opts = qemu_opts_find(qemu_find_opts("machine"), 0);
    if (!machine_opts) {
        goto no_dtb_arg;
    }
    dtb_arg = qemu_opt_get(machine_opts, "dtb");
    hw_dtb_arg = qemu_opt_get(machine_opts, "hw-dtb");
    if (!dtb_arg && !hw_dtb_arg) {
        goto no_dtb_arg;
    }

    /* If the user only provided a -dtb, use it as the hw description.  */
    if (!hw_dtb_arg) {
        hw_dtb_arg = dtb_arg;
    }

    fdt = load_device_tree(hw_dtb_arg, &fdt_size);
    if (!fdt) {
        hw_error("Error: Unable to load Device Tree %s\n", hw_dtb_arg);
        return;
    }

    if (IS_PETALINUX_MACHINE) {
        /* Mark the simple-bus as incompatible as it breaks the Microblaze
         * PetaLinux boot
         */
        add_to_compat_table(NULL, "compatible:simple-bus", NULL);
    }

    /* find memory node or add new one if needed */
    while (qemu_fdt_get_node_by_name(fdt, node_path, "memory")) {
        qemu_fdt_add_subnode(fdt, "/memory@0");
        qemu_fdt_setprop_cells(fdt, "/memory@0", "reg", 0, machine->ram_size);
    }

    if (!qemu_fdt_getprop(fdt, "/memory", "compatible", NULL, 0, NULL)) {
        qemu_fdt_setprop_string(fdt, "/memory", "compatible",
                                "qemu:memory-region");
        qemu_fdt_setprop_cells(fdt, "/memory", "qemu,ram", 1);
    }

    if (IS_PETALINUX_MACHINE) {
        /* If using a *-plnx machine, the AXI DMA memory links are not included
         * in the DTB by default. To avoid seg faults, add the links in here if
         * they have not already been added by the user
         */
        qemu_fdt_get_node_by_name(fdt, dma_path, "dma");

        if (strcmp(dma_path, "") != 0) {
            memory_phandle = qemu_fdt_check_phandle(fdt, node_path);

            if (!memory_phandle) {
                memory_phandle = qemu_fdt_alloc_phandle(fdt);

                qemu_fdt_setprop_cells(fdt, "/memory", "linux,phandle",
                                       memory_phandle);
                qemu_fdt_setprop_cells(fdt, "/memory", "phandle",
                                       memory_phandle);
            }

            if (!qemu_fdt_getprop(fdt, dma_path, "sg", NULL, 0, NULL)) {
                qemu_fdt_setprop_phandle(fdt, dma_path, "sg", node_path);
            }

            if (!qemu_fdt_getprop(fdt, dma_path, "s2mm", NULL, 0, NULL)) {
                qemu_fdt_setprop_phandle(fdt, dma_path, "s2mm", node_path);
            }

            if (!qemu_fdt_getprop(fdt, dma_path, "mm2s", NULL, 0, NULL)) {
                qemu_fdt_setprop_phandle(fdt, dma_path, "mm2s", node_path);
            }
        }

        /* Copy phyaddr value from phy node reg property */
        n_eth = qemu_fdt_get_n_nodes_by_name(fdt, &eth_paths, "ethernet");

        while (n_eth--) {
            mdio_path = qemu_fdt_get_child_by_name(fdt, eth_paths[n_eth],
                                                       "mdio");
            if (mdio_path) {
                phy_path = qemu_fdt_get_child_by_name(fdt, mdio_path,
                                                          "phy");
                if (phy_path) {
                    prop_val = qemu_fdt_getprop_cell(fdt, phy_path, "reg", NULL, 0,
                                                     NULL, &error_abort);
                    qemu_fdt_setprop_cell(fdt, eth_paths[n_eth], "xlnx,phyaddr",
                                          prop_val);
                    g_free(phy_path);
                } else {
                    qemu_log_mask(LOG_GUEST_ERROR, "phy not found in %s",
                                  mdio_path);
                }
                g_free(mdio_path);
            }
            g_free(eth_paths[n_eth]);
        }
        g_free(eth_paths);
    }

    /* Instantiate peripherals from the FDT.  */
    fdti = fdt_generic_create_machine(fdt, NULL);
    main_mem = MEMORY_REGION(object_resolve_path(node_path, NULL));

    ram_kernel_base = object_property_get_int(OBJECT(main_mem), "addr", NULL);
    ram_kernel_size = object_property_get_int(OBJECT(main_mem), "size", NULL);

    if (!memory_region_is_mapped(main_mem)) {
        /* If the memory region is not mapped, map it here.
         * It has to be mapped somewhere, so guess that the base address
         * is where the kernel starts
         */
        memory_region_add_subregion(get_system_memory(), ram_kernel_base,
                                    main_mem);

        if (ram_kernel_base && IS_PETALINUX_MACHINE) {
            /* If the memory added is at an offset from zero QEMU will error
             * when an ISR/exception is triggered. Add a small amount of hack
             * RAM to handle this.
             */
            MemoryRegion *hack_ram = g_new(MemoryRegion, 1);
            memory_region_init_ram(hack_ram, NULL, "hack_ram", 0x1000,
                                   &error_abort);
            vmstate_register_ram_global(hack_ram);
            memory_region_add_subregion(get_system_memory(), 0, hack_ram);
        }
    }

    fdt_init_destroy_fdti(fdti);

    fdt_g = fdt;
    microblaze_load_kernel(MICROBLAZE_CPU(first_cpu), ram_kernel_base,
                           ram_kernel_size, machine->initrd_filename, NULL,
                           microblaze_generic_fdt_reset, 0, fdt, fdt_size);

    /* Register FDT to prop mapper for secondary cores.  */
    cpu = CPU_NEXT(first_cpu);
    while (cpu) {
        qemu_register_reset(secondary_cpu_reset, cpu);
        cpu = CPU_NEXT(cpu);
    }

    return;
no_dtb_arg:
    if (!QTEST_RUNNING) {
        hw_error("DTB must be specified for %s machine model\n", MACHINE_NAME);
    }
    return;
}