diff --git a/ports/nrf/boards/common.template.ld b/ports/nrf/boards/common.template.ld index a2bbf0ec80..9a7df69a47 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 : { diff --git a/ports/nrf/common-hal/rtc/RTC.c b/ports/nrf/common-hal/rtc/RTC.c index 9f56f2f0df..06345f5895 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 0207c8338c..e51f1f7848 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 diff --git a/ports/nrf/supervisor/port.c b/ports/nrf/supervisor/port.c index 539a9cf90b..4edb78f7c0 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 @@ -177,8 +181,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,21 +195,21 @@ 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. +// 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) {