diff options
-rw-r--r-- | util/ifdtool/ifdtool.c | 238 |
1 files changed, 234 insertions, 4 deletions
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 <filename> dump regions into a flashrom layout file\n" " -x | --extract: extract intel fd modules\n" " -i | --inject <region>:<module> inject file <module> into region <region>\n" + " -n | --newlayout <filename> 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); |