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;
+}
+