static void devtree_prepare(void) { int res, node; u64 memreg1[] = {0, mm_bootmem_size}; u64 memreg2[] = {mm_highmem_addr, mm_highmem_size}; res = fdt_open_into(dt_blob_start, __devtree, DT_BUFSIZE); if (res < 0) fatal("fdt_open_into() failed"); node = fdt_path_offset(__devtree, "/chosen"); if (node < 0) fatal("/chosen node not found in devtree"); res = fdt_setprop(__devtree, node, "bootargs", bootargs, strlen(bootargs)+1); if (res < 0) fatal("couldn't set chosen.bootargs property"); if (initrd_start && initrd_size) { u64 start, end; start = mm_addr_to_kernel(initrd_start); res = fdt_setprop(__devtree, node, "linux,initrd-start", &start, sizeof(start)); if (res < 0) fatal("couldn't set chosen.linux,initrd-start property"); end = mm_addr_to_kernel(initrd_start + initrd_size); res = fdt_setprop(__devtree, node, "linux,initrd-end", &end, sizeof(end)); if (res < 0) fatal("couldn't set chosen.linux,initrd-end property"); res = fdt_add_mem_rsv(__devtree, start, initrd_size); if (res < 0) fatal("couldn't add reservation for the initrd"); } node = fdt_path_offset(__devtree, "/memory"); if (node < 0) fatal("/memory node not found in devtree"); res = fdt_setprop(__devtree, node, "reg", memreg1, sizeof(memreg1)); if (res < 0) fatal("couldn't set memory.reg property"); res = fdt_setprop(__devtree, node, "sony,lv1-highmem", memreg2, sizeof(memreg2)); if (res < 0) fatal("couldn't set memory.sony,lv1-highmem property"); res = fdt_add_mem_rsv(__devtree, (u64)__devtree, DT_BUFSIZE); if (res < 0) fatal("couldn't add reservation for the devtree"); res = fdt_pack(__devtree); if (res < 0) fatal("fdt_pack() failed"); printf("Device tree prepared\n"); }
int main(int argc, char *argv[]) { void *fdt; int err; int offset, s1, s2; test_init(argc, argv); fdt = xmalloc(SPACE); /* First create empty tree with SW */ CHECK(fdt_create(fdt, SPACE)); CHECK(fdt_finish_reservemap(fdt)); CHECK(fdt_begin_node(fdt, "")); CHECK(fdt_end_node(fdt)); CHECK(fdt_finish(fdt)); verbose_printf("Built empty tree, totalsize = %d\n", fdt_totalsize(fdt)); CHECK(fdt_open_into(fdt, fdt, SPACE)); CHECK(fdt_add_mem_rsv(fdt, TEST_ADDR_1, TEST_SIZE_1)); CHECK(fdt_add_mem_rsv(fdt, TEST_ADDR_2, TEST_SIZE_2)); CHECK(fdt_setprop_string(fdt, 0, "compatible", "test_tree1")); CHECK(fdt_setprop_cell(fdt, 0, "prop-int", TEST_VALUE_1)); CHECK(fdt_setprop_string(fdt, 0, "prop-str", TEST_STRING_1)); OFF_CHECK(offset, fdt_add_subnode(fdt, 0, "subnode@1")); s1 = offset; CHECK(fdt_setprop_string(fdt, s1, "compatible", "subnode1")); CHECK(fdt_setprop_cell(fdt, s1, "prop-int", TEST_VALUE_1)); OFF_CHECK(offset, fdt_add_subnode(fdt, s1, "subsubnode")); CHECK(fdt_setprop(fdt, offset, "compatible", "subsubnode1\0subsubnode", 23)); CHECK(fdt_setprop_cell(fdt, offset, "prop-int", TEST_VALUE_1)); OFF_CHECK(offset, fdt_add_subnode(fdt, s1, "ss1")); OFF_CHECK(offset, fdt_add_subnode(fdt, 0, "subnode@2")); s2 = offset; CHECK(fdt_setprop_cell(fdt, s2, "linux,phandle", PHANDLE_1)); CHECK(fdt_setprop_cell(fdt, s2, "prop-int", TEST_VALUE_2)); OFF_CHECK(offset, fdt_add_subnode(fdt, s2, "subsubnode@0")); CHECK(fdt_setprop_cell(fdt, offset, "linux,phandle", PHANDLE_2)); CHECK(fdt_setprop(fdt, offset, "compatible", "subsubnode2\0subsubnode", 23)); CHECK(fdt_setprop_cell(fdt, offset, "prop-int", TEST_VALUE_2)); OFF_CHECK(offset, fdt_add_subnode(fdt, s2, "ss2")); CHECK(fdt_pack(fdt)); save_blob("rw_tree1.test.dtb", fdt); PASS(); }
int fdt_initrd(void *fdt, ulong initrd_start, ulong initrd_end) { int nodeoffset; int err, j, total; int is_u64; uint64_t addr, size; /* just return if the size of initrd is zero */ if (initrd_start == initrd_end) return 0; /* find or create "/chosen" node. */ nodeoffset = fdt_find_or_add_subnode(fdt, 0, "chosen"); if (nodeoffset < 0) return nodeoffset; total = fdt_num_mem_rsv(fdt); /* * Look for an existing entry and update it. If we don't find * the entry, we will j be the next available slot. */ for (j = 0; j < total; j++) { err = fdt_get_mem_rsv(fdt, j, &addr, &size); if (addr == initrd_start) { fdt_del_mem_rsv(fdt, j); break; } } err = fdt_add_mem_rsv(fdt, initrd_start, initrd_end - initrd_start); if (err < 0) { printf("fdt_initrd: %s\n", fdt_strerror(err)); return err; } is_u64 = (fdt_address_cells(fdt, 0) == 2); err = fdt_setprop_uxx(fdt, nodeoffset, "linux,initrd-start", (uint64_t)initrd_start, is_u64); if (err < 0) { printf("WARNING: could not set linux,initrd-start %s.\n", fdt_strerror(err)); return err; } err = fdt_setprop_uxx(fdt, nodeoffset, "linux,initrd-end", (uint64_t)initrd_end, is_u64); if (err < 0) { printf("WARNING: could not set linux,initrd-end %s.\n", fdt_strerror(err)); return err; } return 0; }
int spapr_rtas_device_tree_setup(void *fdt, hwaddr rtas_addr, hwaddr rtas_size) { int ret; int i; ret = fdt_add_mem_rsv(fdt, rtas_addr, rtas_size); if (ret < 0) { fprintf(stderr, "Couldn't add RTAS reserve entry: %s\n", fdt_strerror(ret)); return ret; } ret = qemu_fdt_setprop_cell(fdt, "/rtas", "linux,rtas-base", rtas_addr); if (ret < 0) { fprintf(stderr, "Couldn't add linux,rtas-base property: %s\n", fdt_strerror(ret)); return ret; } ret = qemu_fdt_setprop_cell(fdt, "/rtas", "linux,rtas-entry", rtas_addr); if (ret < 0) { fprintf(stderr, "Couldn't add linux,rtas-entry property: %s\n", fdt_strerror(ret)); return ret; } ret = qemu_fdt_setprop_cell(fdt, "/rtas", "rtas-size", rtas_size); if (ret < 0) { fprintf(stderr, "Couldn't add rtas-size property: %s\n", fdt_strerror(ret)); return ret; } for (i = 0; i < TOKEN_MAX; i++) { struct rtas_call *call = &rtas_table[i]; if (!call->name) { continue; } ret = qemu_fdt_setprop_cell(fdt, "/rtas", call->name, i + TOKEN_BASE); if (ret < 0) { fprintf(stderr, "Couldn't add rtas token for %s: %s\n", call->name, fdt_strerror(ret)); return ret; } } return 0; }
int psci_update_dt(void *fdt) { #ifdef CONFIG_ARMV7_NONSEC if (!armv7_boot_nonsec()) return 0; #endif #ifndef CONFIG_ARMV7_SECURE_BASE /* secure code lives in RAM, keep it alive */ fdt_add_mem_rsv(fdt, (unsigned long)__secure_start, __secure_end - __secure_start); #endif return fdt_psci(fdt); }
void ft_cpu_setup(void *blob, bd_t *bd) { #ifdef CONFIG_MP int off; u32 bootpg = determine_mp_bootpg(NULL); #endif do_fixup_by_prop_u32(blob, "device_type", "cpu", 4, "timebase-frequency", bd->bi_busfreq / 4, 1); do_fixup_by_prop_u32(blob, "device_type", "cpu", 4, "bus-frequency", bd->bi_busfreq, 1); do_fixup_by_prop_u32(blob, "device_type", "cpu", 4, "clock-frequency", bd->bi_intfreq, 1); do_fixup_by_prop_u32(blob, "device_type", "soc", 4, "bus-frequency", bd->bi_busfreq, 1); #if defined(CONFIG_MPC8641) do_fixup_by_compat_u32(blob, "fsl,mpc8641-localbus", "bus-frequency", gd->arch.lbc_clk, 1); #endif do_fixup_by_compat_u32(blob, "fsl,elbc", "bus-frequency", gd->arch.lbc_clk, 1); fdt_fixup_memory(blob, (u64)bd->bi_memstart, (u64)bd->bi_memsize); #if defined(CONFIG_HAS_ETH0) || defined(CONFIG_HAS_ETH1) \ || defined(CONFIG_HAS_ETH2) || defined(CONFIG_HAS_ETH3) fdt_fixup_ethernet(blob); #endif #ifdef CONFIG_SYS_NS16550 do_fixup_by_compat_u32(blob, "ns16550", "clock-frequency", CONFIG_SYS_NS16550_CLK, 1); #endif #ifdef CONFIG_MP /* Reserve the boot page so OSes dont use it */ off = fdt_add_mem_rsv(blob, bootpg, (u64)4096); if (off < 0) printf("%s: %s\n", __FUNCTION__, fdt_strerror(off)); ft_fixup_num_cores(blob); #endif #ifdef CONFIG_SYS_SRIO ft_srio_setup(blob); #endif }
void ft_fixup_cpu(void *blob) { int off; __maybe_unused u64 spin_tbl_addr = (u64)get_spin_tbl_addr(); fdt32_t *reg; int addr_cells; u64 val, core_id; size_t *boot_code_size = &(__secondary_boot_code_size); off = fdt_path_offset(blob, "/cpus"); if (off < 0) { puts("couldn't find /cpus node\n"); return; } of_bus_default_count_cells(blob, off, &addr_cells, NULL); off = fdt_node_offset_by_prop_value(blob, -1, "device_type", "cpu", 4); while (off != -FDT_ERR_NOTFOUND) { reg = (fdt32_t *)fdt_getprop(blob, off, "reg", 0); core_id = of_read_number(reg, addr_cells); if (reg) { if (core_id == 0 || (is_core_online(core_id))) { val = spin_tbl_addr; val += id_to_core(core_id) * SPIN_TABLE_ELEM_SIZE; val = cpu_to_fdt64(val); fdt_setprop_string(blob, off, "enable-method", "spin-table"); fdt_setprop(blob, off, "cpu-release-addr", &val, sizeof(val)); } else { debug("skipping offline core\n"); } } else { puts("Warning: found cpu node without reg property\n"); } off = fdt_node_offset_by_prop_value(blob, off, "device_type", "cpu", 4); } fdt_add_mem_rsv(blob, (uintptr_t)&secondary_boot_code, *boot_code_size); }
/* Resize the fdt to its actual size + a bit of padding */ int fdt_shrink_to_minimum(void *blob, uint extrasize) { int i; uint64_t addr, size; int total, ret; uint actualsize; if (!blob) return 0; total = fdt_num_mem_rsv(blob); for (i = 0; i < total; i++) { fdt_get_mem_rsv(blob, i, &addr, &size); if (addr == (uintptr_t)blob) { fdt_del_mem_rsv(blob, i); break; } } /* * Calculate the actual size of the fdt * plus the size needed for 5 fdt_add_mem_rsv, one * for the fdt itself and 4 for a possible initrd * ((initrd-start + initrd-end) * 2 (name & value)) */ actualsize = fdt_off_dt_strings(blob) + fdt_size_dt_strings(blob) + 5 * sizeof(struct fdt_reserve_entry); actualsize += extrasize; /* Make it so the fdt ends on a page boundary */ actualsize = ALIGN(actualsize + ((uintptr_t)blob & 0xfff), 0x1000); actualsize = actualsize - ((uintptr_t)blob & 0xfff); /* Change the fdt header to reflect the correct size */ fdt_set_totalsize(blob, actualsize); /* Add the new reservation */ ret = fdt_add_mem_rsv(blob, (uintptr_t)blob, actualsize); if (ret < 0) return ret; return actualsize; }
int psci_update_dt(void *fdt) { /* * If the PSCI in SEC Firmware didn't work, avoid to update the * device node of PSCI. But still return 0 instead of an error * number to support detecting PSCI dynamically and then switching * the SMP boot method between PSCI and spin-table. */ if (sec_firmware_support_psci_version() == PSCI_INVALID_VER) return 0; fdt_psci(fdt); #if defined(CONFIG_ARMV8_PSCI) && !defined(CONFIG_ARMV8_SECURE_BASE) /* secure code lives in RAM, keep it alive */ fdt_add_mem_rsv(fdt, (unsigned long)__secure_start, __secure_end - __secure_start); #endif return 0; }
void spapr_load_rtas(sPAPRMachineState *spapr, void *fdt, hwaddr addr) { int rtas_node; int ret; /* Copy RTAS blob into guest RAM */ cpu_physical_memory_write(addr, spapr->rtas_blob, spapr->rtas_size); ret = fdt_add_mem_rsv(fdt, addr, spapr->rtas_size); if (ret < 0) { error_report("Couldn't add RTAS reserve entry: %s", fdt_strerror(ret)); exit(1); } /* Update the device tree with the blob's location */ rtas_node = fdt_path_offset(fdt, "/rtas"); assert(rtas_node >= 0); ret = fdt_setprop_cell(fdt, rtas_node, "linux,rtas-base", addr); if (ret < 0) { error_report("Couldn't add linux,rtas-base property: %s", fdt_strerror(ret)); exit(1); } ret = fdt_setprop_cell(fdt, rtas_node, "linux,rtas-entry", addr); if (ret < 0) { error_report("Couldn't add linux,rtas-entry property: %s", fdt_strerror(ret)); exit(1); } ret = fdt_setprop_cell(fdt, rtas_node, "rtas-size", spapr->rtas_size); if (ret < 0) { error_report("Couldn't add rtas-size property: %s", fdt_strerror(ret)); exit(1); } }
void ft_fixup_cpu(void *blob) { int off; __maybe_unused u64 spin_tbl_addr = (u64)get_spin_tbl_addr(); fdt32_t *reg; int addr_cells; u64 val, core_id; size_t *boot_code_size = &(__secondary_boot_code_size); u32 mask = cpu_pos_mask(); int off_prev = -1; off = fdt_path_offset(blob, "/cpus"); if (off < 0) { puts("couldn't find /cpus node\n"); return; } fdt_support_default_count_cells(blob, off, &addr_cells, NULL); off = fdt_node_offset_by_prop_value(blob, off_prev, "device_type", "cpu", 4); while (off != -FDT_ERR_NOTFOUND) { reg = (fdt32_t *)fdt_getprop(blob, off, "reg", 0); if (reg) { core_id = fdt_read_number(reg, addr_cells); if (!test_bit(id_to_core(core_id), &mask)) { fdt_del_node(blob, off); off = off_prev; } } off_prev = off; off = fdt_node_offset_by_prop_value(blob, off_prev, "device_type", "cpu", 4); } #if defined(CONFIG_ARMV8_SEC_FIRMWARE_SUPPORT) && \ defined(CONFIG_SEC_FIRMWARE_ARMV8_PSCI) int node; u32 psci_ver; /* Check the psci version to determine if the psci is supported */ psci_ver = sec_firmware_support_psci_version(); if (psci_ver == 0xffffffff) { /* remove psci DT node */ node = fdt_path_offset(blob, "/psci"); if (node >= 0) goto remove_psci_node; node = fdt_node_offset_by_compatible(blob, -1, "arm,psci"); if (node >= 0) goto remove_psci_node; node = fdt_node_offset_by_compatible(blob, -1, "arm,psci-0.2"); if (node >= 0) goto remove_psci_node; node = fdt_node_offset_by_compatible(blob, -1, "arm,psci-1.0"); if (node >= 0) goto remove_psci_node; remove_psci_node: if (node >= 0) fdt_del_node(blob, node); } else { return; } #endif off = fdt_path_offset(blob, "/cpus"); if (off < 0) { puts("couldn't find /cpus node\n"); return; } fdt_support_default_count_cells(blob, off, &addr_cells, NULL); off = fdt_node_offset_by_prop_value(blob, -1, "device_type", "cpu", 4); while (off != -FDT_ERR_NOTFOUND) { reg = (fdt32_t *)fdt_getprop(blob, off, "reg", 0); if (reg) { core_id = fdt_read_number(reg, addr_cells); if (core_id == 0 || (is_core_online(core_id))) { val = spin_tbl_addr; val += id_to_core(core_id) * SPIN_TABLE_ELEM_SIZE; val = cpu_to_fdt64(val); fdt_setprop_string(blob, off, "enable-method", "spin-table"); fdt_setprop(blob, off, "cpu-release-addr", &val, sizeof(val)); } else { debug("skipping offline core\n"); } } else { puts("Warning: found cpu node without reg property\n"); } off = fdt_node_offset_by_prop_value(blob, off, "device_type", "cpu", 4); } fdt_add_mem_rsv(blob, (uintptr_t)&secondary_boot_code, *boot_code_size); #if defined(CONFIG_EFI_LOADER) && !defined(CONFIG_SPL_BUILD) efi_add_memory_map((uintptr_t)&secondary_boot_code, ALIGN(*boot_code_size, EFI_PAGE_SIZE) >> EFI_PAGE_SHIFT, EFI_RESERVED_MEMORY_TYPE, false); #endif }
printf("libfdt fdt_get_mem_rsv(): %s\n", fdt_strerror(err)); return err; } printf(" %x\t%08x%08x\t%08x%08x\n", j, (u32)(addr >> 32), (u32)(addr & 0xffffffff), (u32)(size >> 32), (u32)(size & 0xffffffff)); } } else if (argv[2][0] == 'a') { uint64_t addr, size; int err; addr = simple_strtoull(argv[3], NULL, 16); size = simple_strtoull(argv[4], NULL, 16); err = fdt_add_mem_rsv(working_fdt, addr, size); if (err < 0) { printf("libfdt fdt_add_mem_rsv(): %s\n", fdt_strerror(err)); return err; } } else if (argv[2][0] == 'd') { unsigned long idx = simple_strtoul(argv[3], NULL, 16); int err = fdt_del_mem_rsv(working_fdt, idx); if (err < 0) { printf("libfdt fdt_del_mem_rsv(): %s\n", fdt_strerror(err)); return err; }
void ft_cpu_setup(void *blob, bd_t *bd) { int off; int val; const char *sysclk_path; struct ccsr_gur __iomem *gur = (void *)(CONFIG_SYS_FSL_GUTS_ADDR); unsigned int svr; svr = in_be32(&gur->svr); unsigned long busclk = get_bus_freq(0); /* delete crypto node if not on an E-processor */ if (!IS_E_PROCESSOR(svr)) fdt_fixup_crypto_node(blob, 0); #if CONFIG_SYS_FSL_SEC_COMPAT >= 4 else { ccsr_sec_t __iomem *sec; sec = (void __iomem *)CONFIG_SYS_FSL_SEC_ADDR; fdt_fixup_crypto_node(blob, sec_in32(&sec->secvid_ms)); } #endif off = fdt_node_offset_by_prop_value(blob, -1, "device_type", "cpu", 4); while (off != -FDT_ERR_NOTFOUND) { val = gd->cpu_clk; fdt_setprop(blob, off, "clock-frequency", &val, 4); off = fdt_node_offset_by_prop_value(blob, off, "device_type", "cpu", 4); } do_fixup_by_prop_u32(blob, "device_type", "soc", 4, "bus-frequency", busclk, 1); ft_fixup_enet_phy_connect_type(blob); #ifdef CONFIG_SYS_NS16550 do_fixup_by_compat_u32(blob, "fsl,16550-FIFO64", "clock-frequency", CONFIG_SYS_NS16550_CLK, 1); #endif sysclk_path = fdt_get_alias(blob, "sysclk"); if (sysclk_path) do_fixup_by_path_u32(blob, sysclk_path, "clock-frequency", CONFIG_SYS_CLK_FREQ, 1); do_fixup_by_compat_u32(blob, "fsl,qoriq-sysclk-2.0", "clock-frequency", CONFIG_SYS_CLK_FREQ, 1); #if defined(CONFIG_DEEP_SLEEP) && defined(CONFIG_SD_BOOT) #define UBOOT_HEAD_LEN 0x1000 /* * Reserved memory in SD boot deep sleep case. * Second stage uboot binary and malloc space should be reserved. * If the memory they occupied has not been reserved, then this * space would be used by kernel and overwritten in uboot when * deep sleep resume, which cause deep sleep failed. * Since second uboot binary has a head, that space need to be * reserved either(assuming its size is less than 0x1000). */ off = fdt_add_mem_rsv(blob, CONFIG_SYS_TEXT_BASE - UBOOT_HEAD_LEN, CONFIG_SYS_MONITOR_LEN + CONFIG_SYS_SPL_MALLOC_SIZE + UBOOT_HEAD_LEN); if (off < 0) printf("Failed to reserve memory for SD boot deep sleep: %s\n", fdt_strerror(off)); #endif #if defined(CONFIG_FSL_ESDHC) fdt_fixup_esdhc(blob, bd); #endif /* * platform bus clock = system bus clock/2 * Here busclk = system bus clock * We are using the platform bus clock as 1588 Timer reference * clock source select */ do_fixup_by_compat_u32(blob, "fsl, gianfar-ptp-timer", "timer-frequency", busclk / 2, 1); /* * clock-freq should change to clock-frequency and * flexcan-v1.0 should change to p1010-flexcan respectively * in the future. */ do_fixup_by_compat_u32(blob, "fsl, flexcan-v1.0", "clock_freq", busclk / 2, 1); do_fixup_by_compat_u32(blob, "fsl, flexcan-v1.0", "clock-frequency", busclk / 2, 1); do_fixup_by_compat_u32(blob, "fsl, ls1021a-flexcan", "clock-frequency", busclk / 2, 1); #if defined(CONFIG_QSPI_BOOT) || defined(CONFIG_SD_BOOT_QSPI) off = fdt_node_offset_by_compat_reg(blob, FSL_IFC_COMPAT, CONFIG_SYS_IFC_ADDR); fdt_set_node_status(blob, off, FDT_STATUS_DISABLED, 0); #else off = fdt_node_offset_by_compat_reg(blob, FSL_QSPI_COMPAT, QSPI0_BASE_ADDR); fdt_set_node_status(blob, off, FDT_STATUS_DISABLED, 0); off = fdt_node_offset_by_compat_reg(blob, FSL_DSPI_COMPAT, DSPI1_BASE_ADDR); fdt_set_node_status(blob, off, FDT_STATUS_DISABLED, 0); #endif }
(u32)(addr >> 32), (u32)(addr & 0xffffffff), (u32)(size >> 32), (u32)(size & 0xffffffff)); } } else if (argv[2][0] == 'a') { uint64_t addr, size; int err; #ifdef CFG_64BIT_STRTOUL addr = simple_strtoull(argv[3], NULL, 16); size = simple_strtoull(argv[4], NULL, 16); #else addr = simple_strtoul(argv[3], NULL, 16); size = simple_strtoul(argv[4], NULL, 16); #endif err = fdt_add_mem_rsv(fdt, addr, size); if (err < 0) { printf("libfdt fdt_add_mem_rsv(): %s\n", fdt_strerror(err)); return err; } } else if (argv[2][0] == 'd') { unsigned long idx = simple_strtoul(argv[3], NULL, 16); int err = fdt_del_mem_rsv(fdt, idx); if (err < 0) { printf("libfdt fdt_del_mem_rsv(): %s\n", fdt_strerror(err)); return err; }
int fdt_initrd(void *fdt, ulong initrd_start, ulong initrd_end, int force) { int nodeoffset; int err, j, total; u32 tmp; const char *path; uint64_t addr, size; /* Find the "chosen" node. */ nodeoffset = fdt_path_offset (fdt, "/chosen"); /* If there is no "chosen" node in the blob return */ if (nodeoffset < 0) { printf("fdt_initrd: %s\n", fdt_strerror(nodeoffset)); return nodeoffset; } /* just return if initrd_start/end aren't valid */ if ((initrd_start == 0) || (initrd_end == 0)) return 0; total = fdt_num_mem_rsv(fdt); /* * Look for an existing entry and update it. If we don't find * the entry, we will j be the next available slot. */ for (j = 0; j < total; j++) { err = fdt_get_mem_rsv(fdt, j, &addr, &size); if (addr == initrd_start) { fdt_del_mem_rsv(fdt, j); break; } } err = fdt_add_mem_rsv(fdt, initrd_start, initrd_end - initrd_start + 1); if (err < 0) { printf("fdt_initrd: %s\n", fdt_strerror(err)); return err; } path = fdt_getprop(fdt, nodeoffset, "linux,initrd-start", NULL); if ((path == NULL) || force) { tmp = __cpu_to_be32(initrd_start); err = fdt_setprop(fdt, nodeoffset, "linux,initrd-start", &tmp, sizeof(tmp)); if (err < 0) { printf("WARNING: " "could not set linux,initrd-start %s.\n", fdt_strerror(err)); return err; } tmp = __cpu_to_be32(initrd_end); err = fdt_setprop(fdt, nodeoffset, "linux,initrd-end", &tmp, sizeof(tmp)); if (err < 0) { printf("WARNING: could not set linux,initrd-end %s.\n", fdt_strerror(err)); return err; } } return 0; }
EFI_STATUS PrepareFdt ( IN CONST CHAR8* CommandLineArguments, IN EFI_PHYSICAL_ADDRESS InitrdImage, IN UINTN InitrdImageSize, IN OUT EFI_PHYSICAL_ADDRESS *FdtBlobBase, IN OUT UINTN *FdtBlobSize ) { EFI_STATUS Status; EFI_PHYSICAL_ADDRESS NewFdtBlobBase; EFI_PHYSICAL_ADDRESS NewFdtBlobAllocation; UINTN NewFdtBlobSize; VOID* fdt; INTN err; INTN node; INT32 lenp; CONST VOID* BootArg; EFI_PHYSICAL_ADDRESS InitrdImageStart; EFI_PHYSICAL_ADDRESS InitrdImageEnd; UINTN Index; UINTN MemoryMapSize; EFI_MEMORY_DESCRIPTOR *MemoryMap; EFI_MEMORY_DESCRIPTOR *MemoryMapPtr; UINTN MapKey; UINTN DescriptorSize; UINT32 DescriptorVersion; UINTN Pages; UINTN OriginalFdtSize; NewFdtBlobAllocation = 0; // // Sanity checks on the original FDT blob. // err = fdt_check_header ((VOID*)(UINTN)(*FdtBlobBase)); if (err != 0) { Print (L"ERROR: Device Tree header not valid (err:%d)\n", err); return EFI_INVALID_PARAMETER; } // The original FDT blob might have been loaded partially. // Check that it is not the case. OriginalFdtSize = (UINTN)fdt_totalsize ((VOID*)(UINTN)(*FdtBlobBase)); if (OriginalFdtSize > *FdtBlobSize) { Print (L"ERROR: Incomplete FDT. Only %d/%d bytes have been loaded.\n", *FdtBlobSize, OriginalFdtSize); return EFI_INVALID_PARAMETER; } // // Relocate the FDT to its final location. // Status = RelocateFdt (*FdtBlobBase, OriginalFdtSize, &NewFdtBlobBase, &NewFdtBlobSize, &NewFdtBlobAllocation); if (EFI_ERROR (Status)) { goto FAIL_RELOCATE_FDT; } fdt = (VOID*)(UINTN)NewFdtBlobBase; node = fdt_subnode_offset (fdt, 0, "chosen"); if (node < 0) { // The 'chosen' node does not exist, create it node = fdt_add_subnode (fdt, 0, "chosen"); if (node < 0) { DEBUG ((EFI_D_ERROR, "Error on finding 'chosen' node\n")); Status = EFI_INVALID_PARAMETER; goto FAIL_COMPLETE_FDT; } } DEBUG_CODE_BEGIN (); BootArg = fdt_getprop (fdt, node, "bootargs", &lenp); if (BootArg != NULL) { DEBUG ((EFI_D_ERROR, "BootArg: %a\n", BootArg)); } DEBUG_CODE_END (); // // Set Linux CmdLine // if ((CommandLineArguments != NULL) && (AsciiStrLen (CommandLineArguments) > 0)) { err = fdt_setprop (fdt, node, "bootargs", CommandLineArguments, AsciiStrSize (CommandLineArguments)); if (err) { DEBUG ((EFI_D_ERROR, "Fail to set new 'bootarg' (err:%d)\n", err)); } } // // Set Linux Initrd // if (InitrdImageSize != 0) { InitrdImageStart = cpu_to_fdt64 (InitrdImage); err = fdt_setprop (fdt, node, "linux,initrd-start", &InitrdImageStart, sizeof (EFI_PHYSICAL_ADDRESS)); if (err) { DEBUG ((EFI_D_ERROR, "Fail to set new 'linux,initrd-start' (err:%d)\n", err)); } InitrdImageEnd = cpu_to_fdt64 (InitrdImage + InitrdImageSize); err = fdt_setprop (fdt, node, "linux,initrd-end", &InitrdImageEnd, sizeof (EFI_PHYSICAL_ADDRESS)); if (err) { DEBUG ((EFI_D_ERROR, "Fail to set new 'linux,initrd-start' (err:%d)\n", err)); } } // // Add the memory regions reserved by the UEFI Firmware // // Retrieve the UEFI Memory Map MemoryMap = NULL; MemoryMapSize = 0; Status = gBS->GetMemoryMap (&MemoryMapSize, MemoryMap, &MapKey, &DescriptorSize, &DescriptorVersion); if (Status == EFI_BUFFER_TOO_SMALL) { // The UEFI specification advises to allocate more memory for the MemoryMap buffer between successive // calls to GetMemoryMap(), since allocation of the new buffer may potentially increase memory map size. Pages = EFI_SIZE_TO_PAGES (MemoryMapSize) + 1; MemoryMap = AllocatePages (Pages); if (MemoryMap == NULL) { Status = EFI_OUT_OF_RESOURCES; goto FAIL_COMPLETE_FDT; } Status = gBS->GetMemoryMap (&MemoryMapSize, MemoryMap, &MapKey, &DescriptorSize, &DescriptorVersion); } // Go through the list and add the reserved region to the Device Tree if (!EFI_ERROR (Status)) { MemoryMapPtr = MemoryMap; for (Index = 0; Index < (MemoryMapSize / DescriptorSize); Index++) { if (IsLinuxReservedRegion ((EFI_MEMORY_TYPE)MemoryMapPtr->Type)) { DEBUG ((DEBUG_VERBOSE, "Reserved region of type %d [0x%lX, 0x%lX]\n", MemoryMapPtr->Type, (UINTN)MemoryMapPtr->PhysicalStart, (UINTN)(MemoryMapPtr->PhysicalStart + MemoryMapPtr->NumberOfPages * EFI_PAGE_SIZE))); err = fdt_add_mem_rsv (fdt, MemoryMapPtr->PhysicalStart, MemoryMapPtr->NumberOfPages * EFI_PAGE_SIZE); if (err != 0) { Print (L"Warning: Fail to add 'memreserve' (err:%d)\n", err); } } MemoryMapPtr = (EFI_MEMORY_DESCRIPTOR*)((UINTN)MemoryMapPtr + DescriptorSize); } } // Update the real size of the Device Tree fdt_pack ((VOID*)(UINTN)(NewFdtBlobBase)); *FdtBlobBase = NewFdtBlobBase; *FdtBlobSize = (UINTN)fdt_totalsize ((VOID*)(UINTN)(NewFdtBlobBase)); return EFI_SUCCESS; FAIL_COMPLETE_FDT: gBS->FreePages (NewFdtBlobAllocation, EFI_SIZE_TO_PAGES (NewFdtBlobSize)); FAIL_RELOCATE_FDT: *FdtBlobSize = (UINTN)fdt_totalsize ((VOID*)(UINTN)(*FdtBlobBase)); // Return success even if we failed to update the FDT blob. // The original one is still valid. return EFI_SUCCESS; }
int fdt_chosen(void *fdt, ulong initrd_start, ulong initrd_end, int force) { int nodeoffset; int err; u32 tmp; /* used to set 32 bit integer properties */ char *str; /* used to set string properties */ err = fdt_check_header(fdt); if (err < 0) { printf("fdt_chosen: %s\n", fdt_strerror(err)); return err; } if (initrd_start && initrd_end) { uint64_t addr, size; int total = fdt_num_mem_rsv(fdt); int j; /* * Look for an existing entry and update it. If we don't find * the entry, we will j be the next available slot. */ for (j = 0; j < total; j++) { err = fdt_get_mem_rsv(fdt, j, &addr, &size); if (addr == initrd_start) { fdt_del_mem_rsv(fdt, j); break; } } err = fdt_add_mem_rsv(fdt, initrd_start, initrd_end - initrd_start + 1); if (err < 0) { printf("fdt_chosen: %s\n", fdt_strerror(err)); return err; } } /* * Find the "chosen" node. */ nodeoffset = fdt_path_offset (fdt, "/chosen"); /* * If we have a "chosen" node already the "force the writing" * is not set, our job is done. */ if ((nodeoffset >= 0) && !force) return 0; /* * No "chosen" node in the blob: create it. */ if (nodeoffset < 0) { /* * Create a new node "/chosen" (offset 0 is root level) */ nodeoffset = fdt_add_subnode(fdt, 0, "chosen"); if (nodeoffset < 0) { printf("WARNING: could not create /chosen %s.\n", fdt_strerror(nodeoffset)); return nodeoffset; } } /* * Update pre-existing properties, create them if non-existant. */ str = getenv("bootargs"); if (str != NULL) { err = fdt_setprop(fdt, nodeoffset, "bootargs", str, strlen(str)+1); if (err < 0) printf("WARNING: could not set bootargs %s.\n", fdt_strerror(err)); } if (initrd_start && initrd_end) { tmp = __cpu_to_be32(initrd_start); err = fdt_setprop(fdt, nodeoffset, "linux,initrd-start", &tmp, sizeof(tmp)); if (err < 0) printf("WARNING: " "could not set linux,initrd-start %s.\n", fdt_strerror(err)); tmp = __cpu_to_be32(initrd_end); err = fdt_setprop(fdt, nodeoffset, "linux,initrd-end", &tmp, sizeof(tmp)); if (err < 0) printf("WARNING: could not set linux,initrd-end %s.\n", fdt_strerror(err)); } #ifdef OF_STDOUT_PATH err = fdt_setprop(fdt, nodeoffset, "linux,stdout-path", OF_STDOUT_PATH, strlen(OF_STDOUT_PATH)+1); if (err < 0) printf("WARNING: could not set linux,stdout-path %s.\n", fdt_strerror(err)); #endif return err; }
/** * elf_exec_load - load ELF executable image * @lowest_load_addr: On return, will be the address where the first PT_LOAD * section will be loaded in memory. * * Return: * 0 on success, negative value on failure. */ static int elf_exec_load(struct kimage *image, struct elfhdr *ehdr, struct elf_info *elf_info, unsigned long *lowest_load_addr) { unsigned long base = 0, lowest_addr = UINT_MAX; int ret; size_t i; struct kexec_buf kbuf = { .image = image, .buf_max = ppc64_rma_size, .top_down = false }; /* Read in the PT_LOAD segments. */ for (i = 0; i < ehdr->e_phnum; i++) { unsigned long load_addr; size_t size; const struct elf_phdr *phdr; phdr = &elf_info->proghdrs[i]; if (phdr->p_type != PT_LOAD) continue; size = phdr->p_filesz; if (size > phdr->p_memsz) size = phdr->p_memsz; kbuf.buffer = (void *) elf_info->buffer + phdr->p_offset; kbuf.bufsz = size; kbuf.memsz = phdr->p_memsz; kbuf.buf_align = phdr->p_align; kbuf.buf_min = phdr->p_paddr + base; ret = kexec_add_buffer(&kbuf); if (ret) goto out; load_addr = kbuf.mem; if (load_addr < lowest_addr) lowest_addr = load_addr; } /* Update entry point to reflect new load address. */ ehdr->e_entry += base; *lowest_load_addr = lowest_addr; ret = 0; out: return ret; } void *elf64_load(struct kimage *image, char *kernel_buf, unsigned long kernel_len, char *initrd, unsigned long initrd_len, char *cmdline, unsigned long cmdline_len) { int i, ret; unsigned int fdt_size; unsigned long kernel_load_addr, purgatory_load_addr; unsigned long initrd_load_addr, fdt_load_addr, stack_top; void *fdt; const void *slave_code; struct elfhdr ehdr; struct elf_info elf_info; struct fdt_reserve_entry *rsvmap; struct kexec_buf kbuf = { .image = image, .buf_min = 0, .buf_max = ppc64_rma_size }; ret = build_elf_exec_info(kernel_buf, kernel_len, &ehdr, &elf_info); if (ret) goto out; ret = elf_exec_load(image, &ehdr, &elf_info, &kernel_load_addr); if (ret) goto out; pr_debug("Loaded the kernel at 0x%lx\n", kernel_load_addr); ret = kexec_load_purgatory(image, 0, ppc64_rma_size, true, &purgatory_load_addr); if (ret) { pr_err("Loading purgatory failed.\n"); goto out; } pr_debug("Loaded purgatory at 0x%lx\n", purgatory_load_addr); if (initrd != NULL) { kbuf.buffer = initrd; kbuf.bufsz = kbuf.memsz = initrd_len; kbuf.buf_align = PAGE_SIZE; kbuf.top_down = false; ret = kexec_add_buffer(&kbuf); if (ret) goto out; initrd_load_addr = kbuf.mem; pr_debug("Loaded initrd at 0x%lx\n", initrd_load_addr); } fdt_size = fdt_totalsize(initial_boot_params) * 2; fdt = kmalloc(fdt_size, GFP_KERNEL); if (!fdt) { pr_err("Not enough memory for the device tree.\n"); ret = -ENOMEM; goto out; } ret = fdt_open_into(initial_boot_params, fdt, fdt_size); if (ret < 0) { pr_err("Error setting up the new device tree.\n"); ret = -EINVAL; goto out; } ret = setup_new_fdt(image, fdt, initrd_load_addr, initrd_len, cmdline); if (ret) goto out; /* * Documentation/devicetree/booting-without-of.txt says we need to * add a reservation entry for the device tree block, but * early_init_fdt_reserve_self reserves the memory even if there's no * such entry. We'll add a reservation entry anyway, to be safe and * compliant. * * Use dummy values, we will correct them in a moment. */ ret = fdt_add_mem_rsv(fdt, 1, 1); if (ret) { pr_err("Error reserving device tree memory: %s\n", fdt_strerror(ret)); ret = -EINVAL; goto out; } fdt_pack(fdt); kbuf.buffer = fdt; kbuf.bufsz = kbuf.memsz = fdt_size; kbuf.buf_align = PAGE_SIZE; kbuf.top_down = true; ret = kexec_add_buffer(&kbuf); if (ret) goto out; fdt_load_addr = kbuf.mem; /* * Fix fdt reservation, now that we now where it will be loaded * and how big it is. */ rsvmap = fdt + fdt_off_mem_rsvmap(fdt); i = fdt_num_mem_rsv(fdt) - 1; rsvmap[i].address = cpu_to_fdt64(fdt_load_addr); rsvmap[i].size = cpu_to_fdt64(fdt_totalsize(fdt)); pr_debug("Loaded device tree at 0x%lx\n", fdt_load_addr); kbuf.memsz = PURGATORY_STACK_SIZE; kbuf.buf_align = PAGE_SIZE; kbuf.top_down = true; ret = kexec_locate_mem_hole(&kbuf); if (ret) { pr_err("Couldn't find free memory for the purgatory stack.\n"); ret = -ENOMEM; goto out; } stack_top = kbuf.mem + PURGATORY_STACK_SIZE - 1; pr_debug("Purgatory stack is at 0x%lx\n", stack_top); slave_code = elf_info.buffer + elf_info.proghdrs[0].p_offset; ret = setup_purgatory(image, slave_code, fdt, kernel_load_addr, fdt_load_addr, stack_top, find_debug_console(fdt)); if (ret) pr_err("Error setting up the purgatory.\n"); out: elf_free_info(&elf_info); /* Make kimage_file_post_load_cleanup free the fdt buffer for us. */ return ret ? ERR_PTR(ret) : fdt; } struct kexec_file_ops kexec_elf64_ops = { .probe = elf64_probe, .load = elf64_load, };
static int new_style_reservation(void *fdt, int reserve_initrd) { int nodeoffset; const void *p; fdt64_t *ranges, *range; char *names, *name; int ranges_len, names_len; nodeoffset = fdt_path_offset(fdt, "/"); if (nodeoffset < 0) { fprintf(stderr, "Device tree has no root node\n"); exit(1); } p = fdt_getprop(fdt, nodeoffset, "reserved-ranges", &ranges_len); if (!p && ranges_len == -FDT_ERR_NOTFOUND) return 0; if (!p) { fprintf(stderr, "getprop reserved-ranges returned %d\n", ranges_len); exit(1); } ranges = malloc(ranges_len); if (!ranges) { perror("malloc"); exit(1); } memcpy(ranges, p, ranges_len); p = fdt_getprop(fdt, nodeoffset, "reserved-names", &names_len); if (!p && names_len == -FDT_ERR_NOTFOUND) return 0; if (!p) { fprintf(stderr, "getprop reserved-names returned %d\n", names_len); exit(1); } names = malloc(names_len); if (!names) { perror("malloc"); exit(1); } memcpy(names, p, names_len); name = names; range = ranges; while (ranges_len > 0 && names_len > 0) { uint64_t start, size; start = fdt64_to_cpu(*range++); size = fdt64_to_cpu(*range++); #ifdef DEBUG printf("%s %lx %lx\n", name, start, size); #endif if (!reserve_initrd && !strcmp(name, "linux,initramfs")) continue; simple_alloc_at(kexec_map, start, size); if (fdt_add_mem_rsv(fdt, start, size)) perror("fdt_add_mem_rsv"); ranges_len -= 2 * sizeof(uint64_t); names_len -= strlen(name) + 1; name += strlen(name) + 1; } free(ranges); free(names); return 1; }
void kexec_memory_map(void *fdt, int reserve_initrd) { uint64_t start, size, end; int nodeoffset; int lpar = 0; kexec_map = simple_init(); /* Work out if we are in LPAR mode */ nodeoffset = fdt_path_offset(fdt, "/rtas"); if (nodeoffset >= 0) { if (fdt_getprop(fdt, nodeoffset, "ibm,hypertas-functions", NULL)) lpar = 1; } /* First find our memory */ nodeoffset = fdt_path_offset(fdt, "/"); if (nodeoffset < 0) { fprintf(stderr, "Device tree has no root node\n"); exit(1); } while (1) { const char *type; int len; const fdt64_t *reg; nodeoffset = fdt_next_node(fdt, nodeoffset, NULL); if (nodeoffset < 0) break; type = fdt_getprop(fdt, nodeoffset, "device_type", NULL); if (!type || strcmp(type, "memory")) continue; reg = fdt_getprop(fdt, nodeoffset, "reg", &len); while (len) { start = fdt64_to_cpu(*reg++); size = fdt64_to_cpu(*reg++); len -= 2 * sizeof(uint64_t); if (lpar == 1) { /* Only use the RMA region for LPAR */ if (start == 0) { if (size > MEMORY_CAP) size = MEMORY_CAP; simple_free(kexec_map, 0, size); mem_top = size; } } else { if (start >= MEMORY_CAP) continue; if ((start + size) > MEMORY_CAP) size = MEMORY_CAP - start; simple_free(kexec_map, start, size); if ((start + size) > mem_top) mem_top = start + size; } } } /* Reserve the kernel */ nodeoffset = fdt_path_offset(fdt, "/chosen"); if (nodeoffset < 0) { fprintf(stderr, "Device tree has no chosen node\n"); exit(1); } /* * XXX FIXME: Need to add linux,kernel-start property to the * kernel to handle relocatable kernels. */ start = 0; if (getprop_u64(fdt, nodeoffset, "linux,kernel-end", &end)) { fprintf(stderr, "getprop linux,kernel-end failed\n"); exit(1); } simple_alloc_at(kexec_map, start, end - start); /* Reserve the MMU hashtable in non LPAR mode */ if (lpar == 0) { if (getprop_u64(fdt, nodeoffset, "linux,htab-base", &start) || getprop_u64(fdt, nodeoffset, "linux,htab-size", &size)) { fprintf(stderr, "Could not find linux,htab-base or " "linux,htab-size properties\n"); exit(1); } if (start < mem_top) simple_alloc_at(kexec_map, start, size); } /* XXX FIXME: Reserve TCEs in kexec_map */ if (new_style_reservation(fdt, reserve_initrd)) return; /* Reserve the initrd if requested */ if (reserve_initrd && !getprop_u64(fdt, nodeoffset, "linux,initrd-start", &start) && !getprop_u64(fdt, nodeoffset, "linux,initrd-end", &end)) { if (start < mem_top) simple_alloc_at(kexec_map, start, end - start); } /* Reserve RTAS */ nodeoffset = fdt_path_offset(fdt, "/rtas"); if (nodeoffset > 0) { uint32_t rtas_start, rtas_size; if (getprop_u32(fdt, nodeoffset, "linux,rtas-base", &rtas_start)) { fprintf(stderr, "getprop linux,rtas-base failed\n"); exit(1); } if (getprop_u32(fdt, nodeoffset, "rtas-size", &rtas_size)) { fprintf(stderr, "getprop rtas-size failed\n"); exit(1); } simple_alloc_at(kexec_map, rtas_start, rtas_size); if (fdt_add_mem_rsv(fdt, rtas_start, rtas_size)) perror("fdt_add_mem_rsv"); } nodeoffset = fdt_path_offset(fdt, "/ibm,opal"); if (nodeoffset > 0) { uint64_t opal_start, opal_size; if (getprop_u64(fdt, nodeoffset, "opal-base-address", &opal_start)) { fprintf(stderr, "getprop opal-base-address failed\n"); exit(1); } if (getprop_u64(fdt, nodeoffset, "opal-runtime-size", &opal_size)) { fprintf(stderr, "getprop opal-runtime-size failed\n"); exit(1); } simple_alloc_at(kexec_map, opal_start, opal_size); if (fdt_add_mem_rsv(fdt, opal_start, opal_size)) perror("fdt_add_mem_rsv"); } }