From f002c784c0af2272b1fdd5430379bc7775d89743 Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Mon, 18 May 2020 11:43:21 +0800 Subject: nrf: linker: add a new .uninitialized section This section immediately follows the .bss section, and is designed to contain uninitialized variables that should persist across reboots. The section is placed directly after .bss, under the theory that the size of Circuit Python's .bss + .data is bigger than the bootloader's .bss + .data, so there is less likely to be a conflict. Signed-off-by: Sean Cross --- ports/nrf/boards/common.template.ld | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/ports/nrf/boards/common.template.ld b/ports/nrf/boards/common.template.ld index a2bbf0ec8..9a7df69a4 100644 --- a/ports/nrf/boards/common.template.ld +++ b/ports/nrf/boards/common.template.ld @@ -103,7 +103,7 @@ SECTIONS _edata = .; /* define a global symbol at data end; used by startup code in order to initialise the .data section in RAM */ } >RAM - /* Uninitialized data section */ + /* Zero-initialized data section */ .bss : { . = ALIGN(4); @@ -116,6 +116,19 @@ SECTIONS _ebss = .; /* define a global symbol at bss end; used by startup code and GC */ } >RAM + /* Uninitialized data section + Data placed into this section will remain unchanged across reboots. */ + .uninitialized (NOLOAD) : + { + . = ALIGN(4); + _suninitialized = .; /* define a global symbol at uninitialized start; currently unused */ + *(.uninitialized) + *(.uninitialized*) + + . = ALIGN(4); + _euninitialized = .; /* define a global symbol at uninitialized end; currently unused */ + } >RAM + /* this is to define the start of the heap, and make sure we have a minimum size */ .heap : { -- cgit v1.2.3 From 192bb155fafb78b2f6073fdbfbbf41d8c44dc12e Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Mon, 18 May 2020 11:45:35 +0800 Subject: nrf: port: move the heap after .uninitialized Previously, it was placed following .bss. However, now that there is a new section after .bss, the heap must be moved forward. Signed-off-by: Sean Cross --- ports/nrf/supervisor/port.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ports/nrf/supervisor/port.c b/ports/nrf/supervisor/port.c index 539a9cf90..a0c7be62b 100644 --- a/ports/nrf/supervisor/port.c +++ b/ports/nrf/supervisor/port.c @@ -177,8 +177,13 @@ void reset_cpu(void) { NVIC_SystemReset(); } +// The uninitialized data section is placed directly after BSS, under the theory +// that Circuit Python has a lot more .data and .bss than the bootloader. As a +// result, this section is less likely to be tampered with by the bootloader. +extern uint32_t _euninitialized; + uint32_t *port_heap_get_bottom(void) { - return port_stack_get_limit(); + return &_euninitialized; } uint32_t *port_heap_get_top(void) { @@ -186,14 +191,13 @@ uint32_t *port_heap_get_top(void) { } uint32_t *port_stack_get_limit(void) { - return &_ebss; + return &_euninitialized; } uint32_t *port_stack_get_top(void) { return &_estack; } -extern uint32_t _ebss; // Place the word to save just after our BSS section that gets blanked. void port_set_saved_word(uint32_t value) { _ebss = value; -- cgit v1.2.3 From 3b5f5ddaa6232a8d3ac868c703e8a511abe045e0 Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Mon, 18 May 2020 11:46:38 +0800 Subject: nrf: port: move saved word into .uninitialized section Circuit Python supports saving a single word of data across reboots. Previously, this data was placed immediately following the .bss. However, this appeared to not work, as Circuit Python zeroes out the heap when it starts up, and the heap begins immediately after the .bss. Switch to using the new .uninitialized section in order to store this word across resets. Signed-off-by: Sean Cross --- ports/nrf/supervisor/port.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ports/nrf/supervisor/port.c b/ports/nrf/supervisor/port.c index a0c7be62b..3a76fa878 100644 --- a/ports/nrf/supervisor/port.c +++ b/ports/nrf/supervisor/port.c @@ -198,13 +198,14 @@ uint32_t *port_stack_get_top(void) { return &_estack; } -// Place the word to save just after our BSS section that gets blanked. +// Place the word in the uninitialized section so it won't get overwritten. +__attribute__((section(".uninitialized"))) uint32_t _saved_word; void port_set_saved_word(uint32_t value) { - _ebss = value; + _saved_word = value; } uint32_t port_get_saved_word(void) { - return _ebss; + return _saved_word; } uint64_t port_get_raw_ticks(uint8_t* subticks) { -- cgit v1.2.3 From 83680c41db821672ff006b96151c6daeca6280e3 Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Mon, 18 May 2020 11:48:23 +0800 Subject: nrf: rtc: persist rtc offset across reboots Store the RTC value in the .uninitialized section, but make sure to flank it with some known values. That way we can determine if the RTC value has been initialized, or if it's random uninitialized garbage. As part of this, add a `common_hal_rtc_init()` routine to determine if the value is correct, or reset it to 0 if it is not valid. Signed-off-by: Sean Cross --- ports/nrf/common-hal/rtc/RTC.c | 29 ++++++++++++++++++++++++++--- ports/nrf/common-hal/rtc/RTC.h | 1 + 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/ports/nrf/common-hal/rtc/RTC.c b/ports/nrf/common-hal/rtc/RTC.c index 9f56f2f0d..06345f589 100644 --- a/ports/nrf/common-hal/rtc/RTC.c +++ b/ports/nrf/common-hal/rtc/RTC.c @@ -34,11 +34,28 @@ #include "supervisor/shared/translate.h" // This is the time in seconds since 2000 that the RTC was started. -static uint32_t rtc_offset = 0; +__attribute__((section(".uninitialized"))) static uint32_t rtc_offset[3]; + +// These values are placed before and after the current RTC count. They are +// used to determine if the RTC count is valid. These randomly-generated values +// will be set when the RTC value is set in order to mark the RTC as valid. If +// the system crashes or reboots, these values will remain undisturbed and the +// RTC offset will remain valid. +// +// If Circuit Python is updated or these symbols shift around, the prefix and +// suffix will no longer match, and the time will no longer be valid. +#define RTC_OFFSET_CHECK_PREFIX 0x25ea7e2a +#define RTC_OFFSET_CHECK_SUFFIX 0x2b80b69e + +void common_hal_rtc_init(void) { + // If the prefix and suffix are not valid, zero-initialize the RTC offset. + if ((rtc_offset[0] != RTC_OFFSET_CHECK_PREFIX) || (rtc_offset[2] != RTC_OFFSET_CHECK_SUFFIX)) + rtc_offset[1] = 0; +} void common_hal_rtc_get_time(timeutils_struct_time_t *tm) { uint64_t ticks_s = port_get_raw_ticks(NULL) / 1024; - timeutils_seconds_since_2000_to_struct_time(rtc_offset + ticks_s, tm); + timeutils_seconds_since_2000_to_struct_time(rtc_offset[1] + ticks_s, tm); } void common_hal_rtc_set_time(timeutils_struct_time_t *tm) { @@ -46,7 +63,13 @@ void common_hal_rtc_set_time(timeutils_struct_time_t *tm) { uint32_t epoch_s = timeutils_seconds_since_2000( tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec ); - rtc_offset = epoch_s - ticks_s; + rtc_offset[1] = epoch_s - ticks_s; + + // Set the prefix and suffix in order to indicate the time is valid. This + // must be done after the offset is updated, in case there is a crash or + // power failure. + rtc_offset[0] = RTC_OFFSET_CHECK_PREFIX; + rtc_offset[2] = RTC_OFFSET_CHECK_SUFFIX; } int common_hal_rtc_get_calibration(void) { diff --git a/ports/nrf/common-hal/rtc/RTC.h b/ports/nrf/common-hal/rtc/RTC.h index 0207c8338..e51f1f784 100644 --- a/ports/nrf/common-hal/rtc/RTC.h +++ b/ports/nrf/common-hal/rtc/RTC.h @@ -29,5 +29,6 @@ extern void rtc_init(void); extern void rtc_reset(void); +extern void common_hal_rtc_init(void); #endif // MICROPY_INCLUDED_NRF_COMMON_HAL_RTC_RTC_H -- cgit v1.2.3 From 9f7c26c60ddcd8ef371290e505f21148e3c98726 Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Mon, 18 May 2020 11:50:04 +0800 Subject: nrf: port: call common_hal_rtc_init() during init Call the new function to set the RTC, or reset it if the offset is not valid. Signed-off-by: Sean Cross --- ports/nrf/supervisor/port.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ports/nrf/supervisor/port.c b/ports/nrf/supervisor/port.c index 3a76fa878..4edb78f7c 100644 --- a/ports/nrf/supervisor/port.c +++ b/ports/nrf/supervisor/port.c @@ -120,6 +120,10 @@ safe_mode_t port_init(void) { // Configure millisecond timer initialization. tick_init(); +#if CIRCUITPY_RTC + common_hal_rtc_init(); +#endif + #if CIRCUITPY_ANALOGIO analogin_init(); #endif -- cgit v1.2.3