Adding PRR protection for MRC settings and RO area of Flash

BUG=chromium:1040046
TEST=run in developer and non-developer mode and verify that the right
sections are write protected

Change-Id: Icdeda00bc98d8ba6834490c6a3f0ca86d326a754
Signed-off-by: dossym@chromium.org
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/coreboot/+/2039492
Reviewed-by: Furquan Shaikh <furquan@chromium.org>
Commit-Queue: Furquan Shaikh <furquan@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/coreboot/+/2044929
diff --git a/src/drivers/spi/eon.c b/src/drivers/spi/eon.c
index c800359..a02f485 100644
--- a/src/drivers/spi/eon.c
+++ b/src/drivers/spi/eon.c
@@ -141,7 +141,7 @@
 
 static int eon_status(struct spi_flash *flash, u8 *reg)
 {
-	return spi_flash_cmd(flash->spi, CMD_EN25Q128_RDSR, reg, sizeof(*reg));
+	return spi_flash_cmd(flash->spi, CMD_EN25_RDSR, reg, sizeof(*reg));
 }
 
 struct spi_flash *spi_flash_probe_eon(struct spi_slave *spi, u8 *idcode)
diff --git a/src/soc/intel/baytrail/Kconfig b/src/soc/intel/baytrail/Kconfig
index d622cb5..05c7ed1 100644
--- a/src/soc/intel/baytrail/Kconfig
+++ b/src/soc/intel/baytrail/Kconfig
@@ -11,6 +11,7 @@
 	select ALT_CBFS_LOAD_PAYLOAD
 	select BACKUP_DEFAULT_SMM_REGION
 	select CACHE_MRC_SETTINGS
+	select MRC_SETTINGS_PROTECT
 	select CACHE_RELOCATED_RAMSTAGE_OUTSIDE_CBMEM
 	select CACHE_ROM
 	select CAR_MIGRATION
@@ -36,6 +37,7 @@
 	select TSC_MONOTONIC_TIMER
 	select TSC_SYNC_MFENCE
 	select UDELAY_TSC
+	select PRR_RO_PROTECT
 
 config BOOTBLOCK_CPU_INIT
 	string
@@ -104,10 +106,18 @@
 	hex
 	default 0x10000
 
+config MRC_SETTINGS_PROTECT
+	bool "Enable protection on MRC settings"
+	default n
+
 endif # CACHE_MRC_SETTINGS
 
 endif # HAVE_MRC
 
+config PRR_RO_PROTECT
+	bool "Enable protection of RO using PRR"
+	default n
+
 # Cache As RAM region layout:
 #
 # +-------------+ DCACHE_RAM_BASE + DCACHE_RAM_SIZE + DCACHE_RAM_MRC_VAR_SIZE
diff --git a/src/soc/intel/baytrail/baytrail/nvm.h b/src/soc/intel/baytrail/baytrail/nvm.h
index d0cbf7b..505d288 100644
--- a/src/soc/intel/baytrail/baytrail/nvm.h
+++ b/src/soc/intel/baytrail/baytrail/nvm.h
@@ -31,4 +31,13 @@
 /* Write data to NVM. Returns 0 on success < 0 on error.  */
 int nvm_write(void *start, const void *data, size_t size);
 
+/* Determine if flash device is write protected */
+int nvm_is_write_protected(void);
+
+/* Apply protection to a range of flash */
+int nvm_protect(void *start, size_t size);
+
+/* Protect RO range of Flash */
+int nvm_region_protect(const char name[]);
+
 #endif /* _NVM_H_ */
diff --git a/src/soc/intel/baytrail/baytrail/spi.h b/src/soc/intel/baytrail/baytrail/spi.h
index 4ffb93a..2950144 100644
--- a/src/soc/intel/baytrail/baytrail/spi.h
+++ b/src/soc/intel/baytrail/baytrail/spi.h
@@ -57,6 +57,12 @@
 # define BCR_LE				(0x1 << 1)
 # define BCR_WPD			(0x1 << 0)
 
+#define SPI_PRR_MAX			5
+#define SPI_PRR_SHIFT			12
+#define SPI_PRR_MASK			0x1fff
+#define SPI_PRR_LIMIT_SHIFT		16
+#define SPI_PRR_WPE			(1 << 31)
+
 /*
  * SPI lockdown configuration.
  */
@@ -71,5 +77,8 @@
 /* Return 0 on success < 0 on failure. */
 int mainboard_get_spi_config(struct spi_config *cfg);
 
+/* Return 0 on success < 0 on failure. */
+int spi_flash_protect(u32 start, u32 size);
+
 #endif /* _BAYTRAIL_SPI_H_ */
 
diff --git a/src/soc/intel/baytrail/nvm.c b/src/soc/intel/baytrail/nvm.c
index f3fce39..9f513f0 100644
--- a/src/soc/intel/baytrail/nvm.c
+++ b/src/soc/intel/baytrail/nvm.c
@@ -23,6 +23,11 @@
 #include <string.h>
 #include <spi-generic.h>
 #include <spi_flash.h>
+#include <baytrail/spi.h>
+#if CONFIG_CHROMEOS
+#include <vendorcode/google/chromeos/chromeos.h>
+#include <vendorcode/google/chromeos/fmap.h>
+#endif
 #include <baytrail/nvm.h>
 
 /* This module assumes the flash is memory mapped just below 4GiB in the
@@ -80,3 +85,66 @@
 		return -1;
 	return flash->write(flash, to_flash_offset(start), size, data);
 }
+
+/* Read flash status register to determine if write protect is active */
+int nvm_is_write_protected(void)
+{
+	u8 sr1;
+	u8 wp_gpio;
+	u8 wp_spi;
+
+	if (!IS_ENABLED(CONFIG_CHROMEOS))
+		return 0;
+
+	if (nvm_init() < 0)
+		return -1;
+
+	/* Read Write Protect GPIO if available */
+	wp_gpio = get_write_protect_state();
+
+	/* Read Status Register 1 */
+	if (spi_flash_status(flash, &sr1) < 0) {
+		printk(BIOS_ERR, "Failed to read SPI status register 1\n");
+		return -1;
+	}
+	wp_spi = !!(sr1 & 0x80);
+
+	printk(BIOS_DEBUG, "SPI flash protection: WPSW=%d SRP0=%d\n",
+	       wp_gpio, wp_spi);
+
+	return wp_gpio && wp_spi;
+}
+
+/* Apply protection to a range of flash */
+int nvm_protect(void *start, size_t size)
+{
+	if (nvm_init() < 0)
+		return -1;
+	return spi_flash_protect(to_flash_offset(start), size);
+}
+
+/* Protect a region of flash */
+int nvm_region_protect(const char name[])
+{
+	if (!IS_ENABLED(CONFIG_CHROMEOS))
+		return -1;
+
+	if (nvm_init() < 0)
+		return -1;
+
+	if (!developer_mode_enabled())
+	{
+		uint32_t wp_ro_size;
+		void *wp_ro_base;
+		wp_ro_size = find_fmap_entry(name, &wp_ro_base);
+		if (wp_ro_size < 0) {
+			printk(BIOS_ERR, "Could not find region %s\n", name);
+			return -1;
+		} else if (nvm_protect((void *)wp_ro_base, wp_ro_size) < 0) {
+			printk(BIOS_ERR, "Could not write protect region %s\n", name);
+			return -1;
+		}
+	}
+	return 0;
+}
+
diff --git a/src/soc/intel/baytrail/southcluster.c b/src/soc/intel/baytrail/southcluster.c
index 4ed52e6..2cc2a07 100644
--- a/src/soc/intel/baytrail/southcluster.c
+++ b/src/soc/intel/baytrail/southcluster.c
@@ -40,6 +40,8 @@
 #include <baytrail/pmc.h>
 #include <baytrail/ramstage.h>
 #include <baytrail/spi.h>
+#include <baytrail/nvm.h>
+#include <baytrail/mrc_cache.h>
 #include "chip.h"
 
 #if IS_ENABLED(CONFIG_CHROMEOS)
@@ -505,6 +507,14 @@
 	const unsigned long spi = SPI_BASE_ADDRESS;
 	struct spi_config cfg;
 
+	/* Protect WP RO range */
+	if (IS_ENABLED(CONFIG_PRR_RO_PROTECT))
+		nvm_region_protect("WP_RO");
+
+	/* Protect MRC Cache range */
+	if (IS_ENABLED(CONFIG_MRC_SETTINGS_PROTECT))
+		nvm_region_protect("RW_MRC_CACHE");
+
 	/* Set the lock enable on the BIOS control register. */
 	write32(bcr, read32(bcr) | BCR_LE);
 
diff --git a/src/soc/intel/baytrail/spi.c b/src/soc/intel/baytrail/spi.c
index 04e5f04..d7cf878 100644
--- a/src/soc/intel/baytrail/spi.c
+++ b/src/soc/intel/baytrail/spi.c
@@ -32,6 +32,7 @@
 
 #include <baytrail/lpc.h>
 #include <baytrail/pci_devs.h>
+#include <baytrail/spi.h>
 
 #ifdef __SMM__
 #define pci_read_config_byte(dev, reg, targ)\
@@ -113,6 +114,7 @@
 	uint8_t *status;
 	uint16_t *control;
 	uint32_t *bbar;
+	uint32_t *pr;
 } ich_spi_controller;
 
 static ich_spi_controller cntlr;
@@ -320,6 +322,7 @@
 	cntlr.control = (uint16_t *)ich9_spi->ssfc;
 	cntlr.bbar = &ich9_spi->bbar;
 	cntlr.preop = &ich9_spi->preop;
+	cntlr.pr = (uint32_t *)ich9_spi->pr;
 	ich_set_bbar(0);
 }
 
@@ -652,3 +655,49 @@
 
 	return 0;
 }
+
+/* Use first empty Protected Range Register to cover region of flash */
+int spi_flash_protect(u32 start, u32 size)
+{
+	u32 end = start + size - 1;
+	u32 reg;
+	int prr;
+
+	/* Sanity checks */
+	if (size == 0 || (size & 0xFFF) != 0 || (start & 0xFFF) != 0) {
+		printk(BIOS_ERR, "%s: invalid range for flash protect 0x%08x-0x%08x\n",
+			__func__, start, end);
+		return -1;
+	}
+
+	/* Find first empty PRR */
+	for (prr = 0; prr < SPI_PRR_MAX; prr++) {
+		reg = readl_(&cntlr.pr[prr]);
+		if (reg == 0)
+			break;
+	}
+	if (prr >= SPI_PRR_MAX) {
+		printk(BIOS_ERR, "ERROR: No SPI PRR free!\n");
+		return -1;
+	}
+
+	/* Set protected range base and limit */
+	reg = ((end >> SPI_PRR_SHIFT) & SPI_PRR_MASK);
+	reg <<= SPI_PRR_LIMIT_SHIFT;
+	reg |= ((start >> SPI_PRR_SHIFT) & SPI_PRR_MASK);
+	reg |= SPI_PRR_WPE;
+
+	/* Set the PRR register and verify it is protected */
+	writel_(reg, &cntlr.pr[prr]);
+	reg = readl_(&cntlr.pr[prr]);
+	if (!(reg & SPI_PRR_WPE)) {
+		printk(BIOS_ERR, "ERROR: Unable to set SPI PRR %d. Value returned: 0x%08x\n",
+			prr, reg);
+		return -1;
+	}
+
+	printk(BIOS_INFO, "%s: PRR %d is enabled for range 0x%08x-0x%08x\n",
+	       __func__, prr, start, end);
+	return 0;
+}
+