From 3647d1b99649c935e300d8377230be3bc866c019 Mon Sep 17 00:00:00 2001 From: David Crocker Date: Mon, 21 Dec 2020 16:17:10 +0000 Subject: [PATCH] Allow for downgrading from future firmware versions with TCM enabled --- cores/arduino/Flash.cpp | 19 +++++++++++---- cores/arduino/Flash.h | 9 +++---- variants/same70/startup_same70.c | 41 +++++++++++++++++++++++++++----- 3 files changed, 55 insertions(+), 14 deletions(-) diff --git a/cores/arduino/Flash.cpp b/cores/arduino/Flash.cpp index 34e59429..aa95a005 100644 --- a/cores/arduino/Flash.cpp +++ b/cores/arduino/Flash.cpp @@ -812,6 +812,17 @@ uint32_t flash_clear_gpnvm(uint32_t ul_gpnvm) return FLASH_RC_ERROR; } +// Read all nine GPNVM bits. Return 0xFFFFFFFF if failed, else the GPNVM bits in bits 0 to 8. +uint32_t flash_read_gpnvm_bits() noexcept +{ + if (EFC_RC_OK != efc_perform_command(EFC, EFC_FCMD_GGPB, 0)) + { + return 0xFFFFFFFF; + } + + return efc_get_result(EFC) & ((1ul << 9) - 1ul); +} + /** * \brief Check if the given GPNVM bit is set or not. * @@ -848,7 +859,7 @@ uint32_t flash_is_gpnvm_set(uint32_t ul_gpnvm) * * \return 0 if successful; otherwise returns an error code. */ -uint32_t flash_read_unique_id(uint32_t *pul_data) +uint32_t flash_read_unique_id(uint32_t *pul_data) noexcept { // dc42 Bug fix: must disable interrupts while executing the EFC read command const irqflags_t flags = cpu_irq_save(); @@ -866,7 +877,7 @@ uint32_t flash_read_unique_id(uint32_t *pul_data) * * \return 0 if successful; otherwise returns an error code. */ -uint32_t flash_read_user_signature(uint32_t *p_data, uint32_t ul_size) +uint32_t flash_read_user_signature(uint32_t *p_data, uint32_t ul_size) noexcept { if (ul_size > (FLASH_USER_SIG_SIZE / sizeof(uint32_t))) { /* Only 512 byte to store user signature */ @@ -888,7 +899,7 @@ uint32_t flash_read_user_signature(uint32_t *p_data, uint32_t ul_size) * * \return 0 if successful; otherwise returns an error code. */ -uint32_t flash_write_user_signature(const uint32_t *p_buffer) +uint32_t flash_write_user_signature(const uint32_t *p_buffer) noexcept { /* Write page buffer. * Writing 8-bit and 16-bit data is not allowed and may lead to @@ -909,7 +920,7 @@ uint32_t flash_write_user_signature(const uint32_t *p_buffer) * * \return 0 if successful; otherwise returns an error code. */ -uint32_t flash_erase_user_signature(void) +uint32_t flash_erase_user_signature(void) noexcept { /* Perform the erase user signature command */ return efc_perform_command(EFC, EFC_FCMD_EUS, 0); diff --git a/cores/arduino/Flash.h b/cores/arduino/Flash.h index c3319d5e..3e221cf3 100644 --- a/cores/arduino/Flash.h +++ b/cores/arduino/Flash.h @@ -114,12 +114,13 @@ uint32_t flash_is_locked(uint32_t ul_start, uint32_t ul_end); uint32_t flash_set_gpnvm(uint32_t ul_gpnvm); uint32_t flash_clear_gpnvm(uint32_t ul_gpnvm); uint32_t flash_is_gpnvm_set(uint32_t ul_gpnvm); -uint32_t flash_read_unique_id(uint32_t *pul_data); // read 4 dwords of unique ID +uint32_t flash_read_gpnvm_bits() noexcept; +uint32_t flash_read_unique_id(uint32_t *pul_data) noexcept; // read 4 dwords of unique ID #if (SAM4S || SAM4E || SAM4N || SAM4C || SAMG || SAM4CP || SAM4CM || SAMV71 || SAMV70 || SAMS70 || SAME70) -uint32_t flash_read_user_signature(uint32_t *p_data, uint32_t ul_size); -uint32_t flash_write_user_signature(const uint32_t *p_buffer); -uint32_t flash_erase_user_signature(void); +uint32_t flash_read_user_signature(uint32_t *p_data, uint32_t ul_size) noexcept; +uint32_t flash_write_user_signature(const uint32_t *p_buffer) noexcept; +uint32_t flash_erase_user_signature(void) noexcept; #endif /// @cond 0 diff --git a/variants/same70/startup_same70.c b/variants/same70/startup_same70.c index 4a572197..149ae036 100644 --- a/variants/same70/startup_same70.c +++ b/variants/same70/startup_same70.c @@ -47,6 +47,8 @@ #include "same70.h" #include "system_same70.h" #include "wdt/wdt.h" +#include "Flash.h" +#include "Reset.h" #if __FPU_USED /* CMSIS defined value to indicate usage of FPU */ #include "fpu/fpu.h" @@ -317,19 +319,22 @@ const DeviceVectors exception_table = { .pfnRSWDT_Handler = (void*) RSWDT_Handler /* 63 Reinforced Secure Watchdog Timer */ }; +// This must be marked noinline so that R0 is loaded with the required value for SP +__attribute((noinline)) void SetStackPointer(uint32_t *topOfStack) +{ + __asm volatile("msr msp, r0"); +} + /** * \brief This is the code that gets called on processor reset. * To initialize the device, and call the main() routine. */ __attribute__((noreturn)) void Reset_Handler(void) { - // Clear the nocache RAM segment - for (uint32_t *pDest = &_szero_nocache; pDest < &_ezero_nocache;) - { - *pDest++ = 0; - } + // If TCM is allocated then SP may point beyond the end of RAM, so move it + SetStackPointer(&_ezero_nocache); - /* Initialize the relocate segment */ + // Initialize the relocate segment { const uint32_t *pSrc = &_etext; uint32_t *pDest = &_srelocate; @@ -343,6 +348,30 @@ __attribute__((noreturn)) void Reset_Handler(void) } } + // Check that no TCM is allocated before we relocate the stack to the top of memory. This uses a RAMFUNC, so we must initialise the relocate segment before here. + // The temporary stack we are on is in the non-cached memory segment, so don't clear that segment until after we have relocated the stack. + if (flash_read_gpnvm_bits() & ((1ul << 7) | (1ul << 8))) + { + // TCM is allocated - we are probably downgrading from later firmware that uses TCM. Disable TCM and reboot. + flash_clear_gpnvm(7); + flash_clear_gpnvm(8); + // On some microchip processors, we need a delay between writing to flash and resetting + for (unsigned int i = 0; i < 10000; ++i) + { + __asm volatile("nop"); + } + Reset(); + } + + // Now it's safe to reset the stack pointer to the top of memory + SetStackPointer(&_estack); + + // Clear the nocache RAM segment + for (uint32_t *pDest = &_szero_nocache; pDest < &_ezero_nocache;) + { + *pDest++ = 0; + } + /* Clear the zero segment */ for (uint32_t *pDest = &_szero; pDest < &_ezero;) {