summaryrefslogtreecommitdiff
path: root/chip/stm32
diff options
context:
space:
mode:
authorCraig Hesling <hesling@chromium.org>2020-01-08 15:41:16 -0800
committerCommit Bot <commit-bot@chromium.org>2020-02-06 06:57:39 +0000
commit127975b6c3efe77b6e92af6b153504c2607d5a07 (patch)
treeeefaa0d3c947c27ff926c9021eb5edb0b955da08 /chip/stm32
parent50d7dd999f709669fe965cda68b753c3ee4602e2 (diff)
downloadchrome-ec-127975b6c3efe77b6e92af6b153504c2607d5a07.tar.gz
nocturne_fp: Add fix for legacy reset loop
This patch mitigates an infinite reset loop caused by an RO bug. The reset occurs in RO when hardware write protect (wp_gpio_asserted) is disabled, but software write protect (ro_now) is still enabled. This can be seen by disabling hardware write protect and issuing a soft reset. There is one case where RO will forgo issuing this system reset. That is when it detects a power on reset. Furthermore, it retrieves its reset flags from the main system_get_reset_flags function, which combines hardware reset registers AND a special RTC backup register designed to preserve reset flags. We exploit this reset backup register mechanism to inject a fake power-on flag before resetting. As an added bonus, we also inject an ap-off flag so that we can determine on startup if the power-on flag is real or forged by this mechanism. If we detect that the power-on flag was forged, we print a warning and fix the current reset flags. In order to ensure that a power-on will be forged when a spurious reset happens (exception or pin reset), we keep the backup register loaded with the power-on and ap-off reset flags, when the hardware write protect is disabled. In order to keep the typical code path (HW+SW WP enabled) clear of complexity and false power-on reports, we only forge the power-on flag when hardware write protect is disabled. Thus, we conditionally setup the forge on startup and setup an interrupt handler to catch changes to the hardware write protect status. It is safe to use ap-off flag for our nefarious purposes, since the fingerprint controller has no functionality to control an AP and has no included code that uses this reset flag. Review: * Normal power on reset --> The ap-off flag should be cleared * Forged power on reset --> We set the ap-off flag Scenarios covered: * True power on --> No reset loop and ap-off would not be set * HW reset pulse --> We preloaded ap-off and power-on flags in the reset backup register * Exception/Watchdog --> Same as above * System reboot --> We modified the system_reset function to add ap-off and power-on to reset backup register BRANCH=nocturne,hatch BUG=b:146428434 TEST=make buildall -j TEST=Checked all of the scenarios mentioned above in the [SW-WP off + HW-WP off], [SW-WP on + HW-WP on], and [SW-WP on + HW-WP off] situations using the nucleo-h743zi board (https://crrev.com/c/1994624). TEST=Checked all of the previous using nocturne_fp board on nucleo-h743zi TEST=Checked stable RO+fixed-RW on Kohaku Change-Id: I89361fa95be8eafe78c80c30f5b3195d7a724f81 Signed-off-by: Craig Hesling <hesling@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/1992740 Reviewed-by: Tom Hughes <tomhughes@chromium.org>
Diffstat (limited to 'chip/stm32')
-rw-r--r--chip/stm32/bkpdata.c24
-rw-r--r--chip/stm32/bkpdata.h2
-rw-r--r--chip/stm32/system.c27
3 files changed, 37 insertions, 16 deletions
diff --git a/chip/stm32/bkpdata.c b/chip/stm32/bkpdata.c
index 51ca4f4a6e..2b26ace7ea 100644
--- a/chip/stm32/bkpdata.c
+++ b/chip/stm32/bkpdata.c
@@ -3,6 +3,8 @@
* found in the LICENSE file.
*/
+#include <assert.h>
+
#include "bkpdata.h"
#include "registers.h"
#include "system.h" /* enum system_bbram_idx */
@@ -67,3 +69,25 @@ int bkpdata_index_lookup(enum system_bbram_idx idx, int *msb)
#endif
return -1;
}
+
+uint32_t bkpdata_read_reset_flags()
+{
+ uint32_t flags = bkpdata_read(BKPDATA_INDEX_SAVED_RESET_FLAGS);
+#ifdef CONFIG_STM32_RESET_FLAGS_EXTENDED
+ flags |= bkpdata_read(BKPDATA_INDEX_SAVED_RESET_FLAGS_2) << 16;
+#endif
+ return flags;
+}
+
+__overridable
+void bkpdata_write_reset_flags(uint32_t save_flags)
+{
+#ifdef CONFIG_STM32_RESET_FLAGS_EXTENDED
+ bkpdata_write(BKPDATA_INDEX_SAVED_RESET_FLAGS, save_flags & 0xffff);
+ bkpdata_write(BKPDATA_INDEX_SAVED_RESET_FLAGS_2, save_flags >> 16);
+#else
+ /* Reset flags are 32-bits, but BBRAM entry is only 16 bits. */
+ ASSERT(!(save_flags >> 16));
+ bkpdata_write(BKPDATA_INDEX_SAVED_RESET_FLAGS, save_flags);
+#endif
+}
diff --git a/chip/stm32/bkpdata.h b/chip/stm32/bkpdata.h
index 09a00e6377..07f20aed49 100644
--- a/chip/stm32/bkpdata.h
+++ b/chip/stm32/bkpdata.h
@@ -74,5 +74,7 @@ uint16_t bkpdata_read(enum bkpdata_index index);
int bkpdata_write(enum bkpdata_index index, uint16_t value);
int bkpdata_index_lookup(enum system_bbram_idx idx, int *msb);
+uint32_t bkpdata_read_reset_flags(void);
+void bkpdata_write_reset_flags(uint32_t save_flags);
#endif /* __CROS_EC_BKPDATA_H */
diff --git a/chip/stm32/system.c b/chip/stm32/system.c
index 31434bd49c..559de04197 100644
--- a/chip/stm32/system.c
+++ b/chip/stm32/system.c
@@ -74,23 +74,16 @@ void system_hibernate(uint32_t seconds, uint32_t microseconds)
static void check_reset_cause(void)
{
- uint32_t flags = bkpdata_read(BKPDATA_INDEX_SAVED_RESET_FLAGS);
+ uint32_t flags = bkpdata_read_reset_flags();
uint32_t raw_cause = STM32_RCC_RESET_CAUSE;
uint32_t pwr_status = STM32_PWR_RESET_CAUSE;
-#ifdef CONFIG_STM32_RESET_FLAGS_EXTENDED
- flags |= bkpdata_read(BKPDATA_INDEX_SAVED_RESET_FLAGS_2) << 16;
-#endif
-
/* Clear the hardware reset cause by setting the RMVF bit */
STM32_RCC_RESET_CAUSE |= RESET_CAUSE_RMVF;
/* Clear SBF in PWR_CSR */
STM32_PWR_RESET_CAUSE_CLR |= RESET_CAUSE_SBF_CLR;
/* Clear saved reset flags */
- bkpdata_write(BKPDATA_INDEX_SAVED_RESET_FLAGS, 0);
-#ifdef CONFIG_STM32_RESET_FLAGS_EXTENDED
- bkpdata_write(BKPDATA_INDEX_SAVED_RESET_FLAGS_2, 0);
-#endif
+ bkpdata_write_reset_flags(0);
if (raw_cause & RESET_CAUSE_WDG) {
/*
@@ -308,6 +301,13 @@ void system_reset(int flags)
/* Disable interrupts to avoid task swaps during reboot */
interrupt_disable();
+ /*
+ * TODO(crbug.com/1045283): Change this part of code to use
+ * system_encode_save_flags, like all other system_reset functions.
+ *
+ * system_encode_save_flags(flags, &save_flags);
+ */
+
/* Save current reset reasons if necessary */
if (flags & SYSTEM_RESET_PRESERVE_FLAGS)
save_flags = system_get_reset_flags() | EC_RESET_FLAG_PRESERVED;
@@ -322,15 +322,10 @@ void system_reset(int flags)
#ifdef CONFIG_STM32_RESET_FLAGS_EXTENDED
if (flags & SYSTEM_RESET_AP_WATCHDOG)
save_flags |= EC_RESET_FLAG_AP_WATCHDOG;
-
- bkpdata_write(BKPDATA_INDEX_SAVED_RESET_FLAGS, save_flags & 0xffff);
- bkpdata_write(BKPDATA_INDEX_SAVED_RESET_FLAGS_2, save_flags >> 16);
-#else
- /* Reset flags are 32-bits, but BBRAM entry is only 16 bits. */
- ASSERT(!(save_flags >> 16));
- bkpdata_write(BKPDATA_INDEX_SAVED_RESET_FLAGS, save_flags);
#endif
+ bkpdata_write_reset_flags(save_flags);
+
if (flags & SYSTEM_RESET_HARD) {
#ifdef CONFIG_SOFTWARE_PANIC
uint32_t reason, info;