ifdtool: Add set top swap size PCH strap subcommand

Top-Block Swap mode of Intel PCH allows to swap the boot block with
another location placed directly below it by redirecting the memory
accesses.

The range of the addresses to be redirected is configured using the Top
Swap Block Size (or BOOT_BLOCK_SIZE) PCH strap using 3 bits to encode
one of 8 sizes:
    64 KB, 128 KB, 256 KB, 512 KB, 1 MB, 2 MB, 4 MB or 8 MB.

The source and target ranges depend on the configured size, eg:
- 64 KB  - FFFF_0000h - FFFF_FFFFh -> FFFE_0000h - FFFE_FFFFh
- 128 KB - FFFE_0000h - FFFF_FFFFh -> FFFC_0000h - FFFD_FFFFh
- 8 MB   - FF80_0000h - FFFF_FFFFh -> FF00_0000h - FF7F_FFFFh

Only supporting Alder Lake-P and Alder Lake-N for now.

Needed for the bootblock redundancy feature suggested at
https://mail.coreboot.org/archives/list/coreboot@coreboot.org/thread/C6JN2PB7K7D67EG7OIKB6BBERZU5YV35/

TEST=check using xxd, MFIT tool, ensure VP6670 boots
Test details:
xxd:
  ./util/ifdtool/ifdtool -p adl -T 0x10000 vp66xx_fd.bin && \
  xxd vp66xx_fd.bin > vp66xx.hex && \
  xxd vp66xx_fd.bin.new > vp66xx_fd.new.hex && \
  diff -au vp66xx_fd.hex vp66xx_fd.new.hex

File vp66xx_fd.bin is 4096 bytes
Writing new image to vp66xx_fd.bin.new

--- vp66xx_fd.hex       2025-10-08 12:03:09.527193533 +0200
+++ vp66xx_fd.new.hex   2025-10-08 12:05:08.717108142 +0200
@@ -18,7 +18,7 @@
 00000110: 7f78 0700 0000 0000 1800 0000 0000 1f00  .x..............
 00000120: 0808 1170 0000 0000 0000 7f06 80f8 8107  ...p............
 00000130: 0000 0000 0f00 0000 2222 2222 2202 2222  ........""""".""
-00000140: 0000 0000 0000 0000 0000 ff00 6000 80c8  ............`...
+00000140: 0000 0000 0000 0000 0000 ff00 0000 80c8  ................
 00000150: 4586 0036 0000 0000 0002 5800 0000 4000  E..6......X...@.
 00000160: 0018 0000 0000 0000 0000 0000 0000 0000  ................
 00000170: 0000 0000 0000 0000 54b3 04a0 3000 0140  ........T...0..@

mfittool:
./mfit --gui -decompose protectli_vp66xx_v0.9.2.rom
In the UI:
Flash Settings > BIOS Configuration > Top Swap Block Size
shows the value changing to the expected one, ie.
    -T 0x10000 results in 64kB
    -T 0x20000 results in 128kB
    -T 0x400000 results in 4MB
    etc.

Change-Id: I50e9d4160ee4b60e83567bcd33c9d80d428cf2bb
Signed-off-by: Filip Gołaś <filip.golas@3mdeb.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/89438
Reviewed-by: Sergii Dmytruk <sergii.dmytruk@3mdeb.com>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
This commit is contained in:
Filip Gołaś 2025-10-06 18:43:12 +02:00 committed by Matt DeVillier
commit f4271cad0a

View file

@ -2173,6 +2173,16 @@ static void new_layout(const char *filename, char *image, int size,
free(new_image);
}
static uint32_t uint_log2(uint32_t n)
{
int log = -1;
while (n) {
n >>= 1;
++log;
}
return log;
}
static void print_version(void)
{
printf("ifdtool v%s -- ", IFDTOOL_VERSION);
@ -2231,6 +2241,9 @@ static void print_usage(const char *name)
" wbg - Wellsburg\n"
" -S | --setpchstrap Write a PCH strap\n"
" -V | --newvalue The new value to write into PCH strap specified by -S\n"
" -T | --topswapsize Set the Top Swap Block Size PCH strap value\n"
" Possible values: 0x10000, 0x20000, 0x40000, 0x80000,\n"
" 0x100000, 0x200000, 0x400000, 0x800000\n"
" -v | --version: print the version\n"
" -h | --help: print this help\n\n"
"<region> is one of Descriptor, BIOS, ME, GbE, Platform Data, Secondary BIOS, "
@ -2246,8 +2259,9 @@ int main(int argc, char *argv[])
int mode_layout = 0, mode_newlayout = 0, mode_density = 0, mode_setstrap = 0;
int mode_read = 0, mode_altmedisable = 0, altmedisable = 0, mode_fmap_template = 0;
int mode_gpr0_disable = 0, mode_gpr0_enable = 0, mode_gpr0_status = 0;
int mode_settopswapsize = 0;
char *region_type_string = NULL, *region_fname = NULL, *layout_fname = NULL;
char *new_filename = NULL;
char *new_filename = NULL, *top_swap_size_arg = NULL;
int region_type = -1, inputfreq = 0;
unsigned int value = 0;
unsigned int pchstrap = 0;
@ -2279,10 +2293,11 @@ int main(int argc, char *argv[])
{"validate", 0, NULL, 't'},
{"setpchstrap", 1, NULL, 'S'},
{"newvalue", 1, NULL, 'V'},
{"topswapsize", 1, NULL, 'T'},
{0, 0, 0, 0}
};
while ((opt = getopt_long(argc, argv, "S:V:df:F:D:C:M:xi:n:O:s:p:elrugEcvth?",
while ((opt = getopt_long(argc, argv, "S:V:df:F:D:C:M:xi:n:O:s:p:T:elrugEcvth?",
long_options, &option_index)) != EOF) {
switch (opt) {
case 'd':
@ -2533,6 +2548,10 @@ int main(int argc, char *argv[])
case 't':
mode_validate = 1;
break;
case 'T':
mode_settopswapsize = 1;
top_swap_size_arg = optarg;
break;
case 'v':
print_version();
exit(EXIT_SUCCESS);
@ -2550,7 +2569,7 @@ int main(int argc, char *argv[])
}
if ((mode_dump + mode_layout + mode_fmap_template + mode_extract + mode_inject +
mode_setstrap + mode_newlayout + (mode_spifreq | mode_em100 |
mode_setstrap + mode_settopswapsize + mode_newlayout + (mode_spifreq | mode_em100 |
mode_unlocked | mode_locked) + mode_altmedisable + mode_validate +
(mode_gpr0_disable | mode_gpr0_enable) + mode_gpr0_status) > 1) {
fprintf(stderr, "You may not specify more than one mode.\n\n");
@ -2559,7 +2578,7 @@ int main(int argc, char *argv[])
}
if ((mode_dump + mode_layout + mode_fmap_template + mode_extract + mode_inject +
mode_setstrap + mode_newlayout + mode_spifreq + mode_em100 +
mode_setstrap + mode_settopswapsize + mode_newlayout + mode_spifreq + mode_em100 +
mode_locked + mode_unlocked + mode_density + mode_altmedisable +
mode_validate + (mode_gpr0_disable | mode_gpr0_enable) + mode_gpr0_status) == 0) {
fprintf(stderr, "You need to specify a mode.\n\n");
@ -2667,6 +2686,59 @@ int main(int argc, char *argv[])
if (mode_gpr0_status)
is_gpr0_protected(image, size);
if (mode_settopswapsize) {
if (platform == -1) {
fprintf(stderr, "Error: No platform specified.\n");
exit(EXIT_FAILURE);
}
struct fpsba *fpsba = find_fpsba(image, size);
uint32_t top_swap_size_offset = 0;
switch (platform) {
case PLATFORM_ADL:
// fixed bits for ADL-P / ADL-N
if (fpsba->pchstrp[0xd0 / 4] == 0x00000300) {
// fixed bits for ADL-P
if (((fpsba->pchstrp[0x7c / 4] >> 1) & 0xff) == 0x18) {
printf("Detected ADL-P flash descriptor\n");
pchstrap = 0x4C / 4;
top_swap_size_offset = 4;
// fixed bits for ADL-N
} else if (((fpsba->pchstrp[0x7c / 4] >> 1) & 0xff) == 0x51) {
printf("Detected ADL-N flash descriptor\n");
pchstrap = 0x4C / 4;
top_swap_size_offset = 4;
}
}
break;
default:
break;
}
if (pchstrap == 0) {
fprintf(stderr, "Setting top swap size not supported on selected platform.\n");
exit(EXIT_FAILURE);
}
uint32_t top_swap_size = strtoul(top_swap_size_arg, NULL, 0);
if (top_swap_size < 0x10000 || !IS_POWER_OF_2(top_swap_size) || top_swap_size > 0x800000) {
fprintf(stderr,
"Unsupported top swap size: %s\n"
"Supported top swap sizes:\n"
"\t0x10000, 0x20000, 0x40000, 0x80000, 0x100000,\n"
"\t0x200000, 0x400000, 0x800000\n",
top_swap_size_arg);
exit(EXIT_FAILURE);
}
// convert to 0x00-0x07
top_swap_size = uint_log2(top_swap_size / 0x10000);
const struct fdbar *fdb = find_fd(image, size);
value = fpsba->pchstrp[pchstrap];
value &= ~(0x7 << top_swap_size_offset);
value |= (top_swap_size << top_swap_size_offset);
set_pchstrap(fpsba, fdb, pchstrap, value);
write_image(new_filename, image, size);
}
if (mode_setstrap) {
struct fpsba *fpsba = find_fpsba(image, size);
const struct fdbar *fdb = find_fd(image, size);