util/ifdtool: add option to change flash layout

The new option "--newlayout <file>" will read <file> 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 <cdouglass.orion@gmail.com>
Reviewed-on: http://review.coreboot.org/5312
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
This commit is contained in:
Chris Douglass 2014-02-27 09:25:19 -05:00 committed by Stefan Reinauer
parent 03ce014cfc
commit 4eabe1e4e1
1 changed files with 234 additions and 4 deletions

View File

@ -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);