UPSTREAM: util/ifdtool: Add support for disabling GPR0

On ChromeOS devices with updateable CSE firmware, the GPR0 (Global
Protected Range) register is used to ensure the CSE RO is write
protected even when the FLMSTR-based protection is temporarily disabled
by coreboot to allow updating the CSE RW. For more details see
Documentation/soc/intel/cse_fw_update/cse_fw_update.md

Therefore to allow modifying the CSE firmware from the CPU, the
descriptor must have both the FLMSTR-based protection disabled (which
can be done using ifdtool --unlock), and GPR0 disabled.

Add an ifdtool option for disabling GPR0. For now I've added support for
all platforms for which I have the SPI programming guide. Support for
more platforms can be added in the future if needed.

BUG=b:270275115
TEST=Run `ifdtool -p adl -g image.bin -O image-unlocked.bin` on a locked
craask image, check the GPR0 field is set to 0.

(cherry picked from commit c64be928de8421ea1bb2f575e32d74d58e41d659)

Original-Change-Id: Iee13ce0b702b3c7a443501cb4fc282580869d03a
Original-Reviewed-on: https://review.coreboot.org/c/coreboot/+/79788
Original-Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
GitOrigin-RevId: c64be928de8421ea1bb2f575e32d74d58e41d659
Change-Id: Ibe634c89eb07744bdee7e07bba5b833d11b8062f
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/coreboot/+/5173176
Commit-Queue: ChromeOS Auto Retry <chromeos-auto-retry@chromeos-bot.iam.gserviceaccount.com>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/coreboot/+/5199730
Auto-Submit: Phoebe Wang <phoebewang@chromium.org>
Commit-Queue: Phoebe Wang <phoebewang@chromium.org>
Reviewed-by: Cheng Yueh <cyueh@chromium.org>
Tested-by: Phoebe Wang <phoebewang@chromium.org>
diff --git a/util/ifdtool/ifdtool.c b/util/ifdtool/ifdtool.c
index 60d29b5..6658623 100644
--- a/util/ifdtool/ifdtool.c
+++ b/util/ifdtool/ifdtool.c
@@ -1466,6 +1466,39 @@
 	write_image(filename, image, size);
 }
 
+static void disable_gpr0(const char *filename, char *image, int size)
+{
+	struct fpsba *fpsba = find_fpsba(image, size);
+	if (!fpsba)
+		exit(EXIT_FAILURE);
+
+	/* Offset expressed as number of 32-bit fields from FPSBA */
+	uint32_t gpr0_offset;
+	switch (platform) {
+	case PLATFORM_CNL:
+		gpr0_offset = 0x10;
+		break;
+	case PLATFORM_JSL:
+		gpr0_offset = 0x12;
+		break;
+	case PLATFORM_TGL:
+	case PLATFORM_ADL:
+		gpr0_offset = 0x15;
+		break;
+	case PLATFORM_MTL:
+		gpr0_offset = 0x40;
+		break;
+	default:
+		fprintf(stderr, "Disabling GPR0 not supported on this platform\n");
+		exit(EXIT_FAILURE);
+	}
+
+	/* 0 means GPR0 protection is disabled */
+	fpsba->pchstrp[gpr0_offset] = 0;
+
+	write_image(filename, image, size);
+}
+
 static void set_pchstrap(struct fpsba *fpsba, const struct fdbar *fdb, const int strap,
 			const unsigned int value)
 {
@@ -1805,6 +1838,7 @@
 	       "   -l | --lock                           Lock firmware descriptor and ME region\n"
 	       "   -r | --read				 Enable CPU/BIOS read access for ME region\n"
 	       "   -u | --unlock                         Unlock firmware descriptor and ME region\n"
+	       "   -g | --gpr0-disable                   Disable GPR0 (Global Protected Range) register\n"
 	       "   -M | --altmedisable <0|1>             Set the MeDisable and AltMeDisable (or HAP for skylake or newer platform)\n"
 	       "                                         bits to disable ME\n"
 	       "   -p | --platform                       Add platform-specific quirks\n"
@@ -1837,6 +1871,7 @@
 	int mode_em100 = 0, mode_locked = 0, mode_unlocked = 0, mode_validate = 0;
 	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;
 	char *region_type_string = NULL, *region_fname = NULL;
 	const char *layout_fname = NULL;
 	char *new_filename = NULL;
@@ -1862,6 +1897,7 @@
 		{"lock", 0, NULL, 'l'},
 		{"read", 0, NULL, 'r'},
 		{"unlock", 0, NULL, 'u'},
+		{"gpr0-disable", 0, NULL, 'g'},
 		{"version", 0, NULL, 'v'},
 		{"help", 0, NULL, 'h'},
 		{"platform", 0, NULL, 'p'},
@@ -1871,7 +1907,7 @@
 		{0, 0, 0, 0}
 	};
 
-	while ((opt = getopt_long(argc, argv, "S:V:df:F:D:C:M:xi:n:O:s:p:elruvth?",
+	while ((opt = getopt_long(argc, argv, "S:V:df:F:D:C:M:xi:n:O:s:p:elrugvth?",
 					long_options, &option_index)) != EOF) {
 		switch (opt) {
 		case 'd':
@@ -2074,6 +2110,9 @@
 				exit(EXIT_FAILURE);
 			}
 			break;
+		case 'g':
+			mode_gpr0_disable = 1;
+			break;
 		case 'p':
 			if (!strcmp(optarg, "aplk")) {
 				platform = PLATFORM_APL;
@@ -2126,7 +2165,8 @@
 
 	if ((mode_dump + mode_layout + mode_fmap_template + mode_extract + mode_inject +
 			mode_setstrap + mode_newlayout + (mode_spifreq | mode_em100 |
-			mode_unlocked | mode_locked) + mode_altmedisable + mode_validate) > 1) {
+			mode_unlocked | mode_locked) + mode_altmedisable + mode_validate +
+			mode_gpr0_disable) > 1) {
 		fprintf(stderr, "You may not specify more than one mode.\n\n");
 		fprintf(stderr, "run '%s -h' for usage\n", argv[0]);
 		exit(EXIT_FAILURE);
@@ -2134,7 +2174,8 @@
 
 	if ((mode_dump + mode_layout + mode_fmap_template + mode_extract + mode_inject +
 			mode_setstrap + mode_newlayout + mode_spifreq + mode_em100 +
-			mode_locked + mode_unlocked + mode_density + mode_altmedisable + mode_validate) == 0) {
+			mode_locked + mode_unlocked + mode_density + mode_altmedisable +
+			mode_validate + mode_gpr0_disable) == 0) {
 		fprintf(stderr, "You need to specify a mode.\n\n");
 		fprintf(stderr, "run '%s -h' for usage\n", argv[0]);
 		exit(EXIT_FAILURE);
@@ -2231,6 +2272,9 @@
 	if (mode_unlocked)
 		unlock_descriptor(new_filename, image, size);
 
+	if (mode_gpr0_disable)
+		disable_gpr0(new_filename, image, size);
+
 	if (mode_setstrap) {
 		struct fpsba *fpsba = find_fpsba(image, size);
 		const struct fdbar *fdb = find_fd(image, size);