blob: 821d277b7a3df1328a7bb86ffd2c3aa570cea0d0 [file] [log] [blame]
/* Copyright 2017 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
/* Flash memory module for STM32L4 family */
#include "common.h"
#include "clock.h"
#include "flash.h"
#include "hooks.h"
#include "registers.h"
#include "panic.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 10
/* Flash page programming timeout. This is 2x the datasheet max. */
#define FLASH_TIMEOUT_US 48000
static inline int calculate_flash_timeout(void)
{
return (FLASH_TIMEOUT_US *
(clock_get_freq() / SECOND) / CYCLE_PER_FLASH_LOOP);
}
static int wait_while_busy(void)
{
int timeout = calculate_flash_timeout();
while (STM32_FLASH_SR & FLASH_SR_BUSY && timeout-- > 0)
;
return (timeout > 0) ? EC_SUCCESS : EC_ERROR_TIMEOUT;
}
static int unlock(int locks)
{
/*
* 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 CR if needed */
if (STM32_FLASH_CR & FLASH_CR_LOCK) {
STM32_FLASH_KEYR = FLASH_KEYR_KEY1;
STM32_FLASH_KEYR = FLASH_KEYR_KEY2;
}
/* unlock option memory if required */
if ((locks & FLASH_CR_OPTLOCK) &&
(STM32_FLASH_CR & FLASH_CR_OPTLOCK)) {
STM32_FLASH_OPTKEYR = FLASH_OPTKEYR_KEY1;
STM32_FLASH_OPTKEYR = FLASH_OPTKEYR_KEY2;
}
/* Re-enable bus fault handler */
ignore_bus_fault(0);
return (STM32_FLASH_CR & (locks | FLASH_CR_LOCK)) ? EC_ERROR_UNKNOWN
: EC_SUCCESS;
}
static void lock(void)
{
STM32_FLASH_CR = FLASH_CR_LOCK;
}
/*
* Option byte organization
*
* [63:56][55:48][47:40][39:32] [31:24][23:16][15: 8][ 7: 0]
* +--------------+-------------------+------+ +-------------------+------+
* | 0x1FFF7800 | nUSER | nRDP | | USER | RDP |
* +--------------+------------+------+------+ +------------+------+------+
* | 0x1FFF7808 | | nPCROP1_STRT| | | PCROP1_STRT |
* +--------------+------------+-------------+ +------------+-------------+
* | 0x1FFF7810 | | nPCROP1_END | | | PCROP1_END |
* +--------------+------------+-------------+ +------------+-------------+
* | 0x1FFF7818 | |nWRP1A| |nWRP1A| | | WRP1A| | WRP1A|
* | | |_END | |_STRT | | | _END | | _STRT|
* +--------------+------------+-------------+ +------------+-------------+
* | 0x1FFF7820 | |nWRP1B| |nWRP1B| | | WRP1B| | WRP1B|
* | | |_END | |_STRT | | | _END | | _STRT|
* +--------------+------------+-------------+ +------------+-------------+
*
* Note that the variable with n prefix means the complement.
*/
static int unlock_optb(void)
{
int rv;
rv = wait_while_busy();
if (rv)
return rv;
rv = unlock(FLASH_CR_OPTLOCK);
if (rv)
return rv;
return EC_SUCCESS;
}
static int commit_optb(void)
{
int rv;
STM32_FLASH_CR |= FLASH_CR_OPTSTRT;
rv = wait_while_busy();
if (rv)
return rv;
lock();
return EC_SUCCESS;
}
static void unprotect_all_blocks(void)
{
unlock_optb();
STM32_FLASH_WRP1AR = FLASH_WRP_RANGE_DISABLED;
STM32_FLASH_WRP1BR = FLASH_WRP_RANGE_DISABLED;
commit_optb();
}
int flash_physical_protect_at_boot(uint32_t new_flags)
{
uint32_t ro_range = FLASH_WRP_RANGE_DISABLED;
uint32_t rb_rw_range = FLASH_WRP_RANGE_DISABLED;
/*
* WRP1AR is storing the write-protection range for the RO region.
* WRP1BR is storing the write-protection range for the
* rollback and RW regions.
*/
if (new_flags & (EC_FLASH_PROTECT_ALL_AT_BOOT |
EC_FLASH_PROTECT_RO_AT_BOOT))
ro_range = FLASH_WRP_RANGE(WP_BANK_OFFSET,
WP_BANK_OFFSET + WP_BANK_COUNT);
if (new_flags & EC_FLASH_PROTECT_ALL_AT_BOOT) {
rb_rw_range = FLASH_WRP_RANGE(WP_BANK_OFFSET + WP_BANK_COUNT,
PHYSICAL_BANKS);
} else {
uint8_t strt = WP_BANK_OFFSET + WP_BANK_COUNT;
uint8_t end = FLASH_WRP_END(FLASH_WRP_RANGE_DISABLED);
#ifdef CONFIG_ROLLBACK
if (new_flags & EC_FLASH_PROTECT_ROLLBACK_AT_BOOT) {
strt = ROLLBACK_BANK_OFFSET;
end = ROLLBACK_BANK_OFFSET + ROLLBACK_BANK_COUNT;
} else {
strt = ROLLBACK_BANK_OFFSET + ROLLBACK_BANK_COUNT;
}
#endif /* !CONFIG_ROLLBACK */
#ifdef CONFIG_FLASH_PROTECT_RW
if (new_flags & EC_FLASH_PROTECT_RW_AT_BOOT)
end = PHYSICAL_BANKS;
#endif /* CONFIG_FLASH_PROTECT_RW */
if (end != FLASH_WRP_END(FLASH_WRP_RANGE_DISABLED))
rb_rw_range = FLASH_WRP_RANGE(strt, end);
}
unlock_optb();
#ifdef CONFIG_WP_ALWAYS
/*
* Set a permanent protection by increasing RDP to level 1,
* trying to unprotected the flash will trigger a full erase.
*/
STM32_FLASH_OPTR = (STM32_FLASH_OPTR & ~0xff) | 0x11;
#endif
STM32_FLASH_WRP1AR = ro_range;
STM32_FLASH_WRP1BR = rb_rw_range;
commit_optb();
return EC_SUCCESS;
}
/**
* Check if write protect register state is inconsistent with RO_AT_BOOT and
* ALL_AT_BOOT state.
*
* @return zero if consistent, non-zero if inconsistent.
*/
static int registers_need_reset(void)
{
uint32_t flags = flash_get_protect();
int ro_at_boot = (flags & EC_FLASH_PROTECT_RO_AT_BOOT) ? 1 : 0;
/*
* The RO region is write-protected by the WRP1AR range,
* it starts at page WP_BANK_OFFSET for WP_BANK_COUNT pages.
*/
uint32_t wrp1ar = STM32_OPTB_WRP1AR;
uint32_t ro_range = ro_at_boot ?
FLASH_WRP_RANGE(WP_BANK_OFFSET, WP_BANK_OFFSET + WP_BANK_COUNT)
: FLASH_WRP_RANGE_DISABLED;
return ro_range != (wrp1ar & FLASH_WRP_MASK);
}
/*****************************************************************************/
/* Physical layer APIs */
int flash_physical_write(int offset, int size, const char *data)
{
uint32_t *address = (void *)(CONFIG_PROGRAM_MEMORY_BASE + offset);
int res = EC_SUCCESS;
int timeout = calculate_flash_timeout();
int i;
int unaligned = (uint32_t)data & (CONFIG_FLASH_WRITE_SIZE - 1);
uint32_t *data32 = (void *)data;
if (unlock(FLASH_CR_LOCK) != EC_SUCCESS)
return EC_ERROR_UNKNOWN;
/* Clear previous error status */
STM32_FLASH_SR = FLASH_SR_ERR_MASK;
/* set PG bit */
STM32_FLASH_CR |= 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();
/* wait to be ready */
for (i = 0; (STM32_FLASH_SR & FLASH_SR_BUSY) && (i < timeout);
i++)
;
if (STM32_FLASH_SR & FLASH_SR_BUSY) {
res = EC_ERROR_TIMEOUT;
goto exit_wr;
}
/* write the 2 words */
if (unaligned) {
*address++ = (uint32_t)data[0] | (data[1] << 8)
| (data[2] << 16) | (data[3] << 24);
*address++ = (uint32_t)data[4] | (data[5] << 8)
| (data[6] << 16) | (data[7] << 24);
data += CONFIG_FLASH_WRITE_SIZE;
} else {
*address++ = *data32++;
*address++ = *data32++;
}
/* Wait for writes to complete */
for (i = 0; (STM32_FLASH_SR & FLASH_SR_BUSY) && (i < timeout);
i++)
;
if (STM32_FLASH_SR & FLASH_SR_BUSY) {
res = EC_ERROR_TIMEOUT;
goto exit_wr;
}
/*
* Check for error conditions - erase failed, voltage error,
* protection error.
*/
if (STM32_FLASH_SR & FLASH_SR_ERR_MASK) {
res = EC_ERROR_UNKNOWN;
goto exit_wr;
}
}
exit_wr:
/* Disable PG bit */
STM32_FLASH_CR &= ~FLASH_CR_PG;
lock();
return res;
}
int flash_physical_erase(int offset, int size)
{
int res = EC_SUCCESS;
int pg;
int last;
if (unlock(FLASH_CR_LOCK) != EC_SUCCESS)
return EC_ERROR_UNKNOWN;
/* Clear previous error status */
STM32_FLASH_SR = FLASH_SR_ERR_MASK;
last = (offset + size) / CONFIG_FLASH_ERASE_SIZE;
for (pg = offset / CONFIG_FLASH_ERASE_SIZE; pg < last; pg++) {
timestamp_t deadline;
/* select page to erase and PER bit */
STM32_FLASH_CR = (STM32_FLASH_CR & ~FLASH_CR_PNB_MASK)
| FLASH_CR_PER | FLASH_CR_PNB(pg);
/* set STRT bit : start erase */
STM32_FLASH_CR |= FLASH_CR_STRT;
/*
* Reload the watchdog timer to avoid watchdog reset during a
* long erase operation.
*/
watchdog_reload();
deadline.val = get_time().val + FLASH_TIMEOUT_US;
/* Wait for erase to complete */
while ((STM32_FLASH_SR & FLASH_SR_BUSY) &&
(get_time().val < deadline.val)) {
usleep(300);
}
if (STM32_FLASH_SR & FLASH_SR_BUSY) {
res = EC_ERROR_TIMEOUT;
goto exit_er;
}
/*
* Check for error conditions - erase failed, voltage error,
* protection error
*/
if (STM32_FLASH_SR & FLASH_SR_ERR_MASK) {
res = EC_ERROR_UNKNOWN;
goto exit_er;
}
}
exit_er:
/* reset PER bit */
STM32_FLASH_CR &= ~(FLASH_CR_PER | FLASH_CR_PNB_MASK);
lock();
return res;
}
int flash_physical_get_protect(int block)
{
uint32_t wrp1ar = STM32_FLASH_WRP1AR;
uint32_t wrp1br = STM32_FLASH_WRP1BR;
return ((block >= FLASH_WRP_START(wrp1ar)) &&
(block < FLASH_WRP_END(wrp1ar))) ||
((block >= FLASH_WRP_START(wrp1br)) &&
(block < FLASH_WRP_END(wrp1br)));
}
/*
* Note: This does not need to update _NOW flags, as get_protect_flags
* in common code already does so.
*/
uint32_t flash_physical_get_protect_flags(void)
{
uint32_t flags = 0;
uint32_t wrp1ar = STM32_OPTB_WRP1AR;
uint32_t wrp1br = STM32_OPTB_WRP1BR;
/* RO region protection range is in WRP1AR range */
if (wrp1ar == FLASH_WRP_RANGE(WP_BANK_OFFSET,
WP_BANK_OFFSET + WP_BANK_COUNT))
flags |= EC_FLASH_PROTECT_RO_AT_BOOT;
/* Rollback and RW regions protection range is in WRP1BR range */
if (wrp1br != FLASH_WRP_RANGE_DISABLED) {
int end = FLASH_WRP_END(wrp1br);
int strt = FLASH_WRP_START(wrp1br);
#ifdef CONFIG_ROLLBACK
if (strt <= ROLLBACK_BANK_OFFSET &&
end >= ROLLBACK_BANK_OFFSET + ROLLBACK_BANK_COUNT)
flags |= EC_FLASH_PROTECT_ROLLBACK_AT_BOOT;
#endif /* CONFIG_ROLLBACK */
#ifdef CONFIG_FLASH_PROTECT_RW
if (end == PHYSICAL_BANKS)
flags |= EC_FLASH_PROTECT_RW_AT_BOOT;
#endif /* CONFIG_FLASH_PROTECT_RW */
if (end == PHYSICAL_BANKS &&
strt == WP_BANK_OFFSET + WP_BANK_COUNT &&
flags & EC_FLASH_PROTECT_RO_AT_BOOT)
flags |= EC_FLASH_PROTECT_ALL_AT_BOOT;
}
return flags;
}
int flash_physical_protect_now(int all)
{
return EC_ERROR_INVAL;
}
uint32_t flash_physical_get_valid_flags(void)
{
return EC_FLASH_PROTECT_RO_AT_BOOT |
EC_FLASH_PROTECT_RO_NOW |
#ifdef CONFIG_FLASH_PROTECT_RW
EC_FLASH_PROTECT_RW_AT_BOOT |
EC_FLASH_PROTECT_RW_NOW |
#endif
#ifdef CONFIG_ROLLBACK
EC_FLASH_PROTECT_ROLLBACK_AT_BOOT |
EC_FLASH_PROTECT_ROLLBACK_NOW |
#endif
EC_FLASH_PROTECT_ALL_AT_BOOT |
EC_FLASH_PROTECT_ALL_NOW;
}
uint32_t 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;
/*
* ALL/RW at-boot state can be set if WP GPIO is asserted and can always
* be cleared.
*/
if (cur_flags & (EC_FLASH_PROTECT_ALL_AT_BOOT |
EC_FLASH_PROTECT_GPIO_ASSERTED))
ret |= EC_FLASH_PROTECT_ALL_AT_BOOT;
#ifdef CONFIG_FLASH_PROTECT_RW
if (cur_flags & (EC_FLASH_PROTECT_RW_AT_BOOT |
EC_FLASH_PROTECT_GPIO_ASSERTED))
ret |= EC_FLASH_PROTECT_RW_AT_BOOT;
#endif
#ifdef CONFIG_ROLLBACK
if (cur_flags & (EC_FLASH_PROTECT_ROLLBACK_AT_BOOT |
EC_FLASH_PROTECT_GPIO_ASSERTED))
ret |= EC_FLASH_PROTECT_ROLLBACK_AT_BOOT;
#endif
return ret;
}
int flash_pre_init(void)
{
uint32_t reset_flags = system_get_reset_flags();
uint32_t prot_flags = flash_get_protect();
int need_reset = 0;
/*
* If we have already jumped between images, an earlier image could
* have applied write protection. Nothing additional needs to be done.
*/
if (reset_flags & RESET_FLAG_SYSJUMP)
return EC_SUCCESS;
if (prot_flags & EC_FLASH_PROTECT_GPIO_ASSERTED) {
if ((prot_flags & EC_FLASH_PROTECT_RO_AT_BOOT) &&
!(prot_flags & EC_FLASH_PROTECT_RO_NOW)) {
/*
* Pstate wants RO protected at boot, but the write
* protect register wasn't set to protect it. Force an
* update to the write protect register and reboot so
* it takes effect.
*/
flash_physical_protect_at_boot(
EC_FLASH_PROTECT_RO_AT_BOOT);
need_reset = 1;
}
if (registers_need_reset()) {
/*
* Write protect register was in an inconsistent state.
* Set it back to a good state and reboot.
*
* TODO(crosbug.com/p/23798): this seems really similar
* to the check above. One of them should be able to
* go away.
*/
flash_protect_at_boot(
prot_flags & EC_FLASH_PROTECT_RO_AT_BOOT);
need_reset = 1;
}
} else {
if (prot_flags & EC_FLASH_PROTECT_RO_NOW) {
/*
* Write protect pin unasserted but some section is
* protected. Drop it and reboot.
*/
unprotect_all_blocks();
need_reset = 1;
}
}
if ((flash_physical_get_valid_flags() & EC_FLASH_PROTECT_ALL_AT_BOOT) &&
(!!(prot_flags & EC_FLASH_PROTECT_ALL_AT_BOOT) !=
!!(prot_flags & EC_FLASH_PROTECT_ALL_NOW))) {
/*
* ALL_AT_BOOT and ALL_NOW should be both set or both unset
* at boot. If they are not, it must be that the chip requires
* OBL_LAUNCH to be set to reload option bytes. Let's reset
* the system with OBL_LAUNCH set.
* This assumes OBL_LAUNCH is used for hard reset in
* chip/stm32/system.c.
*/
need_reset = 1;
}
#ifdef CONFIG_FLASH_PROTECT_RW
if ((flash_physical_get_valid_flags() & EC_FLASH_PROTECT_RW_AT_BOOT) &&
(!!(prot_flags & EC_FLASH_PROTECT_RW_AT_BOOT) !=
!!(prot_flags & EC_FLASH_PROTECT_RW_NOW))) {
/* RW_AT_BOOT and RW_NOW do not match. */
need_reset = 1;
}
#endif
#ifdef CONFIG_ROLLBACK
if ((flash_physical_get_valid_flags() &
EC_FLASH_PROTECT_ROLLBACK_AT_BOOT) &&
(!!(prot_flags & EC_FLASH_PROTECT_ROLLBACK_AT_BOOT) !=
!!(prot_flags & EC_FLASH_PROTECT_ROLLBACK_NOW))) {
/* ROLLBACK_AT_BOOT and ROLLBACK_NOW do not match. */
need_reset = 1;
}
#endif
if (need_reset)
system_reset(SYSTEM_RESET_HARD | SYSTEM_RESET_PRESERVE_FLAGS);
return EC_SUCCESS;
}