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); }
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); }
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; }
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; } } }
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, ð_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; }