| /* Copyright 2018 The ChromiumOS Authors |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| /* Flash memory module for STM32H7 family */ |
| |
| #include "clock.h" |
| #include "common.h" |
| #include "cpu.h" |
| #include "flash-regs.h" |
| #include "flash.h" |
| #include "hooks.h" |
| #include "panic.h" |
| #include "registers.h" |
| #include "system.h" |
| #include "task.h" |
| #include "timer.h" |
| #include "util.h" |
| #include "watchdog.h" |
| |
| /* |
| * Approximate number of CPU cycles per iteration of the loop when polling |
| * the flash status |
| */ |
| #define CYCLE_PER_FLASH_LOOP 2 |
| |
| /* Flash 256-bit word programming timeout. */ |
| #define FLASH_TIMEOUT_US 600 |
| |
| /* |
| * Flash 128-KB block erase timeout. |
| * Datasheet says maximum is about 4 seconds in x8. |
| * Real delay seems to be: < 1 second in x64, < 2 seconds in x8. |
| */ |
| #define FLASH_ERASE_TIMEOUT_US (4200 * MSEC) |
| |
| /* |
| * Option bytes programming timeout. |
| * No specification, real delay seems to be around 300ms. |
| */ |
| #define FLASH_OPT_PRG_TIMEOUT_US (1000 * MSEC) |
| |
| /* |
| * All variants have 2 banks (as in parallel hardware / controllers) |
| * not what is called 'bank' in the common code (ie Write-Protect sectors) |
| * both have the same number of 128KB blocks. |
| */ |
| #define HWBANK_SIZE (CONFIG_FLASH_SIZE_BYTES / 2) |
| #define BLOCKS_PER_HWBANK (HWBANK_SIZE / CONFIG_FLASH_ERASE_SIZE) |
| #define BLOCKS_HWBANK_MASK (BIT(BLOCKS_PER_HWBANK) - 1) |
| |
| /* |
| * We can tune the power consumption vs erase/write speed |
| * by default, go fast (and consume current) |
| */ |
| #define DEFAULT_PSIZE FLASH_CR_PSIZE_DWORD |
| |
| /* Can no longer write/erase flash until next reboot */ |
| static int access_disabled; |
| /* Can no longer modify write-protection in option bytes until next reboot */ |
| static int option_disabled; |
| /* Is physical flash stuck protected? (avoid reboot loop) */ |
| static int stuck_locked; |
| |
| #define FLASH_SYSJUMP_TAG 0x5750 /* "WP" - Write Protect */ |
| #define FLASH_HOOK_VERSION 1 |
| |
| /* The previous write protect state before sys jump */ |
| struct flash_wp_state { |
| int access_disabled; |
| int option_disabled; |
| int stuck_locked; |
| }; |
| |
| static inline int calculate_flash_timeout(void) |
| { |
| return (FLASH_TIMEOUT_US * (clock_get_freq() / SECOND) / |
| CYCLE_PER_FLASH_LOOP); |
| } |
| |
| static int unlock(int bank) |
| { |
| /* unlock CR only if needed */ |
| if (STM32_FLASH_CR(bank) & FLASH_CR_LOCK) { |
| /* |
| * We may have already locked the flash module and get a bus |
| * fault in the attempt to unlock. Need to disable bus fault |
| * handler now. |
| */ |
| ignore_bus_fault(1); |
| |
| STM32_FLASH_KEYR(bank) = FLASH_KEYR_KEY1; |
| STM32_FLASH_KEYR(bank) = FLASH_KEYR_KEY2; |
| ignore_bus_fault(0); |
| } |
| |
| return (STM32_FLASH_CR(bank) & FLASH_CR_LOCK) ? EC_ERROR_UNKNOWN : |
| EC_SUCCESS; |
| } |
| |
| static void lock(int bank) |
| { |
| STM32_FLASH_CR(bank) |= FLASH_CR_LOCK; |
| } |
| |
| static int unlock_optb(void) |
| { |
| if (option_disabled) |
| return EC_ERROR_ACCESS_DENIED; |
| |
| if (unlock(0)) |
| return EC_ERROR_UNKNOWN; |
| |
| if (flash_option_bytes_locked()) { |
| /* |
| * We may have already locked the flash module and get a bus |
| * fault in the attempt to unlock. Need to disable bus fault |
| * handler now. |
| */ |
| ignore_bus_fault(1); |
| |
| unlock_flash_option_bytes(); |
| ignore_bus_fault(0); |
| } |
| |
| return flash_option_bytes_locked() ? EC_ERROR_UNKNOWN : EC_SUCCESS; |
| } |
| |
| static int commit_optb(void) |
| { |
| /* might use this before timer_init, cannot use get_time/usleep */ |
| int timeout = (FLASH_OPT_PRG_TIMEOUT_US * (clock_get_freq() / SECOND) / |
| CYCLE_PER_FLASH_LOOP); |
| |
| STM32_FLASH_OPTCR(0) |= FLASH_OPTCR_OPTSTART; |
| |
| while (STM32_FLASH_OPTSR_CUR(0) & FLASH_OPTSR_BUSY && timeout-- > 0) |
| ; |
| |
| lock_flash_option_bytes(); |
| lock(0); |
| |
| return (timeout > 0) ? EC_SUCCESS : EC_ERROR_TIMEOUT; |
| } |
| |
| static void protect_blocks(uint32_t blocks) |
| { |
| if (unlock_optb()) |
| return; |
| STM32_FLASH_WPSN_PRG(0) &= ~(blocks & BLOCKS_HWBANK_MASK); |
| STM32_FLASH_WPSN_PRG(1) &= |
| ~((blocks >> BLOCKS_PER_HWBANK) & BLOCKS_HWBANK_MASK); |
| commit_optb(); |
| } |
| |
| /* |
| * Helper function definitions for consistency with F4 to enable flash |
| * physical unitesting |
| */ |
| void unlock_flash_control_register(void) |
| { |
| unlock(0); |
| unlock(1); |
| } |
| |
| void unlock_flash_option_bytes(void) |
| { |
| /* |
| * Always use bank 0 flash controller as there is only one option bytes |
| * set for both banks. See http://b/181130245 |
| * |
| * Consecutively program values. Ref: RM0433:4.9.2 |
| */ |
| STM32_FLASH_OPTKEYR(0) = FLASH_OPTKEYR_KEY1; |
| STM32_FLASH_OPTKEYR(0) = FLASH_OPTKEYR_KEY2; |
| } |
| |
| void disable_flash_option_bytes(void) |
| { |
| ignore_bus_fault(1); |
| /* |
| * Always use bank 0 flash controller as there is only one option bytes |
| * set for both banks. See http://b/181130245 |
| * |
| * Writing anything other than the pre-defined keys to the option key |
| * register results in a bus fault and the register being locked until |
| * reboot (even with a further correct key write). |
| */ |
| STM32_FLASH_OPTKEYR(0) = 0xffffffff; |
| ignore_bus_fault(0); |
| } |
| |
| void disable_flash_control_register(void) |
| { |
| ignore_bus_fault(1); |
| /* |
| * Writing anything other than the pre-defined keys to a key |
| * register results in a bus fault and the register being locked until |
| * reboot (even with a further correct key write). |
| */ |
| STM32_FLASH_KEYR(0) = 0xffffffff; |
| STM32_FLASH_KEYR(1) = 0xffffffff; |
| ignore_bus_fault(0); |
| } |
| |
| void lock_flash_control_register(void) |
| { |
| lock(0); |
| lock(1); |
| } |
| |
| void lock_flash_option_bytes(void) |
| { |
| /* |
| * Always use bank 0 flash controller as there is only one option bytes |
| * set for both banks. See http://b/181130245 |
| */ |
| STM32_FLASH_OPTCR(0) |= FLASH_OPTCR_OPTLOCK; |
| } |
| |
| bool flash_option_bytes_locked(void) |
| { |
| /* |
| * Always use bank 0 flash controller as there is only one option bytes |
| * set for both banks. See http://b/181130245 |
| */ |
| return !!(STM32_FLASH_OPTCR(0) & FLASH_OPTCR_OPTLOCK); |
| } |
| |
| bool flash_control_register_locked(void) |
| { |
| return !!(STM32_FLASH_CR(0) & FLASH_CR_LOCK) && |
| !!(STM32_FLASH_CR(1) & FLASH_CR_LOCK); |
| } |
| |
| /* |
| * If RDP as PSTATE option is defined, use that as 'Write Protect enabled' flag: |
| * it makes no sense to be able to unlock RO, as that'd allow flashing |
| * arbitrary RO that could read back all flash. |
| * |
| * crbug.com/888109: Do not copy this code over to other STM32 chips without |
| * understanding the full implications. |
| * |
| * If RDP is not defined, use the option bytes RSS1 bit. |
| * TODO(crbug.com/888104): Validate that using RSS1 for this purpose is safe. |
| */ |
| #ifndef CONFIG_FLASH_READOUT_PROTECTION_AS_PSTATE |
| #error "crbug.com/888104: Using RSS1 for write protect PSTATE may not be safe." |
| #endif |
| static int is_wp_enabled(void) |
| { |
| #ifdef CONFIG_FLASH_READOUT_PROTECTION_AS_PSTATE |
| return (STM32_FLASH_OPTSR_CUR(0) & FLASH_OPTSR_RDP_MASK) != |
| FLASH_OPTSR_RDP_LEVEL_0; |
| #else |
| return !!(STM32_FLASH_OPTSR_CUR(0) & FLASH_OPTSR_RSS1); |
| #endif |
| } |
| |
| static int set_wp(int enabled) |
| { |
| int rv; |
| |
| rv = unlock_optb(); |
| if (rv) |
| return rv; |
| |
| #ifdef CONFIG_FLASH_READOUT_PROTECTION_AS_PSTATE |
| if (enabled) { |
| /* Enable RDP level 1. */ |
| STM32_FLASH_OPTSR_PRG(0) = |
| (STM32_FLASH_OPTSR_PRG(0) & ~FLASH_OPTSR_RDP_MASK) | |
| FLASH_OPTSR_RDP_LEVEL_1; |
| } |
| #else |
| if (enabled) |
| STM32_FLASH_OPTSR_PRG(0) |= FLASH_OPTSR_RSS1; |
| else |
| STM32_FLASH_OPTSR_PRG(0) &= ~FLASH_OPTSR_RSS1; |
| #endif |
| |
| return commit_optb(); |
| } |
| |
| /*****************************************************************************/ |
| /* Physical layer APIs */ |
| |
| int crec_flash_physical_write(int offset, int size, const char *data) |
| { |
| int res = EC_SUCCESS; |
| int bank = offset / HWBANK_SIZE; |
| uint32_t *address = (void *)(CONFIG_PROGRAM_MEMORY_BASE + offset); |
| int timeout = calculate_flash_timeout(); |
| int i; |
| int unaligned = (uint32_t)data & (CONFIG_FLASH_WRITE_SIZE - 1); |
| uint32_t *data32 = (void *)data; |
| |
| if (access_disabled) |
| return EC_ERROR_ACCESS_DENIED; |
| |
| /* work on a single hardware bank at a time */ |
| if ((offset + size - 1) / HWBANK_SIZE != bank) |
| return EC_ERROR_INVAL; |
| |
| if (unlock(bank) != EC_SUCCESS) |
| return EC_ERROR_UNKNOWN; |
| |
| /* Clear previous error status */ |
| STM32_FLASH_CCR(bank) = FLASH_CCR_ERR_MASK; |
| |
| /* select write parallelism */ |
| STM32_FLASH_CR(bank) = (STM32_FLASH_CR(bank) & ~FLASH_CR_PSIZE_MASK) | |
| DEFAULT_PSIZE; |
| |
| /* set PG bit */ |
| STM32_FLASH_CR(bank) |= FLASH_CR_PG; |
| |
| for (; size > 0; size -= CONFIG_FLASH_WRITE_SIZE) { |
| /* |
| * Reload the watchdog timer to avoid watchdog reset when doing |
| * long writing. |
| */ |
| watchdog_reload(); |
| |
| /* write a 256-bit flash word */ |
| if (unaligned) { |
| for (i = 0; i < CONFIG_FLASH_WRITE_SIZE / 4; |
| i++, data += 4) |
| *address++ = (uint32_t)data[0] | |
| (data[1] << 8) | (data[2] << 16) | |
| (data[3] << 24); |
| } else { |
| for (i = 0; i < CONFIG_FLASH_WRITE_SIZE / 4; i++) |
| *address++ = *data32++; |
| } |
| |
| /* Wait for writes to complete */ |
| for (i = 0; |
| (STM32_FLASH_SR(bank) & (FLASH_SR_WBNE | FLASH_SR_QW)) && |
| (i < timeout); |
| i++) |
| ; |
| |
| if (STM32_FLASH_SR(bank) & (FLASH_SR_WBNE | FLASH_SR_QW)) { |
| res = EC_ERROR_TIMEOUT; |
| goto exit_wr; |
| } |
| |
| if (STM32_FLASH_SR(bank) & FLASH_CCR_ERR_MASK) { |
| res = EC_ERROR_UNKNOWN; |
| goto exit_wr; |
| } |
| } |
| |
| exit_wr: |
| /* Disable PG bit */ |
| STM32_FLASH_CR(bank) &= ~FLASH_CR_PG; |
| |
| lock(bank); |
| |
| #ifdef CONFIG_ARMV7M_CACHE |
| /* Invalidate D-cache, to make sure we do not read back stale data. */ |
| cpu_clean_invalidate_dcache(); |
| #endif |
| |
| return res; |
| } |
| |
| int crec_flash_physical_erase(int offset, int size) |
| { |
| int res = EC_SUCCESS; |
| int bank = offset / HWBANK_SIZE; |
| int last = (offset + size) / CONFIG_FLASH_ERASE_SIZE; |
| int sect; |
| |
| if (access_disabled) |
| return EC_ERROR_ACCESS_DENIED; |
| |
| /* work on a single hardware bank at a time */ |
| if ((offset + size - 1) / HWBANK_SIZE != bank) |
| return EC_ERROR_INVAL; |
| |
| if (unlock(bank) != EC_SUCCESS) |
| return EC_ERROR_UNKNOWN; |
| |
| /* Clear previous error status */ |
| STM32_FLASH_CCR(bank) = FLASH_CCR_ERR_MASK; |
| |
| /* select erase parallelism */ |
| STM32_FLASH_CR(bank) = (STM32_FLASH_CR(bank) & ~FLASH_CR_PSIZE_MASK) | |
| DEFAULT_PSIZE; |
| |
| for (sect = offset / CONFIG_FLASH_ERASE_SIZE; sect < last; sect++) { |
| timestamp_t deadline; |
| |
| /* select page to erase and PER bit */ |
| STM32_FLASH_CR(bank) = |
| (STM32_FLASH_CR(bank) & ~FLASH_CR_SNB_MASK) | |
| FLASH_CR_SER | FLASH_CR_SNB(sect); |
| |
| /* set STRT bit : start erase */ |
| STM32_FLASH_CR(bank) |= FLASH_CR_STRT; |
| |
| /* |
| * Reload the watchdog timer to avoid watchdog reset during a |
| * long erase operation. |
| */ |
| watchdog_reload(); |
| |
| deadline.val = get_time().val + FLASH_ERASE_TIMEOUT_US; |
| /* Wait for erase to complete */ |
| while ((STM32_FLASH_SR(bank) & FLASH_SR_BUSY) && |
| (get_time().val < deadline.val)) { |
| /* |
| * Interrupts may not be enabled, so we are using |
| * udelay() instead of crec_usleep() which can trigger |
| * Forced Hard Fault (see b/180761547). |
| */ |
| udelay(5000); |
| } |
| if (STM32_FLASH_SR(bank) & FLASH_SR_BUSY) { |
| res = EC_ERROR_TIMEOUT; |
| goto exit_er; |
| } |
| |
| /* |
| * Check for error conditions - erase failed, voltage error, |
| * protection error |
| */ |
| if (STM32_FLASH_SR(bank) & FLASH_CCR_ERR_MASK) { |
| res = EC_ERROR_UNKNOWN; |
| goto exit_er; |
| } |
| } |
| |
| exit_er: |
| /* reset SER bit */ |
| STM32_FLASH_CR(bank) &= ~(FLASH_CR_SER | FLASH_CR_SNB_MASK); |
| |
| lock(bank); |
| |
| #ifdef CONFIG_ARMV7M_CACHE |
| /* Invalidate D-cache, to make sure we do not read back stale data. */ |
| cpu_clean_invalidate_dcache(); |
| #endif |
| |
| return res; |
| } |
| |
| int crec_flash_physical_get_protect(int block) |
| { |
| int bank = block / BLOCKS_PER_HWBANK; |
| int index = block % BLOCKS_PER_HWBANK; |
| |
| return !(STM32_FLASH_WPSN_CUR(bank) & BIT(index)); |
| } |
| |
| /* |
| * Note: This does not need to update _NOW flags, as flash_get_protect |
| * in common code already does so. |
| */ |
| uint32_t crec_flash_physical_get_protect_flags(void) |
| { |
| uint32_t flags = 0; |
| |
| if (access_disabled) |
| flags |= EC_FLASH_PROTECT_ALL_NOW; |
| |
| if (is_wp_enabled()) |
| flags |= EC_FLASH_PROTECT_RO_AT_BOOT; |
| |
| /* Check if blocks were stuck locked at pre-init */ |
| if (stuck_locked) |
| flags |= EC_FLASH_PROTECT_ERROR_STUCK; |
| |
| return flags; |
| } |
| |
| #define WP_RANGE(start, count) (((1 << (count)) - 1) << (start)) |
| #define RO_WP_RANGE WP_RANGE(WP_BANK_OFFSET, WP_BANK_COUNT) |
| |
| int crec_flash_physical_protect_now(bool all) |
| { |
| protect_blocks(RO_WP_RANGE); |
| |
| /* |
| * Lock the option bytes or the full access by writing a wrong |
| * key to FLASH_*KEYR. This triggers a bus fault, so we need to |
| * disable bus fault handler while doing this. |
| * |
| * This incorrect key fault causes the flash to become |
| * permanently locked until reset, a correct keyring write |
| * will not unlock it. |
| */ |
| |
| if (all) { |
| /* cannot do any write/erase access until next reboot */ |
| disable_flash_control_register(); |
| access_disabled = 1; |
| } |
| /* cannot modify the WP bits in the option bytes until reboot */ |
| disable_flash_option_bytes(); |
| option_disabled = 1; |
| |
| return EC_SUCCESS; |
| } |
| |
| int crec_flash_physical_protect_at_boot(uint32_t new_flags) |
| { |
| int new_wp_enable = !!(new_flags & EC_FLASH_PROTECT_RO_AT_BOOT); |
| |
| if (is_wp_enabled() != new_wp_enable) |
| return set_wp(new_wp_enable); |
| |
| return EC_SUCCESS; |
| } |
| |
| uint32_t crec_flash_physical_get_valid_flags(void) |
| { |
| return EC_FLASH_PROTECT_RO_AT_BOOT | EC_FLASH_PROTECT_RO_NOW | |
| EC_FLASH_PROTECT_ALL_NOW; |
| } |
| |
| uint32_t crec_flash_physical_get_writable_flags(uint32_t cur_flags) |
| { |
| uint32_t ret = 0; |
| |
| /* If RO protection isn't enabled, its at-boot state can be changed. */ |
| if (!(cur_flags & EC_FLASH_PROTECT_RO_NOW)) |
| ret |= EC_FLASH_PROTECT_RO_AT_BOOT; |
| |
| /* |
| * If entire flash isn't protected at this boot, it can be enabled if |
| * the WP GPIO is asserted. |
| */ |
| if (!(cur_flags & EC_FLASH_PROTECT_ALL_NOW) && |
| (cur_flags & EC_FLASH_PROTECT_GPIO_ASSERTED)) |
| ret |= EC_FLASH_PROTECT_ALL_NOW; |
| |
| return ret; |
| } |
| |
| int crec_flash_physical_restore_state(void) |
| { |
| uint32_t reset_flags = system_get_reset_flags(); |
| int version, size; |
| const struct flash_wp_state *prev; |
| |
| /* |
| * If we have already jumped between images, an earlier image could |
| * have applied write protection. We simply need to represent these |
| * irreversible flags to other components. |
| */ |
| if (reset_flags & EC_RESET_FLAG_SYSJUMP) { |
| prev = (const struct flash_wp_state *)system_get_jump_tag( |
| FLASH_SYSJUMP_TAG, &version, &size); |
| if (prev && version == FLASH_HOOK_VERSION && |
| size == sizeof(*prev)) { |
| access_disabled = prev->access_disabled; |
| option_disabled = prev->option_disabled; |
| stuck_locked = prev->stuck_locked; |
| } |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| int crec_flash_pre_init(void) |
| { |
| uint32_t reset_flags = system_get_reset_flags(); |
| uint32_t prot_flags = crec_flash_get_protect(); |
| uint32_t unwanted_prot_flags = EC_FLASH_PROTECT_ALL_NOW | |
| EC_FLASH_PROTECT_ERROR_INCONSISTENT; |
| |
| if (crec_flash_physical_restore_state()) |
| return EC_SUCCESS; |
| |
| /* |
| * If we have already jumped between images, an earlier image could |
| * have applied write protection. Nothing additional needs to be done. |
| */ |
| if (reset_flags & EC_RESET_FLAG_SYSJUMP) |
| return EC_SUCCESS; |
| |
| 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; |
| |
| rv = crec_flash_set_protect(EC_FLASH_PROTECT_RO_NOW, |
| EC_FLASH_PROTECT_RO_NOW); |
| if (rv) |
| return rv; |
| |
| /* Re-read flags */ |
| prot_flags = crec_flash_get_protect(); |
| } |
| } else { |
| /* Don't want RO flash protected */ |
| unwanted_prot_flags |= EC_FLASH_PROTECT_RO_NOW; |
| } |
| |
| /* If there are no unwanted flags, done */ |
| if (!(prot_flags & unwanted_prot_flags)) |
| return EC_SUCCESS; |
| |
| /* |
| * If the last reboot was a power-on reset, it should have cleared |
| * 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 & EC_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); |
| |
| /* That doesn't return, so if we're still here that's an error */ |
| return EC_ERROR_UNKNOWN; |
| } |
| |
| /*****************************************************************************/ |
| /* Hooks */ |
| |
| static void flash_preserve_state(void) |
| { |
| const struct flash_wp_state state = { |
| .access_disabled = access_disabled, |
| .option_disabled = option_disabled, |
| .stuck_locked = stuck_locked, |
| }; |
| |
| system_add_jump_tag(FLASH_SYSJUMP_TAG, FLASH_HOOK_VERSION, |
| sizeof(state), &state); |
| } |
| DECLARE_HOOK(HOOK_SYSJUMP, flash_preserve_state, HOOK_PRIO_DEFAULT); |