summaryrefslogtreecommitdiff
path: root/chip/lm4/flash.c
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2012-07-23 13:08:40 -0700
committerGerrit <chrome-bot@google.com>2012-07-23 23:30:33 -0700
commit1661f1cef07f5b29b7e9c06fb3220fb48d110686 (patch)
tree10149783f545d199b5abcdf4122336cbc9494648 /chip/lm4/flash.c
parent0653aa011ac5ce05f2e0d70bae90b5b7d126ea73 (diff)
downloadchrome-ec-1661f1cef07f5b29b7e9c06fb3220fb48d110686.tar.gz
Refactor flash module
This is a significant rewrite of the flash module, since it turns out that much less of the flash logic is actually common between stm32 and lm4. BUG=chrome-os-partner:11699 TEST=on link, (enable hardware wp) flashinfo -> wp_gpio_asserted flashwp enable flashinfo -> wp_gpio_asserted ro_at_boot reboot flashinfo -> wp_gpio_asserted ro_at_boot ro_now flashwp disable -> error 7 flashwp now flashinfo -> wp_gpio_asserted ro_at_boot ro_now rw_now reboot flashinfo -> wp_gpio_asserted ro_at_boot ro_now (disable hardware wp) reboot flashinfo -> ro_at_boot flashwp disable flashinfo -> (no flags) Change-Id: If22b02373946ce1c080d49ccded4f8fa3e380115 Signed-off-by: Randall Spangler <rspangler@chromium.org> Reviewed-on: https://gerrit.chromium.org/gerrit/28200 Reviewed-by: Vic Yang <victoryang@chromium.org>
Diffstat (limited to 'chip/lm4/flash.c')
-rw-r--r--chip/lm4/flash.c252
1 files changed, 232 insertions, 20 deletions
diff --git a/chip/lm4/flash.c b/chip/lm4/flash.c
index 0bbf0ede67..826783b45b 100644
--- a/chip/lm4/flash.c
+++ b/chip/lm4/flash.c
@@ -6,6 +6,7 @@
/* Flash memory module for Chrome EC */
#include "flash.h"
+#include "power_button.h"
#include "registers.h"
#include "system.h"
#include "timer.h"
@@ -24,9 +25,129 @@
#define ERASE_TIMEOUT_MS 200
#define WRITE_TIMEOUT_US 300
-int flash_physical_size(void)
+/* Number of physical banks of flash */
+#define PHYSICAL_BANKS (CONFIG_FLASH_PHYSICAL_SIZE / CONFIG_FLASH_BANK_SIZE)
+
+/* Persistent protection state flash offset / size / bank */
+#define PSTATE_OFFSET CONFIG_SECTION_FLASH_PSTATE_OFF
+#define PSTATE_SIZE CONFIG_SECTION_FLASH_PSTATE_SIZE
+#define PSTATE_BANK (PSTATE_OFFSET / CONFIG_FLASH_BANK_SIZE)
+
+/* Read-only firmware offset and size in units of flash banks */
+#define RO_BANK_OFFSET (CONFIG_SECTION_RO_OFF / CONFIG_FLASH_BANK_SIZE)
+#define RO_BANK_COUNT (CONFIG_SECTION_RO_SIZE / CONFIG_FLASH_BANK_SIZE)
+
+int stuck_locked; /* Is physical flash stuck protected? */
+
+/* Persistent protection state - emulates a SPI status register for flashrom */
+struct persist_state {
+ uint8_t version; /* Version of this struct */
+ uint8_t flags; /* Lock flags (PERSIST_FLAG_*) */
+ uint8_t reserved[2]; /* Reserved; set 0 */
+};
+
+#define PERSIST_STATE_VERSION 2 /* Expected persist_state.version */
+
+/* Flags for persist_state.flags */
+/* Protect persist state and RO firmware at boot */
+#define PERSIST_FLAG_PROTECT_RO 0x02
+
+/**
+ * Read persistent state into pstate.
+ */
+static int read_pstate(struct persist_state *pstate)
+{
+ memcpy(pstate, flash_physical_dataptr(PSTATE_OFFSET), sizeof(*pstate));
+
+ /* Sanity-check data and initialize if necessary */
+ if (pstate->version != PERSIST_STATE_VERSION) {
+ memset(pstate, 0, sizeof(*pstate));
+ pstate->version = PERSIST_STATE_VERSION;
+ }
+
+ return EC_SUCCESS;
+}
+
+/**
+ * Write persistent state from pstate, erasing if necessary.
+ */
+static int write_pstate(const struct persist_state *pstate)
+{
+ struct persist_state current_pstate;
+ int rv;
+
+ /* Check if pstate has actually changed */
+ if (!read_pstate(&current_pstate) &&
+ !memcmp(&current_pstate, pstate, sizeof(*pstate)))
+ return EC_SUCCESS;
+
+ /* Erase pstate */
+ rv = flash_physical_erase(PSTATE_OFFSET, PSTATE_SIZE);
+ if (rv)
+ return rv;
+
+ /*
+ * Note that if we lose power in here, we'll lose the pstate contents.
+ * That's ok, because it's only possible to write the pstate before
+ * it's protected.
+ */
+
+ /* Rewrite the data */
+ return flash_physical_write(PSTATE_OFFSET, sizeof(*pstate),
+ (const char *)pstate);
+}
+
+/**
+ * Enable write protect for the read-only code.
+ *
+ * Once write protect is enabled, it will STAY enabled until the system is
+ * hard-rebooted with the hardware write protect pin deasserted. If the write
+ * protect pin is deasserted, the protect setting is ignored, and the entire
+ * flash will be writable.
+ *
+ * @param enable Enable write protection
+ */
+static int protect_ro_at_boot(int enable)
{
- return (LM4_FLASH_FSIZE + 1) * CONFIG_FLASH_BANK_SIZE;
+ struct persist_state pstate;
+ int new_flags = enable ? PERSIST_FLAG_PROTECT_RO : 0;
+ int rv;
+
+ /* Read the current persist state from flash */
+ rv = read_pstate(&pstate);
+ if (rv)
+ return rv;
+
+ /* Update state if necessary */
+ if (pstate.flags != new_flags) {
+
+ /* Fail if write protect block is already locked */
+ if (flash_physical_get_protect(PSTATE_BANK))
+ return EC_ERROR_ACCESS_DENIED;
+
+ /* Set the new flag */
+ pstate.flags = new_flags;
+
+ /* Write the state back to flash */
+ rv = write_pstate(&pstate);
+ if (rv)
+ return rv;
+ }
+
+ return EC_SUCCESS;
+}
+
+/**
+ * Protect flash banks until reboot.
+ *
+ * @param start_bank Start bank to protect
+ * @param bank_count Number of banks to protect
+ */
+static void protect_banks(int start_bank, int bank_count)
+{
+ int bank;
+ for (bank = start_bank; bank < start_bank + bank_count; bank++)
+ LM4_FLASH_FMPPE[F_BANK(bank)] &= ~F_BIT(bank);
}
/**
@@ -67,6 +188,8 @@ static int write_buffer(void)
return EC_SUCCESS;
}
+/*****************************************************************************/
+/* Physical layer APIs */
int flash_physical_write(int offset, int size, const char *data)
{
@@ -101,7 +224,6 @@ int flash_physical_write(int offset, int size, const char *data)
return EC_SUCCESS;
}
-
int flash_physical_erase(int offset, int size)
{
LM4_FLASH_FCMISC = LM4_FLASH_FCRIS; /* Clear previous error status */
@@ -138,25 +260,101 @@ int flash_physical_erase(int offset, int size)
return EC_SUCCESS;
}
-
int flash_physical_get_protect(int bank)
{
return (LM4_FLASH_FMPPE[F_BANK(bank)] & F_BIT(bank)) ? 0 : 1;
}
+/*****************************************************************************/
+/* High-level APIs */
-void flash_physical_set_protect(int start_bank, int bank_count)
+uint32_t flash_get_protect(void)
{
- int bank;
- for (bank = start_bank; bank < start_bank + bank_count; bank++)
- LM4_FLASH_FMPPE[F_BANK(bank)] &= ~F_BIT(bank);
+ struct persist_state pstate;
+ uint32_t flags = 0;
+ int i;
+
+ /* Read the current persist state from flash */
+ read_pstate(&pstate);
+ if (pstate.flags & PERSIST_FLAG_PROTECT_RO)
+ flags |= EC_FLASH_PROTECT_RO_AT_BOOT;
+
+#ifdef CONFIG_TASK_POWERBTN
+ /* Check if write protect pin is asserted now */
+ if (write_protect_asserted())
+ flags |= EC_FLASH_PROTECT_GPIO_ASSERTED;
+#endif
+
+ /* Scan flash protection */
+ for (i = 0; i < PHYSICAL_BANKS; i++) {
+ /* Is this bank part of RO? */
+ int is_ro = ((i >= RO_BANK_OFFSET &&
+ i < RO_BANK_OFFSET + RO_BANK_COUNT) ||
+ i == PSTATE_BANK);
+ int bank_flag = (is_ro ? EC_FLASH_PROTECT_RO_NOW :
+ EC_FLASH_PROTECT_RW_NOW);
+
+ if (flash_physical_get_protect(i)) {
+ /* At least one bank in the region is protected */
+ flags |= bank_flag;
+ } else if (flags & bank_flag) {
+ /* But not all banks in the region! */
+ flags |= EC_FLASH_PROTECT_ERROR_INCONSISTENT;
+ }
+ }
+
+ /* Check if blocks were stuck locked at pre-init */
+ if (stuck_locked)
+ flags |= EC_FLASH_PROTECT_ERROR_STUCK;
+
+ return flags;
}
-int flash_physical_pre_init(void)
+int flash_set_protect(uint32_t mask, uint32_t flags)
{
- int reset_flags = system_get_reset_flags();
- int any_wp = 0;
- int i;
+ int retval = EC_SUCCESS;
+ int rv;
+
+ /*
+ * Process flags we can set. Track the most recent error, but process
+ * all flags before returning.
+ */
+
+ if (mask & EC_FLASH_PROTECT_RO_AT_BOOT) {
+ rv = protect_ro_at_boot(flags & EC_FLASH_PROTECT_RO_AT_BOOT);
+ if (rv)
+ retval = rv;
+ }
+
+ /* All subsequent flags only work if write protect is disabled */
+ if (!(flash_get_protect() & EC_FLASH_PROTECT_GPIO_ASSERTED))
+ return retval;
+
+ if ((mask & EC_FLASH_PROTECT_RO_NOW) &&
+ (flags & EC_FLASH_PROTECT_RO_NOW)) {
+ /* Protect pstate */
+ protect_banks(PSTATE_BANK, 1);
+
+ /* Protect the read-only section */
+ protect_banks(RO_BANK_OFFSET, RO_BANK_COUNT);
+ }
+
+ if ((mask & EC_FLASH_PROTECT_RW_NOW) &&
+ (flags & EC_FLASH_PROTECT_RW_NOW)) {
+ /* Protect the entire flash */
+ protect_banks(0, CONFIG_FLASH_PHYSICAL_SIZE /
+ CONFIG_FLASH_BANK_SIZE);
+ }
+
+ return retval;
+}
+
+int flash_pre_init(void)
+{
+ uint32_t reset_flags = system_get_reset_flags();
+ uint32_t prot_flags = flash_get_protect();
+ uint32_t unwanted_prot_flags = EC_FLASH_PROTECT_RW_NOW |
+ EC_FLASH_PROTECT_ERROR_INCONSISTENT;
/*
* If we have already jumped between images, an earlier image could
@@ -165,16 +363,28 @@ int flash_physical_pre_init(void)
if (reset_flags & RESET_FLAG_SYSJUMP)
return EC_SUCCESS;
- /* Check if any blocks are currently physically write-protected */
- for (i = 0; i < (LM4_FLASH_FSIZE + 1) / 32; i++) {
- if (LM4_FLASH_FMPPE[i] != 0xffffffff) {
- any_wp = 1;
- break;
+ if ((prot_flags & EC_FLASH_PROTECT_GPIO_ASSERTED)) {
+ /*
+ * Write protect is asserted. If we want RO flash protected,
+ * protect it now.
+ */
+ if ((prot_flags & EC_FLASH_PROTECT_RO_AT_BOOT) &&
+ !(prot_flags & EC_FLASH_PROTECT_RO_NOW)) {
+ int rv = flash_set_protect(EC_FLASH_PROTECT_RO_NOW,
+ EC_FLASH_PROTECT_RO_NOW);
+ if (rv)
+ return rv;
+
+ /* Re-read flags */
+ prot_flags = flash_get_protect();
}
+ } else {
+ /* Don't want RO flash protected */
+ unwanted_prot_flags |= EC_FLASH_PROTECT_RO_NOW;
}
- /* If nothing is write-protected, done. */
- if (!any_wp)
+ /* If there are no unwanted flags, done */
+ if (!(prot_flags & unwanted_prot_flags))
return EC_SUCCESS;
/*
@@ -182,8 +392,10 @@ int flash_physical_pre_init(void)
* write-protect. If it didn't, then the flash write protect registers
* have been permanently committed and we can't fix that.
*/
- if (reset_flags & RESET_FLAG_POWER_ON)
+ if (reset_flags & RESET_FLAG_POWER_ON) {
+ stuck_locked = 1;
return EC_ERROR_ACCESS_DENIED;
+ }
/* Otherwise, do a hard boot to clear the flash protection registers */
system_reset(SYSTEM_RESET_HARD | SYSTEM_RESET_PRESERVE_FLAGS);