X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=tools%2Fifdtool.c;h=48059c02b550ee8db3cbfc7c9702b152936a0d4e;hb=231af7f95a4e96debeb380bd904ebee60f0bd7bf;hp=a4b481fb60b45d7769fb48d5098a889d68d09f57;hpb=fce0a90a68de507dc827c1ff40d9e446047fa043;p=oweals%2Fu-boot.git diff --git a/tools/ifdtool.c b/tools/ifdtool.c index a4b481fb60..48059c02b5 100644 --- a/tools/ifdtool.c +++ b/tools/ifdtool.c @@ -18,6 +18,7 @@ #include #include #include +#include #include "ifdtool.h" #undef DEBUG @@ -32,6 +33,18 @@ #define FLREG_BASE(reg) ((reg & 0x00000fff) << 12); #define FLREG_LIMIT(reg) (((reg & 0x0fff0000) >> 4) | 0xfff); +enum input_file_type_t { + IF_normal, + IF_fdt, + IF_uboot, +}; + +struct input_file { + char *fname; + unsigned int addr; + enum input_file_type_t type; +}; + /** * find_fd() - Find the flash description in the ROM image * @@ -54,7 +67,8 @@ static struct fdbar_t *find_fd(char *image, int size) return NULL; } - debug("Found Flash Descriptor signature at 0x%08x\n", i); + debug("Found Flash Descriptor signature at 0x%08lx\n", + (char *)ptr - image); return (struct fdbar_t *)ptr; } @@ -448,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 | @@ -464,6 +478,16 @@ static int write_regions(char *image, int size) return ret; } +static int perror_fname(const char *fmt, const char *fname) +{ + char msg[strlen(fmt) + strlen(fname) + 1]; + + sprintf(msg, fmt, fname); + perror(msg); + + return -1; +} + /** * write_image() - Write the image to a file * @@ -480,10 +504,10 @@ static int write_image(char *filename, char *image, int size) new_fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); - if (write(new_fd, image, size) != size) { - perror("Error while writing"); - return -1; - } + if (new_fd < 0) + return perror_fname("Could not open file '%s'", filename); + if (write(new_fd, image, size) != size) + return perror_fname("Could not write file '%s'", filename); close(new_fd); return 0; @@ -585,14 +609,10 @@ int open_for_read(const char *fname, int *sizep) int fd = open(fname, O_RDONLY); struct stat buf; - if (fd == -1) { - perror("Could not open file"); - return -1; - } - if (fstat(fd, &buf) == -1) { - perror("Could not stat file"); - return -1; - } + if (fd == -1) + return perror_fname("Could not open file '%s'", fname); + if (fstat(fd, &buf) == -1) + return perror_fname("Could not stat file '%s'", fname); *sizep = buf.st_size; debug("File %s is %d bytes\n", fname, *sizep); @@ -686,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 - * @return 0 if OK, -ve on error + * @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; @@ -698,7 +721,27 @@ static int write_data(char *image, int size, unsigned int addr, if (write_fd < 0) return write_fd; - offset = addr + size; + 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) { @@ -714,6 +757,219 @@ static int write_data(char *image, int size, unsigned int addr, close(write_fd); + 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 + * + * This writes U-Boot into a place in the flash, followed by its device tree. + * The microcode pointer is written so that U-Boot can find the microcode in + * the device tree very early in boot. + * + * @image: Pointer to image + * @size: Size of image in bytes + * @uboot: Input file information for u-boot.bin + * @fdt: Input file information for u-boot.dtb + * @ucode_ptr: Address in U-Boot where the microcode pointer should be placed + * @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, + int collate_ucode, int *offset_uboot_top, + int *offset_uboot_start) +{ + int uboot_size, fdt_size; + int uboot_top; + + 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); + 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) { + 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; } @@ -732,6 +988,7 @@ static void print_usage(const char *name) " -x | --extract: extract intel fd modules\n" " -i | --inject : inject file into region \n" " -w | --write : write file to appear at memory address \n" + " multiple files can be written simultaneously\n" " -s | --spifreq <20|33|50> set the SPI frequency\n" " -e | --em100 set SPI frequency to 20MHz and disable\n" " Dual Output Fast Read Support\n" @@ -777,57 +1034,67 @@ 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; - char *region_type_string = NULL, *src_fname = NULL; - char *addr_str = NULL; + 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; enum spi_frequency spifreq = SPI_FREQUENCY_20MHZ; - unsigned int addr = 0; + struct input_file input_file[WRITE_MAX], *ifile, *fdt = NULL; + unsigned char wr_idx, wr_num = 0; int rom_size = -1; bool write_it; char *filename; char *outfile = NULL; struct stat buf; int size = 0; + unsigned int ucode_ptr = 0; + bool have_uboot = false; int bios_fd; char *image; 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'}, {"extract", 0, NULL, 'x'}, + {"fdt", 1, NULL, 'f'}, {"inject", 1, NULL, 'i'}, {"lock", 0, NULL, 'l'}, + {"microcode", 1, NULL, 'm'}, {"romsize", 1, NULL, 'r'}, {"spifreq", 1, NULL, 's'}, {"unlock", 0, NULL, 'u'}, + {"uboot", 1, NULL, 'U'}, {"write", 1, NULL, 'w'}, {"version", 0, NULL, 'v'}, {"help", 0, NULL, 'h'}, {0, 0, 0, 0} }; - while ((opt = getopt_long(argc, argv, "cdD:ehi:lr:s:uvw: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; case 'D': mode_write_descriptor = 1; - src_fname = optarg; + desc_fname = optarg; break; case 'e': mode_em100 = 1; break; case 'i': if (get_two_words(optarg, ®ion_type_string, - &src_fname)) { + &inject_fname)) { print_usage(argv[0]); exit(EXIT_FAILURE); } @@ -852,6 +1119,9 @@ int main(int argc, char *argv[]) case 'l': mode_locked = 1; break; + case 'm': + ucode_ptr = strtoul(optarg, NULL, 0); + break; case 'r': rom_size = strtol(optarg, NULL, 0); debug("ROM size %d\n", rom_size); @@ -885,12 +1155,29 @@ int main(int argc, char *argv[]) exit(EXIT_SUCCESS); break; case 'w': + case 'U': + case 'f': + ifile = &input_file[wr_num]; mode_write = 1; - if (get_two_words(optarg, &addr_str, &src_fname)) { - print_usage(argv[0]); - exit(EXIT_FAILURE); + if (wr_num < WRITE_MAX) { + if (get_two_words(optarg, &addr_str, + &ifile->fname)) { + print_usage(argv[0]); + exit(EXIT_FAILURE); + } + ifile->addr = strtoll(optarg, NULL, 0); + ifile->type = opt == 'f' ? IF_fdt : + opt == 'U' ? IF_uboot : IF_normal; + if (ifile->type == IF_fdt) + fdt = ifile; + else if (ifile->type == IF_uboot) + have_uboot = true; + wr_num++; + } else { + fprintf(stderr, + "The number of files to write simultaneously exceeds the limitation (%d)\n", + WRITE_MAX); } - addr = strtol(optarg, NULL, 0); break; case 'x': mode_extract = 1; @@ -941,6 +1228,13 @@ int main(int argc, char *argv[]) exit(EXIT_FAILURE); } + if (have_uboot && !fdt) { + fprintf(stderr, + "You must supply a device tree file for U-Boot\n\n"); + print_usage(argv[0]); + exit(EXIT_FAILURE); + } + filename = argv[optind]; if (optind + 2 != argc) outfile = argv[optind + 1]; @@ -997,13 +1291,33 @@ int main(int argc, char *argv[]) } if (mode_write_descriptor) - ret = write_data(image, size, -size, src_fname); + ret = write_data(image, size, -size, desc_fname, 0, 0); if (mode_inject) - ret = inject_region(image, size, region_type, src_fname); - - if (mode_write) - ret = write_data(image, size, addr, src_fname); + 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, collate_ucode, + &offset_uboot_top, + &offset_uboot_start); + } else { + ret = write_data(image, size, ifile->addr, + ifile->fname, offset_uboot_top, + offset_uboot_start); + } + if (ret < 0) + break; + } + } if (mode_spifreq) set_spi_frequency(image, size, spifreq); @@ -1035,5 +1349,5 @@ int main(int argc, char *argv[]) free(image); close(bios_fd); - return ret ? 1 : 0; + return ret < 0 ? 1 : 0; }