ichspi.c: warn user and disable writes when a protected address range is detected

This includes not only the notorious read-only flash descriptors and locked ME
regions, but also the more rarely used PRs (Protected Ranges).
The user can enforce write support by specifying ich_spi_force=yes in the
programmer options, but we don't tell him the exact syntax interactively. He
has to read it up in the man page.

Corresponding to flashrom svn r1494.

Signed-off-by: Stefan Tauner <stefan.tauner@alumni.tuwien.ac.at>
Acked-by: Carl-Daniel Hailfinger <c-d.hailfinger.devel.2006@gmx.net>
diff --git a/flashrom.8 b/flashrom.8
index e5f9a29..76aacba 100644
--- a/flashrom.8
+++ b/flashrom.8
@@ -315,6 +315,21 @@
 chip is attached). The other options (swseq, hwseq) select the respective mode
 (if possible).
 .sp
+ICH8 and later southbridges may also have locked address ranges of different
+kinds if a valid descriptor was written to it. The flash address space is then
+partitioned in multiple so called "Flash Regions" containing the host firmware,
+the ME firmware and so on respectively. The flash descriptor can also specify up
+to 5 so called "Protected Regions", which are freely chosen address ranges
+independent from the aforementioned "Flash Regions". All of them can be write
+and/or read protected individually. If flashrom detects such a lock it will
+disable write support unless the user forces it with the
+.sp
+.B "  flashrom \-p internal:ich_spi_force=yes"
+.sp
+syntax. If this leads to erase or write accesses to the flash it would most
+probably bring it into an inconsistent and unbootable state and we will not
+provide any support in such a case.
+.sp
 If you have an Intel chipset with an ICH6 or later southbridge and if you want
 to set specific IDSEL values for a non-default flash chip or an embedded
 controller (EC), you can use the
diff --git a/ichspi.c b/ichspi.c
index 163ecf1..5b07a0b 100644
--- a/ichspi.c
+++ b/ichspi.c
@@ -1421,7 +1421,8 @@
 #define ICH_BRWA(x)  ((x >>  8) & 0xff)
 #define ICH_BRRA(x)  ((x >>  0) & 0xff)
 
-static void do_ich9_spi_frap(uint32_t frap, int i)
+/* returns 0 if region is unused or r/w */
+static int ich9_handle_frap(uint32_t frap, int i)
 {
 	static const char *const access_names[4] = {
 		"locked", "read-only", "write-only", "read-write"
@@ -1436,19 +1437,26 @@
 	int offset = ICH9_REG_FREG0 + i * 4;
 	uint32_t freg = mmio_readl(ich_spibar + offset);
 
-	msg_pdbg("0x%02X: 0x%08x (FREG%i: %s)\n",
-		     offset, freg, i, region_names[i]);
-
 	base  = ICH_FREG_BASE(freg);
 	limit = ICH_FREG_LIMIT(freg);
 	if (base > limit) {
 		/* this FREG is disabled */
-		msg_pdbg("%s region is unused.\n", region_names[i]);
-		return;
+		msg_pdbg2("0x%02X: 0x%08x FREG%i: %s region is unused.\n",
+			  offset, freg, i, region_names[i]);
+		return 0;
+	}
+	msg_pdbg("0x%02X: 0x%08x ", offset, freg);
+	if (rwperms == 0x3) {
+		msg_pdbg("FREG%i: %s region (0x%08x-0x%08x) is %s.\n", i,
+			 region_names[i], base, (limit | 0x0fff),
+			 access_names[rwperms]);
+		return 0;
 	}
 
-	msg_pdbg("0x%08x-0x%08x is %s\n", base, (limit | 0x0fff),
-		 access_names[rwperms]);
+	msg_pinfo("FREG%i: WARNING: %s region (0x%08x-0x%08x) is %s.\n", i,
+		  region_names[i], base, (limit | 0x0fff),
+		  access_names[rwperms]);
+	return 1;
 }
 
 	/* In contrast to FRAP and the master section of the descriptor the bits
@@ -1460,21 +1468,25 @@
 #define ICH_PR_PERMS(pr)	(((~((pr) >> PR_RP_OFF) & 1) << 0) | \
 				 ((~((pr) >> PR_WP_OFF) & 1) << 1))
 
-static void prettyprint_ich9_reg_pr(int i)
+/* returns 0 if range is unused (i.e. r/w) */
+static int ich9_handle_pr(int i)
 {
-	static const char *const access_names[4] = {
-		"locked", "read-only", "write-only", "read-write"
+	static const char *const access_names[3] = {
+		"locked", "read-only", "write-only"
 	};
 	uint8_t off = ICH9_REG_PR0 + (i * 4);
 	uint32_t pr = mmio_readl(ich_spibar + off);
-	int rwperms = ICH_PR_PERMS(pr);
+	unsigned int rwperms = ICH_PR_PERMS(pr);
 
-	msg_pdbg2("0x%02X: 0x%08x (PR%u", off, pr, i);
-	if (rwperms != 0x3)
-		msg_pdbg2(")\n0x%08x-0x%08x is %s\n", ICH_FREG_BASE(pr),
-			 ICH_FREG_LIMIT(pr) | 0x0fff, access_names[rwperms]);
-	else
-		msg_pdbg2(", unused)\n");
+	if (rwperms == 0x3) {
+		msg_pdbg2("0x%02X: 0x%08x (PR%u is unused)\n", off, pr, i);
+		return 0;
+	}
+
+	msg_pdbg("0x%02X: 0x%08x ", off, pr);
+	msg_pinfo("PR%u: WARNING: 0x%08x-0x%08x is %s.\n", i, ICH_FREG_BASE(pr),
+		  ICH_FREG_LIMIT(pr) | 0x0fff, access_names[rwperms]);
+	return 1;
 }
 
 /* Set/Clear the read and write protection enable bits of PR register @i
@@ -1537,6 +1549,8 @@
 	uint16_t spibar_offset, tmp2;
 	uint32_t tmp;
 	char *arg;
+	int ich_spi_force = 0;
+	int ich_spi_rw_restricted = 0;
 	int desc_valid = 0;
 	struct ich_descriptors desc = {{ 0 }};
 	enum ich_spi_mode {
@@ -1631,6 +1645,22 @@
 		}
 		free(arg);
 
+		arg = extract_programmer_param("ich_spi_force");
+		if (arg && !strcmp(arg, "yes")) {
+			ich_spi_force = 1;
+			msg_pspew("ich_spi_force enabled.\n");
+		} else if (arg && !strlen(arg)) {
+			msg_perr("Missing argument for ich_spi_force.\n");
+			free(arg);
+			return ERROR_FATAL;
+		} else if (arg) {
+			msg_perr("Unknown argument for ich_spi_force: \"%s\" "
+				 "(not \"yes\").\n", arg);
+			free(arg);
+			return ERROR_FATAL;
+		}
+		free(arg);
+
 		tmp2 = mmio_readw(ich_spibar + ICH9_REG_HSFS);
 		msg_pdbg("0x04: 0x%04x (HSFS)\n", tmp2);
 		prettyprint_ich9_reg_hsfs(tmp2);
@@ -1665,17 +1695,36 @@
 			msg_pdbg("BRWA 0x%02x, ", ICH_BRWA(tmp));
 			msg_pdbg("BRRA 0x%02x\n", ICH_BRRA(tmp));
 
-			/* Decode and print FREGx and FRAP registers */
+			/* Handle FREGx and FRAP registers */
 			for (i = 0; i < 5; i++)
-				do_ich9_spi_frap(tmp, i);
+				ich_spi_rw_restricted |= ich9_handle_frap(tmp, i);
 		}
 
-		/* try to disable PR locks before printing them */
-		if (!ichspi_lock)
-			for (i = 0; i < 5; i++)
+		for (i = 0; i < 5; i++) {
+			/* if not locked down try to disable PR locks first */
+			if (!ichspi_lock)
 				ich9_set_pr(i, 0, 0);
-		for (i = 0; i < 5; i++)
-			prettyprint_ich9_reg_pr(i);
+			ich_spi_rw_restricted |= ich9_handle_pr(i);
+		}
+
+		if (ich_spi_rw_restricted) {
+			msg_pinfo("Please send a verbose log to "
+				  "flashrom@flashrom.org if this board is not "
+				  "listed on\n"
+				  "http://flashrom.org/Supported_hardware#Supported_mainboards "
+				  "yet.\n");
+			if (!ich_spi_force)
+				programmer_may_write = 0;
+			msg_pinfo("Writes have been disabled. You can enforce "
+				  "write support with the\nich_spi_force "
+				  "programmer option, but it will most likely "
+				  "harm your hardware!\nIf you force flashrom "
+				  "you will get no support if something "
+				  "breaks.\n");
+			if (ich_spi_force)
+				msg_pinfo("Continuing with write support "
+					  "because the user forced us to!\n");
+		}
 
 		tmp = mmio_readl(ich_spibar + ICH9_REG_SSFS);
 		msg_pdbg("0x90: 0x%02x (SSFS)\n", tmp & 0xff);