diff options
Diffstat (limited to 'chip/lm4/flash.c')
-rw-r--r-- | chip/lm4/flash.c | 252 |
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(¤t_pstate) && + !memcmp(¤t_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); |