static int setup_dtb_prop(char **bufp, off_t *sizep, const char *node_name, const char *prop_name, const void *val, int len) { char *dtb_buf; off_t dtb_size; int off; int prop_len = 0; const struct fdt_property *prop; if ((bufp == NULL) || (sizep == NULL) || (*bufp == NULL)) die("Internal error\n"); dtb_buf = *bufp; dtb_size = *sizep; /* check if the subnode has already exist */ off = fdt_path_offset(dtb_buf, node_name); if (off == -FDT_ERR_NOTFOUND) { dtb_size += fdt_node_len(node_name); fdt_set_totalsize(dtb_buf, dtb_size); dtb_buf = xrealloc(dtb_buf, dtb_size); if (dtb_buf == NULL) die("xrealloc failed\n"); off = fdt_add_subnode(dtb_buf, off, node_name); } if (off < 0) { fprintf(stderr, "FDT: Error adding %s node.\n", node_name); return -1; } prop = fdt_get_property(dtb_buf, off, prop_name, &prop_len); if ((prop == NULL) && (prop_len != -FDT_ERR_NOTFOUND)) { die("FDT: fdt_get_property"); } else if (prop == NULL) { /* prop_len == -FDT_ERR_NOTFOUND */ /* prop doesn't exist */ dtb_size += fdt_prop_len(prop_name, len); } else { if (prop_len < len) dtb_size += len - prop_len; } if (fdt_totalsize(dtb_buf) < dtb_size) { fdt_set_totalsize(dtb_buf, dtb_size); dtb_buf = xrealloc(dtb_buf, dtb_size); if (dtb_buf == NULL) die("xrealloc failed\n"); } if (fdt_setprop(dtb_buf, off, prop_name, val, len) != 0) { fprintf(stderr, "FDT: Error setting %s/%s property.\n", node_name, prop_name); return -1; } *bufp = dtb_buf; *sizep = dtb_size; return 0; }
int fdt_resize(void *fdt, void *buf, int bufsize) { size_t headsize, tailsize; char *oldtail, *newtail; FDT_SW_CHECK_HEADER(fdt); headsize = fdt_off_dt_struct(fdt); tailsize = fdt_size_dt_strings(fdt); if ((headsize + tailsize) > bufsize) return -FDT_ERR_NOSPACE; oldtail = (char *)fdt + fdt_totalsize(fdt) - tailsize; newtail = (char *)buf + bufsize - tailsize; /* Two cases to avoid clobbering data if the old and new * buffers partially overlap */ if (buf <= fdt) { memmove(buf, fdt, headsize); memmove(newtail, oldtail, tailsize); } else { memmove(newtail, oldtail, tailsize); memmove(buf, fdt, headsize); } fdt_set_off_dt_strings(buf, bufsize); fdt_set_totalsize(buf, bufsize); return 0; }
int fdt_finish(void *fdt) { int err = check_header_sw(fdt); char *p = (char *)fdt; uint32_t *end; int oldstroffset, newstroffset; uint32_t tag; int offset, nextoffset; if (err) return err; /* Add terminator */ end = grab_space(fdt, sizeof(*end)); if (! end) return -FDT_ERR_NOSPACE; *end = cpu_to_fdt32(FDT_END); /* Relocate the string table */ oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt); newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt)); fdt_set_off_dt_strings(fdt, newstroffset); /* Walk the structure, correcting string offsets */ offset = 0; while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) { if (tag == FDT_PROP) { struct fdt_property *prop = fdt_offset_ptr_w(fdt, offset, sizeof(*prop)); int nameoff; if (! prop) return -FDT_ERR_BADSTRUCTURE; nameoff = fdt32_to_cpu(prop->nameoff); nameoff += fdt_size_dt_strings(fdt); prop->nameoff = cpu_to_fdt32(nameoff); } offset = nextoffset; } /* Finally, adjust the header */ fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt)); fdt_set_magic(fdt, FDT_MAGIC); return 0; }
int fdt_finish(void *fdt) { char *p = (char *)fdt; uint32_t *end; int oldstroffset, newstroffset; uint32_t tag; int offset, nextoffset; FDT_SW_CHECK_HEADER(fdt); end = _fdt_grab_space(fdt, sizeof(*end)); if (! end) return -FDT_ERR_NOSPACE; *end = cpu_to_fdt32(FDT_END); oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt); newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt)); fdt_set_off_dt_strings(fdt, newstroffset); offset = 0; while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) { if (tag == FDT_PROP) { struct fdt_property *prop = _fdt_offset_ptr_w(fdt, offset); int nameoff; nameoff = fdt32_to_cpu(prop->nameoff); nameoff += fdt_size_dt_strings(fdt); prop->nameoff = cpu_to_fdt32(nameoff); } offset = nextoffset; } if (nextoffset < 0) return nextoffset; fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt)); fdt_set_magic(fdt, FDT_MAGIC); return 0; }
/* 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 fdt_create(void *buf, int bufsize) { void *fdt = buf; if (bufsize < (int)sizeof(struct fdt_header)) return -FDT_ERR_NOSPACE; memset(buf, 0, bufsize); fdt_set_magic(fdt, FDT_SW_MAGIC); fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION); fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION); fdt_set_totalsize(fdt, bufsize); fdt_set_off_mem_rsvmap(fdt, FDT_ALIGN(sizeof(struct fdt_header), sizeof(struct fdt_reserve_entry))); fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt)); fdt_set_off_dt_strings(fdt, bufsize); return 0; }
int image_setup_libfdt(bootm_headers_t *images, void *blob, int of_size, struct lmb *lmb) { ulong *initrd_start = &images->initrd_start; ulong *initrd_end = &images->initrd_end; int ret; if (fdt_chosen(blob, 1) < 0) { puts("ERROR: /chosen node create failed"); puts(" - must RESET the board to recover.\n"); return -1; } arch_fixup_memory_node(blob); if (IMAGE_OF_BOARD_SETUP) ft_board_setup(blob, gd->bd); fdt_fixup_ethernet(blob); /* Delete the old LMB reservation */ lmb_free(lmb, (phys_addr_t)(u32)(uintptr_t)blob, (phys_size_t)fdt_totalsize(blob)); ret = fdt_resize(blob); if (ret < 0) return ret; of_size = ret; if (*initrd_start && *initrd_end) { of_size += FDT_RAMDISK_OVERHEAD; fdt_set_totalsize(blob, of_size); } /* Create a new LMB reservation */ lmb_reserve(lmb, (ulong)blob, of_size); fdt_initrd(blob, *initrd_start, *initrd_end, 1); if (!ft_verify_fdt(blob)) return -1; return 0; }
static void *copy_fdt(void *fdt) { u64 fdt_size = fdt_totalsize(fdt); unsigned long fdt_ram_start = -1L, fdt_pages; u64 new_fdt_addr; void *new_fdt; int i; for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { u64 ram_start = gd->bd->bi_dram[i].start; u64 ram_size = gd->bd->bi_dram[i].size; if (!ram_size) continue; if (ram_start < fdt_ram_start) fdt_ram_start = ram_start; } /* Give us at least 4kb breathing room */ fdt_size = ALIGN(fdt_size + 4096, 4096); fdt_pages = fdt_size >> EFI_PAGE_SHIFT; /* Safe fdt location is at 128MB */ new_fdt_addr = fdt_ram_start + (128 * 1024 * 1024) + fdt_size; if (efi_allocate_pages(1, EFI_BOOT_SERVICES_DATA, fdt_pages, &new_fdt_addr) != EFI_SUCCESS) { /* If we can't put it there, put it somewhere */ new_fdt_addr = (ulong)memalign(4096, fdt_size); } new_fdt = (void*)(ulong)new_fdt_addr; memcpy(new_fdt, fdt, fdt_totalsize(fdt)); fdt_set_totalsize(new_fdt, fdt_size); return new_fdt; }
/** * dump_fdt_regions() - Dump regions of an FDT as binary data * * This dumps an FDT as binary, but only certain regions of it. This is the * final stage of the grep - we have a list of regions we want to dump, * and this function dumps them. * * The output of this function may or may not be a valid FDT. To ensure it * is, these disp->flags must be set: * * FDT_REG_SUPERNODES: ensures that subnodes are preceeded by their * parents. Without this option, fragments of subnode data may be * output without the supernodes above them. This is useful for * hashing but cannot produce a valid FDT. * FDT_REG_ADD_STRING_TAB: Adds a string table to the end of the FDT. * Without this none of the properties will have names * FDT_REG_ADD_MEM_RSVMAP: Adds a mem_rsvmap table - an FDT is invalid * without this. * * @disp: Display structure, holding info about our options * @blob: FDT blob to display * @region: List of regions to display * @count: Number of regions * @out: Output destination */ static int dump_fdt_regions(struct display_info *disp, const void *blob, struct fdt_region region[], int count, char *out) { struct fdt_header *fdt; int size, struct_start; int ptr; int i; /* Set up a basic header (even if we don't actually write it) */ fdt = (struct fdt_header *)out; memset(fdt, '\0', sizeof(*fdt)); fdt_set_magic(fdt, FDT_MAGIC); struct_start = FDT_ALIGN(sizeof(struct fdt_header), sizeof(struct fdt_reserve_entry)); fdt_set_off_mem_rsvmap(fdt, struct_start); fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION); fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION); /* * Calculate the total size of the regions we are writing out. The * first will be the mem_rsvmap if the FDT_REG_ADD_MEM_RSVMAP flag * is set. The last will be the string table if FDT_REG_ADD_STRING_TAB * is set. */ for (i = size = 0; i < count; i++) size += region[i].size; /* Bring in the mem_rsvmap section from the old file if requested */ if (count > 0 && (disp->flags & FDT_REG_ADD_MEM_RSVMAP)) { struct_start += region[0].size; size -= region[0].size; } fdt_set_off_dt_struct(fdt, struct_start); /* Update the header to have the correct offsets/sizes */ if (count >= 2 && (disp->flags & FDT_REG_ADD_STRING_TAB)) { int str_size; str_size = region[count - 1].size; fdt_set_size_dt_struct(fdt, size - str_size); fdt_set_off_dt_strings(fdt, struct_start + size - str_size); fdt_set_size_dt_strings(fdt, str_size); fdt_set_totalsize(fdt, struct_start + size); } /* Write the header if required */ ptr = 0; if (disp->header) { ptr = sizeof(*fdt); while (ptr < fdt_off_mem_rsvmap(fdt)) out[ptr++] = '\0'; } /* Output all the nodes including any mem_rsvmap/string table */ for (i = 0; i < count; i++) { struct fdt_region *reg = ®ion[i]; memcpy(out + ptr, (const char *)blob + reg->offset, reg->size); ptr += reg->size; } return ptr; }
int image_setup_libfdt(bootm_headers_t *images, void *blob, int of_size, struct lmb *lmb) { ulong *initrd_start = &images->initrd_start; ulong *initrd_end = &images->initrd_end; int ret = -EPERM; int fdt_ret; if (fdt_root(blob) < 0) { printf("ERROR: root node setup failed\n"); goto err; } if (fdt_chosen(blob) < 0) { printf("ERROR: /chosen node create failed\n"); goto err; } if (arch_fixup_fdt(blob) < 0) { printf("ERROR: arch-specific fdt fixup failed\n"); goto err; } if (IMAGE_OF_BOARD_SETUP) { fdt_ret = ft_board_setup(blob, gd->bd); if (fdt_ret) { printf("ERROR: board-specific fdt fixup failed: %s\n", fdt_strerror(fdt_ret)); goto err; } } if (IMAGE_OF_SYSTEM_SETUP) { fdt_ret = ft_system_setup(blob, gd->bd); if (fdt_ret) { printf("ERROR: system-specific fdt fixup failed: %s\n", fdt_strerror(fdt_ret)); goto err; } } fdt_fixup_ethernet(blob); /* Delete the old LMB reservation */ lmb_free(lmb, (phys_addr_t)(u32)(uintptr_t)blob, (phys_size_t)fdt_totalsize(blob)); ret = fdt_shrink_to_minimum(blob); if (ret < 0) goto err; of_size = ret; if (*initrd_start && *initrd_end) { of_size += FDT_RAMDISK_OVERHEAD; fdt_set_totalsize(blob, of_size); } /* Create a new LMB reservation */ lmb_reserve(lmb, (ulong)blob, of_size); fdt_initrd(blob, *initrd_start, *initrd_end); if (!ft_verify_fdt(blob)) goto err; #if defined(CONFIG_SOC_KEYSTONE) if (IMAGE_OF_BOARD_SETUP) ft_board_setup_ex(blob, gd->bd); #endif return 0; err: printf(" - must RESET the board to recover.\n\n"); return ret; }
/** * boot_relocate_fdt - relocate flat device tree * @lmb: pointer to lmb handle, will be used for memory mgmt * @of_flat_tree: pointer to a char* variable, will hold fdt start address * @of_size: pointer to a ulong variable, will hold fdt length * * boot_relocate_fdt() allocates a region of memory within the bootmap and * relocates the of_flat_tree into that region, even if the fdt is already in * the bootmap. It also expands the size of the fdt by CONFIG_SYS_FDT_PAD * bytes. * * of_flat_tree and of_size are set to final (after relocation) values * * returns: * 0 - success * 1 - failure */ int boot_relocate_fdt(struct lmb *lmb, char **of_flat_tree, ulong *of_size) { void *fdt_blob = *of_flat_tree; void *of_start = NULL; char *fdt_high; ulong of_len = 0; int err; int disable_relocation = 0; /* nothing to do */ if (*of_size == 0) return 0; if (fdt_check_header(fdt_blob) != 0) { fdt_error("image is not a fdt"); goto error; } /* position on a 4K boundary before the alloc_current */ /* Pad the FDT by a specified amount */ of_len = *of_size + CONFIG_SYS_FDT_PAD; /* If fdt_high is set use it to select the relocation address */ fdt_high = getenv("fdt_high"); if (fdt_high) { void *desired_addr = (void *)simple_strtoul(fdt_high, NULL, 16); if (((ulong) desired_addr) == ~0UL) { /* All ones means use fdt in place */ of_start = fdt_blob; lmb_reserve(lmb, (ulong)of_start, of_len); disable_relocation = 1; } else if (desired_addr) { of_start = (void *)(ulong) lmb_alloc_base(lmb, of_len, 0x1000, (ulong)desired_addr); if (of_start == NULL) { puts("Failed using fdt_high value for Device Tree"); goto error; } } else { of_start = (void *)(ulong) lmb_alloc(lmb, of_len, 0x1000); } } else { of_start = (void *)(ulong) lmb_alloc_base(lmb, of_len, 0x1000, getenv_bootm_mapsize() + getenv_bootm_low()); } if (of_start == NULL) { puts("device tree - allocation error\n"); goto error; } if (disable_relocation) { /* * We assume there is space after the existing fdt to use * for padding */ fdt_set_totalsize(of_start, of_len); printf(" Using Device Tree in place at %p, end %p\n", of_start, of_start + of_len - 1); } else { debug("## device tree at %p ... %p (len=%ld [0x%lX])\n", fdt_blob, fdt_blob + *of_size - 1, of_len, of_len); printf(" Loading Device Tree to %p, end %p ... ", of_start, of_start + of_len - 1); err = fdt_open_into(fdt_blob, of_start, of_len); if (err != 0) { fdt_error("fdt move failed"); goto error; } puts("OK\n"); } *of_flat_tree = of_start; *of_size = of_len; set_working_fdt_addr((ulong)*of_flat_tree); return 0; error: return 1; }