X-Git-Url: https://git.karo-electronics.de/?a=blobdiff_plain;f=tools%2Fifdtool.c;h=48059c02b550ee8db3cbfc7c9702b152936a0d4e;hb=34f1c9fe14172b89141254522e73ebfb6493554e;hp=fe8366ba4f6fb16a871676356817e4bcb75862c2;hpb=87b27c7aa73241d1eb244048f7aee06fc24af4ef;p=karo-tx-uboot.git diff --git a/tools/ifdtool.c b/tools/ifdtool.c index fe8366ba4f..48059c02b5 100644 --- a/tools/ifdtool.c +++ b/tools/ifdtool.c @@ -462,7 +462,7 @@ static int write_regions(char *image, int size) if (ret) return ret; dump_region(i, frba); - if (region.size == 0) + if (region.size <= 0) continue; region_fd = open(region_filename(i), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | @@ -706,10 +706,13 @@ int inject_region(char *image, int size, int region_type, char *region_fname) * 0xffffffff so use an address relative to that. For an * 8MB ROM the start address is 0xfff80000. * @write_fname: Filename to add to the image + * @offset_uboot_top: Offset of the top of U-Boot + * @offset_uboot_start: Offset of the start of U-Boot * @return number of bytes written if OK, -ve on error */ static int write_data(char *image, int size, unsigned int addr, - const char *write_fname) + const char *write_fname, int offset_uboot_top, + int offset_uboot_start) { int write_fd, write_size; int offset; @@ -719,6 +722,26 @@ static int write_data(char *image, int size, unsigned int addr, return write_fd; offset = (uint32_t)(addr + size); + if (offset_uboot_top) { + if (offset_uboot_start < offset && + offset_uboot_top >= offset) { + fprintf(stderr, "U-Boot image overlaps with region '%s'\n", + write_fname); + fprintf(stderr, + "U-Boot finishes at offset %x, file starts at %x\n", + offset_uboot_top, offset); + return -EXDEV; + } + if (offset_uboot_start > offset && + offset_uboot_start <= offset + write_size) { + fprintf(stderr, "U-Boot image overlaps with region '%s'\n", + write_fname); + fprintf(stderr, + "U-Boot starts at offset %x, file finishes at %x\n", + offset_uboot_start, offset + write_size); + return -EXDEV; + } + } debug("Writing %s to offset %#x\n", write_fname, offset); if (offset < 0 || offset + write_size > size) { @@ -737,6 +760,171 @@ static int write_data(char *image, int size, unsigned int addr, return write_size; } +static int scan_ucode(const void *blob, char *ucode_base, int *countp, + const char **datap, int *data_sizep) +{ + const char *data = NULL; + int node, count; + int data_size; + char *ucode; + + for (node = 0, count = 0, ucode = ucode_base; node >= 0; count++) { + node = fdt_node_offset_by_compatible(blob, node, + "intel,microcode"); + if (node < 0) + break; + + data = fdt_getprop(blob, node, "data", &data_size); + if (!data) { + debug("Missing microcode data in FDT '%s': %s\n", + fdt_get_name(blob, node, NULL), + fdt_strerror(data_size)); + return -ENOENT; + } + + if (ucode_base) + memcpy(ucode, data, data_size); + ucode += data_size; + } + + if (countp) + *countp = count; + if (datap) + *datap = data; + if (data_sizep) + *data_sizep = data_size; + + return ucode - ucode_base; +} + +static int remove_ucode(char *blob) +{ + int node, count; + int ret; + + /* Keep going until we find no more microcode to remove */ + do { + for (node = 0, count = 0; node >= 0;) { + int ret; + + node = fdt_node_offset_by_compatible(blob, node, + "intel,microcode"); + if (node < 0) + break; + + ret = fdt_delprop(blob, node, "data"); + + /* + * -FDT_ERR_NOTFOUND means we already removed the + * data for this one, so we just continue. + * 0 means we did remove it, so offsets may have + * changed and we need to restart our scan. + * Anything else indicates an error we should report. + */ + if (ret == -FDT_ERR_NOTFOUND) + continue; + else if (!ret) + node = 0; + else + return ret; + } + } while (count); + + /* Pack down to remove excees space */ + ret = fdt_pack(blob); + if (ret) + return ret; + + return fdt_totalsize(blob); +} + +static int write_ucode(char *image, int size, struct input_file *fdt, + int fdt_size, unsigned int ucode_ptr, + int collate_ucode) +{ + const char *data = NULL; + char *ucode_buf; + const void *blob; + char *ucode_base; + uint32_t *ptr; + int ucode_size; + int data_size; + int offset; + int count; + int ret; + + blob = (void *)image + (uint32_t)(fdt->addr + size); + + debug("DTB at %lx\n", (char *)blob - image); + + /* Find out about the micrcode we have */ + ucode_size = scan_ucode(blob, NULL, &count, &data, &data_size); + if (ucode_size < 0) + return ucode_size; + if (!count) { + debug("No microcode found in FDT\n"); + return -ENOENT; + } + + if (count > 1 && !collate_ucode) { + fprintf(stderr, + "Cannot handle multiple microcode blocks - please use -C flag to collate them\n"); + return -EMLINK; + } + + /* + * Collect the microcode into a buffer, remove it from the device + * tree and place it immediately above the (now smaller) device tree. + */ + if (collate_ucode && count > 1) { + ucode_buf = malloc(ucode_size); + if (!ucode_buf) { + fprintf(stderr, + "Out of memory for microcode (%d bytes)\n", + ucode_size); + return -ENOMEM; + } + ret = scan_ucode(blob, ucode_buf, NULL, NULL, NULL); + if (ret < 0) + return ret; + + /* Remove the microcode from the device tree */ + ret = remove_ucode((char *)blob); + if (ret < 0) { + debug("Could not remove FDT microcode: %s\n", + fdt_strerror(ret)); + return -EINVAL; + } + debug("Collated %d microcode block(s)\n", count); + debug("Device tree reduced from %x to %x bytes\n", + fdt_size, ret); + fdt_size = ret; + + /* + * Place microcode area immediately above the FDT, aligned + * to a 16-byte boundary. + */ + ucode_base = (char *)(((unsigned long)blob + fdt_size + 15) & + ~15); + + data = ucode_base; + data_size = ucode_size; + memcpy(ucode_base, ucode_buf, ucode_size); + free(ucode_buf); + } + + offset = (uint32_t)(ucode_ptr + size); + ptr = (void *)image + offset; + + ptr[0] = (data - image) - size; + ptr[1] = data_size; + debug("Wrote microcode pointer at %x: addr=%x, size=%x\n", ucode_ptr, + ptr[0], ptr[1]); + + return (collate_ucode ? data + data_size : (char *)blob + fdt_size) - + image; +} + /** * write_uboot() - Write U-Boot, device tree and microcode pointer * @@ -752,48 +940,34 @@ static int write_data(char *image, int size, unsigned int addr, * @return 0 if OK, -ve on error */ static int write_uboot(char *image, int size, struct input_file *uboot, - struct input_file *fdt, unsigned int ucode_ptr) + struct input_file *fdt, unsigned int ucode_ptr, + int collate_ucode, int *offset_uboot_top, + int *offset_uboot_start) { - const void *blob; - const char *data; - int uboot_size; - uint32_t *ptr; - int data_size; - int offset; - int node; - int ret; + int uboot_size, fdt_size; + int uboot_top; - uboot_size = write_data(image, size, uboot->addr, uboot->fname); + uboot_size = write_data(image, size, uboot->addr, uboot->fname, 0, 0); if (uboot_size < 0) return uboot_size; fdt->addr = uboot->addr + uboot_size; debug("U-Boot size %#x, FDT at %#x\n", uboot_size, fdt->addr); - ret = write_data(image, size, fdt->addr, fdt->fname); - if (ret < 0) - return ret; + fdt_size = write_data(image, size, fdt->addr, fdt->fname, 0, 0); + if (fdt_size < 0) + return fdt_size; + + uboot_top = (uint32_t)(fdt->addr + size) + fdt_size; if (ucode_ptr) { - blob = (void *)image + (uint32_t)(fdt->addr + size); - debug("DTB at %lx\n", (char *)blob - image); - node = fdt_node_offset_by_compatible(blob, 0, - "intel,microcode"); - if (node < 0) { - debug("No microcode found in FDT: %s\n", - fdt_strerror(node)); - return -ENOENT; - } - data = fdt_getprop(blob, node, "data", &data_size); - if (!data) { - debug("No microcode data found in FDT: %s\n", - fdt_strerror(data_size)); - return -ENOENT; - } - offset = ucode_ptr - uboot->addr; - ptr = (void *)image + offset; - ptr[0] = uboot->addr + (data - image); - ptr[1] = data_size; - debug("Wrote microcode pointer at %x: addr=%x, size=%x\n", - ucode_ptr, ptr[0], ptr[1]); + uboot_top = write_ucode(image, size, fdt, fdt_size, ucode_ptr, + collate_ucode); + if (uboot_top < 0) + return uboot_top; + } + + if (offset_uboot_top && offset_uboot_start) { + *offset_uboot_top = uboot_top; + *offset_uboot_start = (uint32_t)(uboot->addr + size); } return 0; @@ -860,7 +1034,7 @@ int main(int argc, char *argv[]) int mode_dump = 0, mode_extract = 0, mode_inject = 0; int mode_spifreq = 0, mode_em100 = 0, mode_locked = 0; int mode_unlocked = 0, mode_write = 0, mode_write_descriptor = 0; - int create = 0; + int create = 0, collate_ucode = 0; char *region_type_string = NULL, *inject_fname = NULL; char *desc_fname = NULL, *addr_str = NULL; int region_type = -1, inputfreq = 0; @@ -880,6 +1054,7 @@ int main(int argc, char *argv[]) int ret; static struct option long_options[] = { {"create", 0, NULL, 'c'}, + {"collate-microcode", 0, NULL, 'C'}, {"dump", 0, NULL, 'd'}, {"descriptor", 1, NULL, 'D'}, {"em100", 0, NULL, 'e'}, @@ -898,12 +1073,15 @@ int main(int argc, char *argv[]) {0, 0, 0, 0} }; - while ((opt = getopt_long(argc, argv, "cdD:ef:hi:lm:r:s:uU:vw:x?", + while ((opt = getopt_long(argc, argv, "cCdD:ef:hi:lm:r:s:uU:vw:x?", long_options, &option_index)) != EOF) { switch (opt) { case 'c': create = 1; break; + case 'C': + collate_ucode = 1; + break; case 'd': mode_dump = 1; break; @@ -987,7 +1165,7 @@ int main(int argc, char *argv[]) print_usage(argv[0]); exit(EXIT_FAILURE); } - ifile->addr = strtol(optarg, NULL, 0); + ifile->addr = strtoll(optarg, NULL, 0); ifile->type = opt == 'f' ? IF_fdt : opt == 'U' ? IF_uboot : IF_normal; if (ifile->type == IF_fdt) @@ -1113,22 +1291,28 @@ int main(int argc, char *argv[]) } if (mode_write_descriptor) - ret = write_data(image, size, -size, desc_fname); + ret = write_data(image, size, -size, desc_fname, 0, 0); if (mode_inject) ret = inject_region(image, size, region_type, inject_fname); if (mode_write) { + int offset_uboot_top = 0; + int offset_uboot_start = 0; + for (wr_idx = 0; wr_idx < wr_num; wr_idx++) { ifile = &input_file[wr_idx]; if (ifile->type == IF_fdt) { continue; } else if (ifile->type == IF_uboot) { ret = write_uboot(image, size, ifile, fdt, - ucode_ptr); + ucode_ptr, collate_ucode, + &offset_uboot_top, + &offset_uboot_start); } else { ret = write_data(image, size, ifile->addr, - ifile->fname); + ifile->fname, offset_uboot_top, + offset_uboot_start); } if (ret < 0) break;