2018-12-06 17:24:20 -05:00
/*
* This file is part of the MicroPython project , http : //micropython.org/
*
* The MIT License ( MIT )
*
* Copyright ( c ) 2018 Scott Shawcroft for Adafruit Industries
*
* Permission is hereby granted , free of charge , to any person obtaining a copy
* of this software and associated documentation files ( the " Software " ) , to deal
* in the Software without restriction , including without limitation the rights
* to use , copy , modify , merge , publish , distribute , sublicense , and / or sell
* copies of the Software , and to permit persons to whom the Software is
* furnished to do so , subject to the following conditions :
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER
* LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM ,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE .
*/
# include "supervisor/shared/safe_mode.h"
# include "mphalport.h"
2021-05-19 19:22:07 -04:00
# if defined(CIRCUITPY_BOOT_BUTTON)
2018-12-06 17:24:20 -05:00
# include "shared-bindings/digitalio/DigitalInOut.h"
2021-01-20 19:47:18 -05:00
# endif
2020-11-21 23:29:52 -05:00
# include "shared-bindings/microcontroller/Processor.h"
# include "shared-bindings/microcontroller/ResetReason.h"
2018-12-06 17:24:20 -05:00
# include "supervisor/serial.h"
# include "supervisor/shared/rgb_led_colors.h"
2021-04-16 19:18:01 -04:00
# include "supervisor/shared/status_leds.h"
2022-05-27 15:59:54 -04:00
# include "supervisor/shared/translate/translate.h"
2019-11-18 09:22:41 -05:00
# include "supervisor/shared/tick.h"
2018-12-06 17:24:20 -05:00
# define SAFE_MODE_DATA_GUARD 0xad0000af
# define SAFE_MODE_DATA_GUARD_MASK 0xff0000ff
static safe_mode_t current_safe_mode ;
safe_mode_t wait_for_safe_mode_reset ( void ) {
uint32_t reset_state = port_get_saved_word ( ) ;
safe_mode_t safe_mode = NO_SAFE_MODE ;
if ( ( reset_state & SAFE_MODE_DATA_GUARD_MASK ) = = SAFE_MODE_DATA_GUARD ) {
safe_mode = ( reset_state & ~ SAFE_MODE_DATA_GUARD_MASK ) > > 8 ;
}
if ( safe_mode ! = NO_SAFE_MODE ) {
port_set_saved_word ( SAFE_MODE_DATA_GUARD ) ;
current_safe_mode = safe_mode ;
return safe_mode ;
2021-07-14 19:45:47 -04:00
} else {
current_safe_mode = 0 ;
2018-12-06 17:24:20 -05:00
}
2020-11-21 23:29:52 -05:00
const mcu_reset_reason_t reset_reason = common_hal_mcu_processor_get_reset_reason ( ) ;
if ( reset_reason ! = RESET_REASON_POWER_ON & &
2021-03-01 00:01:02 -05:00
reset_reason ! = RESET_REASON_RESET_PIN & &
2021-07-14 19:45:47 -04:00
reset_reason ! = RESET_REASON_UNKNOWN & &
reset_reason ! = RESET_REASON_SOFTWARE ) {
2020-10-27 20:55:03 -04:00
return NO_SAFE_MODE ;
}
2021-08-30 17:05:00 -04:00
# ifdef CIRCUITPY_SKIP_SAFE_MODE_WAIT
return NO_SAFE_MODE ;
# endif
2018-12-06 17:24:20 -05:00
port_set_saved_word ( SAFE_MODE_DATA_GUARD | ( MANUAL_SAFE_MODE < < 8 ) ) ;
// Wait for a while to allow for reset.
2021-04-16 19:18:01 -04:00
# if CIRCUITPY_STATUS_LED
status_led_init ( ) ;
2018-12-06 17:24:20 -05:00
# endif
2020-09-11 11:36:54 -04:00
# ifdef CIRCUITPY_BOOT_BUTTON
digitalio_digitalinout_obj_t boot_button ;
common_hal_digitalio_digitalinout_construct ( & boot_button , CIRCUITPY_BOOT_BUTTON ) ;
common_hal_digitalio_digitalinout_switch_to_input ( & boot_button , PULL_UP ) ;
# endif
2019-11-18 09:22:41 -05:00
uint64_t start_ticks = supervisor_ticks_ms64 ( ) ;
2018-12-06 17:24:20 -05:00
uint64_t diff = 0 ;
2021-04-16 19:18:01 -04:00
bool boot_in_safe_mode = false ;
2021-05-18 20:37:21 -04:00
while ( diff < 1000 ) {
2021-04-16 19:18:01 -04:00
# ifdef CIRCUITPY_STATUS_LED
2021-05-19 19:22:07 -04:00
// Blink on for 100, off for 100
bool led_on = ( diff % 250 ) < 125 ;
2021-04-16 19:18:01 -04:00
if ( led_on ) {
new_status_color ( SAFE_MODE ) ;
} else {
new_status_color ( BLACK ) ;
}
2018-12-06 17:24:20 -05:00
# endif
2020-09-13 13:47:14 -04:00
# ifdef CIRCUITPY_BOOT_BUTTON
2020-09-11 11:36:54 -04:00
if ( ! common_hal_digitalio_digitalinout_get_value ( & boot_button ) ) {
2021-04-16 19:18:01 -04:00
boot_in_safe_mode = true ;
break ;
2020-09-11 11:36:54 -04:00
}
# endif
2019-11-18 09:22:41 -05:00
diff = supervisor_ticks_ms64 ( ) - start_ticks ;
2018-12-06 17:24:20 -05:00
}
2021-04-16 19:18:01 -04:00
# if CIRCUITPY_STATUS_LED
new_status_color ( BLACK ) ;
status_led_deinit ( ) ;
2018-12-06 17:24:20 -05:00
# endif
2021-04-16 19:18:01 -04:00
if ( boot_in_safe_mode ) {
return USER_SAFE_MODE ;
}
2021-05-19 19:22:07 -04:00
// Restore the original state of the saved word if no reset occured during our wait period.
port_set_saved_word ( reset_state ) ;
2018-12-06 17:24:20 -05:00
return NO_SAFE_MODE ;
}
2019-05-09 13:15:28 -04:00
void safe_mode_on_next_reset ( safe_mode_t reason ) {
port_set_saved_word ( SAFE_MODE_DATA_GUARD | ( reason < < 8 ) ) ;
}
2019-04-08 19:58:50 -04:00
// Don't inline this so it's easy to break on it from GDB.
2019-04-05 22:06:37 -04:00
void __attribute__ ( ( noinline , ) ) reset_into_safe_mode ( safe_mode_t reason ) {
2018-12-06 17:24:20 -05:00
if ( current_safe_mode > BROWNOUT & & reason > BROWNOUT ) {
while ( true ) {
// This very bad because it means running in safe mode didn't save us. Only ignore brownout
// because it may be due to a switch bouncing.
}
}
2019-05-09 13:15:28 -04:00
safe_mode_on_next_reset ( reason ) ;
2018-12-06 17:24:20 -05:00
reset_cpu ( ) ;
}
2019-12-12 14:35:24 -05:00
2018-12-06 17:24:20 -05:00
void print_safe_mode_message ( safe_mode_t reason ) {
2019-05-08 18:23:40 -04:00
if ( reason = = NO_SAFE_MODE ) {
return ;
}
2021-05-12 15:53:52 -04:00
serial_write ( " \r \n " ) ;
serial_write_compressed ( translate ( " You are in safe mode because: \n " ) ) ;
const compressed_string_t * message = NULL ;
// First check for safe mode reasons that do not necessarily reflect bugs.
2019-12-12 14:35:24 -05:00
2020-09-11 11:36:54 -04:00
switch ( reason ) {
case USER_SAFE_MODE :
# ifdef BOARD_USER_SAFE_MODE_ACTION
2021-05-20 12:57:33 -04:00
message = BOARD_USER_SAFE_MODE_ACTION ;
2022-03-21 20:58:43 -04:00
# elif defined(CIRCUITPY_BOOT_BUTTON)
2022-10-28 23:31:12 -04:00
message = translate ( " The BOOT button was pressed at start up. \n " ) ;
2020-09-11 11:36:54 -04:00
# endif
2022-03-21 20:58:43 -04:00
if ( message ! = NULL ) {
// Output a user safe mode string if it's set.
serial_write_compressed ( message ) ;
2022-10-28 23:31:12 -04:00
message = translate ( " To exit, please reset the board without requesting safe mode. " ) ;
2022-03-21 20:58:43 -04:00
// The final piece is printed below.
}
2021-05-12 15:53:52 -04:00
break ;
2020-09-11 11:36:54 -04:00
case MANUAL_SAFE_MODE :
2021-05-12 15:53:52 -04:00
message = translate ( " You pressed the reset button during boot. Press again to exit safe mode. " ) ;
break ;
2020-09-11 11:36:54 -04:00
case PROGRAMMATIC_SAFE_MODE :
2021-05-12 15:53:52 -04:00
message = translate ( " The `microcontroller` module was used to boot into safe mode. Press reset to exit safe mode. " ) ;
2020-09-11 11:36:54 -04:00
break ;
case BROWNOUT :
2021-05-12 15:53:52 -04:00
message = translate ( " The microcontroller's power dipped. Make sure your power supply provides \n enough power for the whole circuit and press reset (after ejecting CIRCUITPY). " ) ;
break ;
case USB_TOO_MANY_ENDPOINTS :
message = translate ( " USB devices need more endpoints than are available. " ) ;
break ;
case USB_TOO_MANY_INTERFACE_NAMES :
message = translate ( " USB devices specify too many interface names. " ) ;
break ;
2021-10-13 12:30:01 -04:00
case USB_BOOT_DEVICE_NOT_INTERFACE_ZERO :
message = translate ( " Boot device must be first device (interface #0). " ) ;
break ;
2021-05-12 15:53:52 -04:00
case WATCHDOG_RESET :
2022-06-29 15:31:18 -04:00
message = translate ( " Internal watchdog timer expired. " ) ;
2021-05-12 15:53:52 -04:00
break ;
2021-12-27 18:58:24 -05:00
case NO_CIRCUITPY :
message = translate ( " CIRCUITPY drive could not be found or created. " ) ;
2021-12-27 19:36:11 -05:00
break ;
2020-09-11 11:36:54 -04:00
default :
break ;
}
2021-05-12 15:53:52 -04:00
if ( message ) {
serial_write_compressed ( message ) ;
serial_write ( " \r \n " ) ;
return ;
}
// Something worse happened.
2020-09-11 11:36:54 -04:00
serial_write_compressed ( translate ( " CircuitPython core code crashed hard. Whoops! \n " ) ) ;
2021-05-05 12:35:12 -04:00
2020-09-11 11:36:54 -04:00
switch ( reason ) {
case HARD_CRASH :
2021-05-05 12:35:12 -04:00
message = translate ( " Crash into the HardFault_Handler. " ) ;
break ;
2020-09-11 11:36:54 -04:00
case MICROPY_NLR_JUMP_FAIL :
2021-05-05 12:35:12 -04:00
message = translate ( " NLR jump failed. Likely memory corruption. " ) ;
break ;
2020-09-11 11:36:54 -04:00
case MICROPY_FATAL_ERROR :
2021-05-05 12:35:12 -04:00
message = translate ( " Fatal error. " ) ;
2020-09-11 11:36:54 -04:00
break ;
2021-05-12 15:53:52 -04:00
case NO_HEAP :
message = translate ( " CircuitPython was unable to allocate the heap. " ) ;
break ;
case HEAP_OVERWRITTEN :
message = translate ( " The CircuitPython heap was corrupted because the stack was too small. \n Increase the stack size if you know how. If not: " ) ;
break ;
2020-09-11 11:36:54 -04:00
case GC_ALLOC_OUTSIDE_VM :
2021-05-05 12:35:12 -04:00
message = translate ( " Attempted heap allocation when VM not running. " ) ;
2020-09-11 11:36:54 -04:00
break ;
2021-03-15 09:57:36 -04:00
# ifdef SOFTDEVICE_PRESENT
2020-09-11 11:36:54 -04:00
// defined in ports/nrf/bluetooth/bluetooth_common.mk
// will print "Unknown reason" if somehow encountered on other ports
case NORDIC_SOFT_DEVICE_ASSERT :
2021-05-05 12:35:12 -04:00
message = translate ( " Nordic system firmware failure assertion. " ) ;
2020-09-11 11:36:54 -04:00
break ;
2021-03-15 09:57:36 -04:00
# endif
2020-09-11 11:36:54 -04:00
case FLASH_WRITE_FAIL :
2021-05-05 12:35:12 -04:00
message = translate ( " Failed to write internal flash. " ) ;
2020-09-11 11:36:54 -04:00
break ;
case MEM_MANAGE :
2021-05-05 12:35:12 -04:00
message = translate ( " Invalid memory access. " ) ;
2020-09-11 11:36:54 -04:00
break ;
default :
2021-05-05 12:35:12 -04:00
message = translate ( " Unknown reason. " ) ;
2020-09-11 11:36:54 -04:00
break ;
}
2021-05-05 12:35:12 -04:00
serial_write_compressed ( message ) ;
2021-05-12 15:53:52 -04:00
serial_write_compressed ( translate ( " \n Please file an issue with the contents of your CIRCUITPY drive at \n https://github.com/adafruit/circuitpython/issues \n " ) ) ;
2020-09-13 13:47:14 -04:00
}