summaryrefslogtreecommitdiff
path: root/util/ifdtool
diff options
context:
space:
mode:
authorPatrick Rudolph <patrick.rudolph@9elements.com>2022-10-24 15:06:15 +0200
committerLean Sheng Tan <sheng.tan@9elements.com>2023-01-10 13:55:17 +0000
commit98ecaa4a55badd09fe85a56d28d3c1793b52b963 (patch)
treef8b8a10b86e7a9efdf76ce763ff0b63a8000a293 /util/ifdtool
parentd9c82695f50ca59ef5cdecdbefe7402ad90b3f27 (diff)
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 unofficially 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> Reviewed-on: https://review.coreboot.org/c/coreboot/+/68780 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Lean Sheng Tan <sheng.tan@9elements.com> Reviewed-by: Christian Walter <christian.walter@9elements.com>
Diffstat (limited to 'util/ifdtool')
-rw-r--r--util/ifdtool/ifdtool.c51
1 files changed, 49 insertions, 2 deletions
diff --git a/util/ifdtool/ifdtool.c b/util/ifdtool/ifdtool.c
index e97efcc206..95bf1525e2 100644
--- a/util/ifdtool/ifdtool.c
+++ b/util/ifdtool/ifdtool.c
@@ -44,6 +44,8 @@
#define PLATFORM_HAS_10GBE_0_REGION (platform == PLATFORM_DNV)
#define PLATFORM_HAS_10GBE_1_REGION (platform == PLATFORM_DNV)
+static int max_regions_from_fdbar(const fdbar_t *fdb);
+
static int ifd_version;
static int chipset;
static unsigned int max_regions = 0;
@@ -296,14 +298,16 @@ static int is_platform_ifd_2(void)
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 = MIN(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 = MIN(max_regions_from_fdbar(fdb), MAX_REGIONS_OLD);
}
}
@@ -410,6 +414,49 @@ static void dump_region_layout(char *buf, size_t bufsize, unsigned int num,
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 unofficially 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.
+ */
+static 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 MIN((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;