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-01-21 13:24:21 -05:00
# if defined(MICROPY_HW_LED_STATUS) || 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"
# include "supervisor/shared/rgb_led_status.h"
# include "supervisor/shared/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 ;
}
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 & &
reset_reason ! = RESET_REASON_RESET_PIN ) {
2020-10-27 20:55:03 -04:00
return NO_SAFE_MODE ;
}
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.
temp_status_color ( SAFE_MODE ) ;
# ifdef MICROPY_HW_LED_STATUS
digitalio_digitalinout_obj_t status_led ;
common_hal_digitalio_digitalinout_construct ( & status_led , MICROPY_HW_LED_STATUS ) ;
common_hal_digitalio_digitalinout_switch_to_output ( & status_led , true , DRIVE_MODE_PUSH_PULL ) ;
# 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 ;
while ( diff < 700 ) {
# ifdef MICROPY_HW_LED_STATUS
// Blink on for 100, off for 100, on for 100, off for 100 and on for 200
common_hal_digitalio_digitalinout_set_value ( & status_led , diff > 100 & & diff / 100 ! = 2 & & diff / 100 ! = 4 ) ;
# 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 ) ) {
return USER_SAFE_MODE ;
}
# endif
2019-11-18 09:22:41 -05:00
diff = supervisor_ticks_ms64 ( ) - start_ticks ;
2018-12-06 17:24:20 -05:00
}
# ifdef MICROPY_HW_LED_STATUS
common_hal_digitalio_digitalinout_deinit ( & status_led ) ;
# endif
clear_temp_status ( ) ;
port_set_saved_word ( SAFE_MODE_DATA_GUARD ) ;
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
2019-12-12 14:51:50 -05:00
# define FILE_AN_ISSUE translate("\nPlease file an issue with the contents of your CIRCUITPY drive at \nhttps: //github.com/adafruit/circuitpython/issues\n")
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 ;
}
2019-12-12 14:51:50 -05:00
serial_write ( " \n " ) ;
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
// Output a user safe mode string if it's set.
serial_write_compressed ( translate ( " You requested starting safe mode by " ) ) ;
2020-09-29 01:44:30 -04:00
serial_write_compressed ( BOARD_USER_SAFE_MODE_ACTION ) ;
2020-09-12 08:16:52 -04:00
serial_write_compressed ( translate ( " To exit, please reset the board without " ) ) ;
2020-09-29 01:44:30 -04:00
serial_write_compressed ( BOARD_USER_SAFE_MODE_ACTION ) ;
2020-09-11 11:36:54 -04:00
# else
2020-09-13 13:27:24 -04:00
break ;
2020-09-11 11:36:54 -04:00
# endif
return ;
case MANUAL_SAFE_MODE :
serial_write_compressed ( translate ( " CircuitPython is in safe mode because you pressed the reset button during boot. Press again to exit safe mode. \n " ) ) ;
return ;
case PROGRAMMATIC_SAFE_MODE :
serial_write_compressed ( translate ( " The `microcontroller` module was used to boot into safe mode. Press reset to exit safe mode. \n " ) ) ;
return ;
default :
break ;
}
2019-12-12 14:35:24 -05:00
2020-09-11 11:36:54 -04:00
serial_write_compressed ( translate ( " You are in safe mode: something unanticipated happened. \n " ) ) ;
switch ( reason ) {
case BROWNOUT :
serial_write_compressed ( 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). \n " ) ) ;
return ;
case HEAP_OVERWRITTEN :
serial_write_compressed ( translate ( " The CircuitPython heap was corrupted because the stack was too small. \n Please increase the stack size if you know how, or if not: " ) ) ;
serial_write_compressed ( FILE_AN_ISSUE ) ;
return ;
case NO_HEAP :
serial_write_compressed ( translate ( " CircuitPython was unable to allocate the heap. \n " ) ) ;
serial_write_compressed ( FILE_AN_ISSUE ) ;
return ;
default :
break ;
}
serial_write_compressed ( translate ( " CircuitPython core code crashed hard. Whoops! \n " ) ) ;
switch ( reason ) {
case HARD_CRASH :
serial_write_compressed ( translate ( " Crash into the HardFault_Handler. " ) ) ;
return ;
case MICROPY_NLR_JUMP_FAIL :
serial_write_compressed ( translate ( " MicroPython NLR jump failed. Likely memory corruption. " ) ) ;
return ;
case MICROPY_FATAL_ERROR :
serial_write_compressed ( translate ( " MicroPython fatal error. " ) ) ;
break ;
case GC_ALLOC_OUTSIDE_VM :
serial_write_compressed ( translate ( " Attempted heap allocation when MicroPython VM not running. " ) ) ;
break ;
# ifdef SOFTDEVICE_PRESENT
// defined in ports/nrf/bluetooth/bluetooth_common.mk
// will print "Unknown reason" if somehow encountered on other ports
case NORDIC_SOFT_DEVICE_ASSERT :
serial_write_compressed ( translate ( " Nordic Soft Device failure assertion. " ) ) ;
break ;
# endif
case FLASH_WRITE_FAIL :
serial_write_compressed ( translate ( " Failed to write internal flash. " ) ) ;
break ;
case MEM_MANAGE :
serial_write_compressed ( translate ( " Invalid memory access. " ) ) ;
break ;
case WATCHDOG_RESET :
serial_write_compressed ( translate ( " Watchdog timer expired. " ) ) ;
break ;
default :
serial_write_compressed ( translate ( " Unknown reason. " ) ) ;
break ;
}
serial_write_compressed ( FILE_AN_ISSUE ) ;
2020-09-13 13:47:14 -04:00
}