Attention is currently required from: Stefan Reinauer.
Patrick Rudolph has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/68780 )
Change subject: ifdtool: Determine max regions from IFD ......................................................................
ifdtool: Determine max regions from IFD
IFDv1 always has 8 regions, while IFDv2 always has 16 regions.
It's platform specific which regions are used or are reserved. The 'SPI programming guide' as the name says is a guide only, not a specification what the hardware actually does. The best to do is not to rely on the guide, but detect how many regions are present in the IFD and expose them all.
Very early IFDv2 chipsets, sometimes in-officially referred to as IFDv1.5 platforms, only have 8 regions. To not corrupt the IFD when operating on an IFDv1.5 detect how much space is actually present in the IFD.
Fixes IFD corruption on Wellsburg/Lynxpoint when writing a new flash layout.
Change-Id: I0e3f23ec580b8b8402eb1bf165e3995c8db633f1 Signed-off-by: Patrick Rudolph patrick.rudolph@9elements.com --- M util/ifdtool/ifdtool.c M util/ifdtool/ifdtool.h 2 files changed, 78 insertions(+), 2 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/80/68780/1
diff --git a/util/ifdtool/ifdtool.c b/util/ifdtool/ifdtool.c index e97efcc..6cac179 100644 --- a/util/ifdtool/ifdtool.c +++ b/util/ifdtool/ifdtool.c @@ -296,14 +296,18 @@
static void check_ifd_version(char *image, int size) { + const fdbar_t *fdb = find_fd(image, size); + if (is_platform_ifd_2()) { ifd_version = IFD_VERSION_2; chipset = ifd2_platform_to_chipset(platform); - max_regions = MAX_REGIONS; + max_regions = (max_regions_from_fdbar(fdb) < MAX_REGIONS) ? + max_regions_from_fdbar(fdb) : MAX_REGIONS; } else { ifd_version = IFD_VERSION_1; chipset = ifd1_guess_chipset(image, size); - max_regions = MAX_REGIONS_OLD; + max_regions = (max_regions_from_fdbar(fdb) < MAX_REGIONS_OLD) ? + max_regions_from_fdbar(fdb) : MAX_REGIONS_OLD; } }
@@ -410,6 +414,50 @@ region.base, region.limit, region_name_short(num)); }
+static int sort_compare(const void *a, const void *b) +{ + return *(size_t *)a - *(size_t *)b; +} + +/* + * IFDv1 always has 8 regions, while IFDv2 always has 16 regions. + * + * It's platform specific which regions are used or are reserved. + * The 'SPI programming guide' as the name says is a guide only, + * not a specification what the hardware actually does. + * The best to do is not to rely on the guide, but detect how many + * regions are present in the IFD and expose them all. + * + * Very early IFDv2 chipsets, sometimes in-officially referred to as + * IFDv1.5 platforms, only have 8 regions. To not corrupt the IFD when + * operating on an IFDv1.5 detect how much space is actually present + * in the IFD. + */ +int max_regions_from_fdbar(const fdbar_t *fdb) +{ + const size_t fcba = (fdb->flmap0 & 0xff) << 4; + const size_t fmba = (fdb->flmap1 & 0xff) << 4; + const size_t frba = ((fdb->flmap0 >> 16) & 0xff) << 4; + const size_t fpsba = ((fdb->flmap1 >> 16) & 0xff) << 4; + const size_t flumap = 4096 - 256 - 4; + size_t sorted[5] = {fcba, fmba, frba, fpsba, flumap}; + + qsort(sorted, ARRAY_SIZE(sorted), sizeof(size_t), sort_compare); + + for (size_t i = 0; i < 4; i++) { + /* + * Find FRBA in the sorted array and determine the size of the + * region by the start of the next region. Every region requires + * 4 bytes of space. + */ + if (sorted[i] == frba) + return ((sorted[i+1] - sorted[i])/4) < MAX_REGIONS ? + ((sorted[i+1] - sorted[i])/4) : MAX_REGIONS; + } + /* Never reaches this point */ + return 0; +} + static void dump_frba(const frba_t *frba) { unsigned int i; diff --git a/util/ifdtool/ifdtool.h b/util/ifdtool/ifdtool.h index 1ee76f1..536e56d 100644 --- a/util/ifdtool/ifdtool.h +++ b/util/ifdtool/ifdtool.h @@ -205,3 +205,5 @@ const char *filename; const char *fmapname; }; + +int max_regions_from_fdbar(const fdbar_t *fdb); \ No newline at end of file