From 4eabe1e4e1e4aa7ffa5a147a609a8782f48f0791 Mon Sep 17 00:00:00 2001 From: Chris Douglass Date: Thu, 27 Feb 2014 09:25:19 -0500 Subject: util/ifdtool: add option to change flash layout The new option "--newlayout " will read in flashrom's layout format and copy flash regions from the current flash image file to a new flash image file. If a region grows, the padding is added at the beginning of the target region in the new file so that the data is "right-aligned" to the end of the region. If a region shrinks, a warning is given and the tail end of existing data is copied to the target region in the new file. Regions of zero or negative size are ignored. (In the example below 00fff000:00000fff regions are an artifact of the address encoding in the register fields.) Example Usage: Given a flash image for a board with a Sandy Bridge processor and Intel 6-Series chipset in the file vpx7654.bin ifdtool --layout layout.txt vpx7564.bin will yield the file layout.txt: 00000000:00000fff fd 00180000:003fffff bios 00001000:0017ffff me 00fff000:00000fff gbe 00fff000:00000fff pd Notice that the "bios" portion extends to the end of the 4MB flash. It may be edited to extend the bios portion to consume to the extent of an 8MB flash. like layout2.txt: 00000000:00000fff fd 00180000:007fffff bios 00001000:0017ffff me 00fff000:00000fff gbe 00fff000:00000fff pd ifdtool --newlayout layout.txt vpx7654.bin will create a file vpx7654.bin.new that is 8MB. Change-Id: I0e0925a725c40fa44d8c4b6e86552028779d0523 Signed-off-by: Christopher Douglass Reviewed-on: http://review.coreboot.org/5312 Tested-by: build bot (Jenkins) Reviewed-by: Stefan Reinauer --- util/ifdtool/ifdtool.c | 238 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 234 insertions(+), 4 deletions(-) (limited to 'util/ifdtool') diff --git a/util/ifdtool/ifdtool.c b/util/ifdtool/ifdtool.c index b5a0ac2f65..28dbaacec7 100644 --- a/util/ifdtool/ifdtool.c +++ b/util/ifdtool/ifdtool.c @@ -95,6 +95,35 @@ static region_t get_region(frba_t *frba, int region_type) return region; } +static void set_region(frba_t *frba, int region_type, region_t region) +{ + switch (region_type) { + case 0: + frba->flreg0 = (((region.limit >> 12) & 0x7fff) << 16) + | ((region.base >> 12) & 0x7fff); + break; + case 1: + frba->flreg1 = (((region.limit >> 12) & 0x7fff) << 16) + | ((region.base >> 12) & 0x7fff); + break; + case 2: + frba->flreg2 = (((region.limit >> 12) & 0x7fff) << 16) + | ((region.base >> 12) & 0x7fff); + break; + case 3: + frba->flreg3 = (((region.limit >> 12) & 0x7fff) << 16) + | ((region.base >> 12) & 0x7fff); + break; + case 4: + frba->flreg4 = (((region.limit >> 12) & 0x7fff) << 16) + | ((region.base >> 12) & 0x7fff); + break; + default: + fprintf(stderr, "Invalid region type.\n"); + exit (EXIT_FAILURE); + } +} + static const char *region_name(int region_type) { if (region_type < 0 || region_type > 4) { @@ -115,6 +144,20 @@ static const char *region_name_short(int region_type) return region_names[region_type].terse; } +static int region_num(const char *name) +{ + int i; + + for (i = 0; i < 5; i++) { + if (strcasecmp(name, region_names[i].pretty) == 0) + return i; + if (strcasecmp(name, region_names[i].terse) == 0) + return i; + } + + return -1; +} + static const char *region_filename(int region_type) { static const char *region_filenames[5] = { @@ -641,6 +684,179 @@ void inject_region(char *filename, char *image, int size, int region_type, write_image(filename, image, size); } +unsigned int next_pow2(unsigned int x) +{ + unsigned int y = 1; + if (x == 0) + return 0; + while (y <= x) + y = y << 1; + + return y; +} + +/** + * Determine if two memory regions overlap. + * + * @param r1, r2 Memory regions to compare. + * @return 0 if the two regions are seperate + * @return 1 if the two regions overlap + */ +static int regions_collide(region_t r1, region_t r2) +{ + if ((r1.size == 0) || (r2.size == 0)) + return 0; + + if ( ((r1.base >= r2.base) && (r1.base <= r2.limit)) || + ((r1.limit >= r2.base) && (r1.limit <= r2.limit)) ) + return 1; + + return 0; +} + +void new_layout(char *filename, char *image, int size, char *layout_fname) +{ + FILE *romlayout; + char tempstr[256]; + char layout_region_name[256]; + int i, j; + int region_number; + region_t current_regions[5]; + region_t new_regions[5]; + int new_extent = 0; + char *new_image; + + /* load current descriptor map and regions */ + fdbar_t *fdb = find_fd(image, size); + if (!fdb) + exit(EXIT_FAILURE); + + frba_t *frba = + (frba_t *) (image + (((fdb->flmap0 >> 16) & 0xff) << 4)); + + for (i = 0; i < 5; i++) { + current_regions[i] = get_region(frba, i); + new_regions[i] = get_region(frba, i); + } + + /* read new layout */ + romlayout = fopen(layout_fname, "r"); + + if (!romlayout) { + perror("Could not read layout file.\n"); + exit(EXIT_FAILURE); + } + + while (!feof(romlayout)) { + char *tstr1, *tstr2; + + if (2 != fscanf(romlayout, "%s %s\n", tempstr, + layout_region_name)) + continue; + + region_number = region_num(layout_region_name); + if (region_number < 0) + continue; + + tstr1 = strtok(tempstr, ":"); + tstr2 = strtok(NULL, ":"); + if (!tstr1 || !tstr2) { + fprintf(stderr, "Could not parse layout file.\n"); + exit(EXIT_FAILURE); + } + new_regions[region_number].base = strtol(tstr1, + (char **)NULL, 16); + new_regions[region_number].limit = strtol(tstr2, + (char **)NULL, 16); + new_regions[region_number].size = + new_regions[region_number].limit - + new_regions[region_number].base + 1; + + if (new_regions[region_number].size < 0) + new_regions[region_number].size = 0; + } + fclose(romlayout); + + /* check new layout */ + for (i = 0; i < 5; i++) { + if (new_regions[i].size == 0) + continue; + + if (new_regions[i].size < current_regions[i].size) { + printf("DANGER: Region %s is shrinking.\n", + region_name(i)); + printf(" The region will be truncated to fit.\n"); + printf(" This may result in an unusable image.\n"); + } + + for (j = i + 1; j < 5; j++) { + if (regions_collide(new_regions[i], new_regions[j])) { + fprintf(stderr, "Regions would overlap.\n"); + exit(EXIT_FAILURE); + } + } + + /* detect if the image size should grow */ + if (new_extent < new_regions[i].limit) + new_extent = new_regions[i].limit; + } + + new_extent = next_pow2(new_extent - 1); + if (new_extent != size) { + printf("The image has changed in size.\n"); + printf("The old image is %d bytes.\n", size); + printf("The new image is %d bytes.\n", new_extent); + } + + /* copy regions to a new image */ + new_image = malloc(new_extent); + memset(new_image, 0xff, new_extent); + for (i = 0; i < 5; i++) { + int copy_size = new_regions[i].size; + int offset_current = 0, offset_new = 0; + region_t current = current_regions[i]; + region_t new = new_regions[i]; + + if (new.size == 0) + continue; + + if (new.size > current.size) { + /* copy from the end of the current region */ + copy_size = current.size; + offset_new = new.size - current.size; + } + + if (new.size < current.size) { + /* copy to the end of the new region */ + offset_current = current.size - new.size; + } + + printf("Copy Descriptor %d (%s) (%d bytes)\n", i, + region_name(i), copy_size); + printf(" from %08x+%08x:%08x (%10d)\n", current.base, + offset_current, current.limit, current.size); + printf(" to %08x+%08x:%08x (%10d)\n", new.base, + offset_new, new.limit, new.size); + + memcpy(new_image + new.base + offset_new, + image + current.base + offset_current, + copy_size); + } + + /* update new descriptor regions */ + fdb = find_fd(new_image, new_extent); + if (!fdb) + exit(EXIT_FAILURE); + + frba = (frba_t *) (new_image + (((fdb->flmap0 >> 16) & 0xff) << 4)); + for (i = 1; i < 5; i++) { + set_region(frba, i, new_regions[i]); + } + + write_image(filename, new_image, new_extent); + free(new_image); +} + static void print_version(void) { printf("ifdtool v%s -- ", IFDTOOL_VERSION); @@ -665,6 +881,7 @@ static void print_usage(const char *name) " -f | --layout dump regions into a flashrom layout file\n" " -x | --extract: extract intel fd modules\n" " -i | --inject : inject file into region \n" + " -n | --newlayout update regions using a flashrom layout file\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" @@ -681,7 +898,7 @@ int main(int argc, char *argv[]) int opt, option_index = 0; int mode_dump = 0, mode_extract = 0, mode_inject = 0, mode_spifreq = 0; int mode_em100 = 0, mode_locked = 0, mode_unlocked = 0; - int mode_layout = 0; + int mode_layout = 0, mode_newlayout = 0; char *region_type_string = NULL, *region_fname = NULL, *layout_fname = NULL; int region_type = -1, inputfreq = 0; enum spi_frequency spifreq = SPI_FREQUENCY_20MHZ; @@ -691,6 +908,7 @@ int main(int argc, char *argv[]) {"layout", 1, NULL, 'f'}, {"extract", 0, NULL, 'x'}, {"inject", 1, NULL, 'i'}, + {"newlayout", 1, NULL, 'n'}, {"spifreq", 1, NULL, 's'}, {"em100", 0, NULL, 'e'}, {"lock", 0, NULL, 'l'}, @@ -700,7 +918,7 @@ int main(int argc, char *argv[]) {0, 0, 0, 0} }; - while ((opt = getopt_long(argc, argv, "df:xi:s:eluvh?", + while ((opt = getopt_long(argc, argv, "df:xi:n:s:eluvh?", long_options, &option_index)) != EOF) { switch (opt) { case 'd': @@ -748,6 +966,15 @@ int main(int argc, char *argv[]) } mode_inject = 1; break; + case 'n': + mode_newlayout = 1; + layout_fname = strdup(optarg); + if (!layout_fname) { + fprintf(stderr, "No layout file specified\n"); + print_usage(argv[0]); + exit(EXIT_FAILURE); + } + break; case 's': // Parse the requested SPI frequency inputfreq = strtol(optarg, NULL, 0); @@ -800,7 +1027,7 @@ int main(int argc, char *argv[]) } if ((mode_dump + mode_layout + mode_extract + mode_inject + - (mode_spifreq | mode_em100 | mode_unlocked | + mode_newlayout + (mode_spifreq | mode_em100 | mode_unlocked | mode_locked)) > 1) { fprintf(stderr, "You may not specify more than one mode.\n\n"); print_usage(argv[0]); @@ -808,7 +1035,7 @@ int main(int argc, char *argv[]) } if ((mode_dump + mode_layout + mode_extract + mode_inject + - mode_spifreq + mode_em100 + mode_locked + + mode_newlayout + mode_spifreq + mode_em100 + mode_locked + mode_unlocked) == 0) { fprintf(stderr, "You need to specify a mode.\n\n"); print_usage(argv[0]); @@ -862,6 +1089,9 @@ int main(int argc, char *argv[]) inject_region(filename, image, size, region_type, region_fname); + if (mode_newlayout) + new_layout(filename, image, size, layout_fname); + if (mode_spifreq) set_spi_frequency(filename, image, size, spifreq); -- cgit v1.2.3