Merge pull request #7577 from dhalbert/safemode-py

Implement safemode.py
This commit is contained in:
Dan Halbert 2023-02-16 14:15:20 -05:00 committed by GitHub
commit bbadc00599
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
56 changed files with 567 additions and 395 deletions

View File

@ -138,6 +138,16 @@ Behavior
- Adds a safe mode that does not run user code after a hard crash or brown out. This makes it
possible to fix code that causes nasty crashes by making it available through mass storage after
the crash. A reset (the button) is needed after it's fixed to get back into normal mode.
- Safe mode may be handled programmatically by providing a ``safemode.py``.
``safemode.py`` is run if the board has reset due to entering safe mode, unless the safe mode
initiated by the user by pressing button(s).
USB is not available so nothing can be printed.
``safemode.py`` can determine why the safe mode occurred
using ``supervisor.runtime.safe_mode_reason``, and take appropriate action. For instance,
if a hard crash occurred, ``safemode.py`` may do a ``microcontroller.reset()``
to automatically restart despite the crash.
If the battery is low, but is being charged, ``safemode.py`` may put the board in deep sleep
for a while. Or it may simply reset, and have ``code.py`` check the voltage and do the sleep.
- RGB status LED indicating CircuitPython state.
- One green flash - code completed without error.
- Two red flashes - code ended due to an exception.
@ -145,9 +155,9 @@ Behavior
- Re-runs ``code.py`` or other main file after file system writes by a workflow. (Disable with
``supervisor.disable_autoreload()``)
- Autoreload is disabled while the REPL is active.
- Main is one of these: ``code.txt``, ``code.py``, ``main.py``,
``main.txt``
- Boot is one of these: ``boot.py``, ``boot.txt``
- ``code.py`` may also be named``code.txt``, ``main.py``, or ``main.txt``.
- ``boot.py`` may also be named ``boot.txt``.
- ``safemode.py`` may also be named ``safemode.txt``.
API
~~~

View File

@ -31,8 +31,20 @@ msgstr ""
#: supervisor/shared/safe_mode.c
msgid ""
"\n"
"Please file an issue with the contents of your CIRCUITPY drive at \n"
"https://github.com/adafruit/circuitpython/issues\n"
"Please file an issue with your program at https://github.com/adafruit/"
"circuitpython/issues."
msgstr ""
#: supervisor/shared/safe_mode.c
msgid ""
"\n"
"Press reset to exit safe mode.\n"
msgstr ""
#: supervisor/shared/safe_mode.c
msgid ""
"\n"
"You are in safe mode because:\n"
msgstr ""
#: py/obj.c
@ -85,7 +97,7 @@ msgstr ""
#: ports/raspberrypi/common-hal/alarm/__init__.c
#: ports/raspberrypi/common-hal/analogio/AnalogOut.c
#: ports/raspberrypi/common-hal/rtc/RTC.c ports/stm/common-hal/alarm/__init__.c
#: ports/stm/common-hal/rtc/RTC.c
#: ports/stm/common-hal/canio/Listener.c ports/stm/common-hal/rtc/RTC.c
msgid "%q"
msgstr ""
@ -530,10 +542,6 @@ msgstr ""
msgid "Attempt to allocate %d blocks"
msgstr ""
#: supervisor/shared/safe_mode.c
msgid "Attempted heap allocation when VM not running."
msgstr ""
#: ports/raspberrypi/audio_dma.c
msgid "Audio conversion not implemented"
msgstr ""
@ -582,20 +590,13 @@ msgid "Bitmap size and bits per value must match"
msgstr ""
#: supervisor/shared/safe_mode.c
msgid "Boot device must be first device (interface #0)."
msgid "Boot device must be first (interface #0)."
msgstr ""
#: ports/mimxrt10xx/common-hal/busio/UART.c
msgid "Both RX and TX required for flow control"
msgstr ""
#: ports/atmel-samd/boards/circuitplayground_express/mpconfigboard.h
#: ports/atmel-samd/boards/circuitplayground_express_crickit/mpconfigboard.h
#: ports/atmel-samd/boards/circuitplayground_express_displayio/mpconfigboard.h
#: ports/atmel-samd/boards/meowmeow/mpconfigboard.h
msgid "Both buttons were pressed at start up.\n"
msgstr ""
#: ports/atmel-samd/common-hal/rotaryio/IncrementalEncoder.c
msgid "Both pins must support hardware interrupts"
msgstr ""
@ -661,12 +662,6 @@ msgstr ""
msgid "Bus pin %d is already in use"
msgstr ""
#: ports/espressif/boards/m5stack_core_basic/mpconfigboard.h
#: ports/espressif/boards/m5stack_core_fire/mpconfigboard.h
#: ports/espressif/boards/m5stack_stick_c/mpconfigboard.h
msgid "Button A was pressed at start up.\n"
msgstr ""
#: shared-bindings/_bleio/UUID.c
msgid "Byte buffer must be 16 bytes."
msgstr ""
@ -797,10 +792,6 @@ msgstr ""
msgid "CircuitPython core code crashed hard. Whoops!\n"
msgstr ""
#: supervisor/shared/safe_mode.c
msgid "CircuitPython was unable to allocate the heap."
msgstr ""
#: shared-module/bitbangio/I2C.c
msgid "Clock stretch too long"
msgstr ""
@ -839,10 +830,6 @@ msgstr ""
msgid "Couldn't allocate decoder"
msgstr ""
#: supervisor/shared/safe_mode.c
msgid "Crash into the HardFault_Handler."
msgstr ""
#: ports/stm/common-hal/analogio/AnalogOut.c
msgid "DAC Channel Init Error"
msgstr ""
@ -934,6 +921,10 @@ msgstr ""
msgid "Error in regex"
msgstr ""
#: supervisor/shared/safe_mode.c
msgid "Error in safemode.py."
msgstr ""
#: shared-bindings/socketpool/Socket.c shared-bindings/ssl/SSLSocket.c
msgid "Error: Failure to bind"
msgstr ""
@ -1007,7 +998,7 @@ msgid "Failed to write internal flash."
msgstr ""
#: supervisor/shared/safe_mode.c
msgid "Fatal error."
msgid "Fault detected by hardware."
msgstr ""
#: py/moduerrno.c
@ -1095,6 +1086,15 @@ msgstr ""
msgid "Hardware in use, try alternative pins"
msgstr ""
#: supervisor/shared/safe_mode.c
msgid "Heap allocation when VM not running."
msgstr ""
#: supervisor/shared/safe_mode.c
msgid ""
"Heap was corrupted because the stack was too small. Increase stack size."
msgstr ""
#: extmod/vfs_posix_file.c py/objstringio.c
msgid "I/O operation on closed file"
msgstr ""
@ -1209,6 +1209,10 @@ msgstr ""
msgid "Internal watchdog timer expired."
msgstr ""
#: supervisor/shared/safe_mode.c
msgid "Interrupt error."
msgstr ""
#: py/argcheck.c shared-bindings/digitalio/DigitalInOut.c
msgid "Invalid %q"
msgstr ""
@ -1257,10 +1261,6 @@ msgstr ""
msgid "Invalid format chunk size"
msgstr ""
#: supervisor/shared/safe_mode.c
msgid "Invalid memory access."
msgstr ""
#: ports/espressif/common-hal/wifi/Radio.c
msgid "Invalid multicast MAC address"
msgstr ""
@ -1549,10 +1549,6 @@ msgstr ""
msgid "No timer available"
msgstr ""
#: supervisor/shared/safe_mode.c
msgid "Nordic system firmware failure assertion."
msgstr ""
#: ports/nrf/common-hal/_bleio/__init__.c
msgid "Nordic system firmware out of memory"
msgstr ""
@ -2004,53 +2000,19 @@ msgid "Temperature read timed out"
msgstr ""
#: supervisor/shared/safe_mode.c
msgid "The BOOT button was pressed at start up.\n"
msgstr ""
#: supervisor/shared/safe_mode.c
msgid ""
"The CircuitPython heap was corrupted because the stack was too small.\n"
"Increase the stack size if you know how. If not:"
msgstr ""
#: ports/espressif/boards/adafruit_feather_esp32_v2/mpconfigboard.h
msgid "The SW38 button was pressed at start up.\n"
msgstr ""
#: ports/espressif/boards/hardkernel_odroid_go/mpconfigboard.h
msgid "The VOLUME button was pressed at start up.\n"
msgstr ""
#: supervisor/shared/safe_mode.c
msgid ""
"The `microcontroller` module was used to boot into safe mode. Press reset to "
"exit safe mode."
msgid "The `microcontroller` module was used to boot into safe mode."
msgstr ""
#: py/obj.c
msgid "The above exception was the direct cause of the following exception:"
msgstr ""
#: ports/espressif/boards/m5stack_atom_echo/mpconfigboard.h
#: ports/espressif/boards/m5stack_atom_lite/mpconfigboard.h
#: ports/espressif/boards/m5stack_atom_matrix/mpconfigboard.h
#: ports/espressif/boards/m5stack_atom_u/mpconfigboard.h
msgid "The central button was pressed at start up.\n"
msgstr ""
#: ports/nrf/boards/aramcon2_badge/mpconfigboard.h
msgid "The left button was pressed at start up.\n"
msgstr ""
#: shared-bindings/rgbmatrix/RGBMatrix.c
msgid "The length of rgb_pins must be 6, 12, 18, 24, or 30"
msgstr ""
#: supervisor/shared/safe_mode.c
msgid ""
"The microcontroller's power dipped. Make sure your power supply provides\n"
"enough power for the whole circuit and press reset (after ejecting "
"CIRCUITPY)."
msgid "The power dipped. Make sure you are providing enough power."
msgstr ""
#: shared-module/audiomixer/MixerVoice.c
@ -2069,6 +2031,10 @@ msgstr ""
msgid "The sample's signedness does not match the mixer's"
msgstr ""
#: supervisor/shared/safe_mode.c
msgid "Third-party firmware fatal error."
msgstr ""
#: shared-module/imagecapture/ParallelImageCapture.c
msgid "This microcontroller does not support continuous capture."
msgstr ""
@ -2101,10 +2067,6 @@ msgstr ""
msgid "Timeout is too long: Maximum timeout length is %d seconds"
msgstr ""
#: supervisor/shared/safe_mode.c
msgid "To exit, please reset the board without requesting safe mode."
msgstr ""
#: ports/atmel-samd/common-hal/audiobusio/I2SOut.c
msgid "Too many channels in sample"
msgstr ""
@ -2196,6 +2158,10 @@ msgstr ""
msgid "Unable to allocate buffers for signed conversion"
msgstr ""
#: supervisor/shared/safe_mode.c
msgid "Unable to allocate the heap."
msgstr ""
#: ports/espressif/common-hal/busio/I2C.c
msgid "Unable to create lock"
msgstr ""
@ -2401,13 +2367,44 @@ msgstr ""
msgid "Writes not supported on Characteristic"
msgstr ""
#: supervisor/shared/safe_mode.c
msgid "You are in safe mode because:\n"
#: ports/atmel-samd/boards/circuitplayground_express/mpconfigboard.h
#: ports/atmel-samd/boards/circuitplayground_express_crickit/mpconfigboard.h
#: ports/atmel-samd/boards/circuitplayground_express_displayio/mpconfigboard.h
#: ports/atmel-samd/boards/meowmeow/mpconfigboard.h
msgid "You pressed both buttons at start up."
msgstr ""
#: ports/espressif/boards/m5stack_core_basic/mpconfigboard.h
#: ports/espressif/boards/m5stack_core_fire/mpconfigboard.h
#: ports/espressif/boards/m5stack_stick_c/mpconfigboard.h
msgid "You pressed button A at start up."
msgstr ""
#: supervisor/shared/safe_mode.c
msgid ""
"You pressed the reset button during boot. Press again to exit safe mode."
msgid "You pressed the BOOT button at start up"
msgstr ""
#: ports/espressif/boards/adafruit_feather_esp32_v2/mpconfigboard.h
msgid "You pressed the SW38 button at start up."
msgstr ""
#: ports/espressif/boards/hardkernel_odroid_go/mpconfigboard.h
msgid "You pressed the VOLUME button at start up."
msgstr ""
#: ports/espressif/boards/m5stack_atom_echo/mpconfigboard.h
#: ports/espressif/boards/m5stack_atom_lite/mpconfigboard.h
#: ports/espressif/boards/m5stack_atom_matrix/mpconfigboard.h
#: ports/espressif/boards/m5stack_atom_u/mpconfigboard.h
msgid "You pressed the central button at start up."
msgstr ""
#: ports/nrf/boards/aramcon2_badge/mpconfigboard.h
msgid "You pressed the left button at start up."
msgstr ""
#: supervisor/shared/safe_mode.c
msgid "You pressed the reset button during boot."
msgstr ""
#: supervisor/shared/micropython.c

67
main.c
View File

@ -358,7 +358,7 @@ STATIC void print_code_py_status_message(safe_mode_t safe_mode) {
} else {
serial_write_compressed(translate("Auto-reload is off.\n"));
}
if (safe_mode != NO_SAFE_MODE) {
if (safe_mode != SAFE_MODE_NONE) {
serial_write_compressed(translate("Running in safe mode! Not running saved code.\n"));
}
}
@ -384,11 +384,11 @@ STATIC bool run_code_py(safe_mode_t safe_mode, bool *simulate_reset) {
// Do the filesystem flush check before reload in case another write comes
// in while we're doing the flush.
if (safe_mode == NO_SAFE_MODE) {
if (safe_mode == SAFE_MODE_NONE) {
stack_resize();
filesystem_flush();
}
if (safe_mode == NO_SAFE_MODE && !autoreload_pending()) {
if (safe_mode == SAFE_MODE_NONE && !autoreload_pending()) {
static const char *const supported_filenames[] = {
"code.txt", "code.py", "main.py", "main.txt"
};
@ -510,7 +510,7 @@ STATIC bool run_code_py(safe_mode_t safe_mode, bool *simulate_reset) {
} else
#endif
if (_exec_result.return_code != PYEXEC_EXCEPTION) {
if (safe_mode == NO_SAFE_MODE) {
if (safe_mode == SAFE_MODE_NONE) {
color = ALL_DONE;
blink_count = ALL_DONE_BLINKS;
} else {
@ -730,8 +730,34 @@ STATIC bool run_code_py(safe_mode_t safe_mode, bool *simulate_reset) {
vstr_t *boot_output;
#if CIRCUITPY_SAFEMODE_PY
STATIC void __attribute__ ((noinline)) run_safemode_py(safe_mode_t safe_mode) {
// Don't run if we aren't in safe mode or we won't be able to find safemode.py.
// Also don't run if it's a user-initiated safemode (pressing button(s) during boot),
// since that's deliberate.
if (safe_mode == SAFE_MODE_NONE || safe_mode == SAFE_MODE_USER || !filesystem_present()) {
return;
}
supervisor_allocation *heap = allocate_remaining_memory();
start_mp(heap);
static const char *const safemode_py_filenames[] = {"safemode.py", "safemode.txt"};
maybe_run_list(safemode_py_filenames, MP_ARRAY_SIZE(safemode_py_filenames));
// If safemode.py itself caused an error, change the safe_mode state to indicate that.
if (_exec_result.exception != MP_OBJ_NULL &&
_exec_result.exception != MP_OBJ_SENTINEL) {
set_safe_mode(SAFE_MODE_SAFEMODE_PY_ERROR);
}
cleanup_after_vm(heap, _exec_result.exception);
_exec_result.exception = NULL;
}
#endif
STATIC void __attribute__ ((noinline)) run_boot_py(safe_mode_t safe_mode) {
if (safe_mode == NO_HEAP) {
if (safe_mode == SAFE_MODE_NO_HEAP) {
return;
}
@ -739,7 +765,7 @@ STATIC void __attribute__ ((noinline)) run_boot_py(safe_mode_t safe_mode) {
// There is USB setup to do even if boot.py is not actually run.
const bool ok_to_run = filesystem_present()
&& safe_mode == NO_SAFE_MODE
&& safe_mode == SAFE_MODE_NONE
&& MP_STATE_VM(vfs_mount_table) != NULL;
static const char *const boot_py_filenames[] = {"boot.py", "boot.txt"};
@ -913,7 +939,7 @@ STATIC int run_repl(void) {
int __attribute__((used)) main(void) {
// initialise the cpu and peripherals
safe_mode_t safe_mode = port_init();
set_safe_mode(port_init());
// Turn on RX and TX LEDs if we have them.
init_rxtx_leds();
@ -929,8 +955,8 @@ int __attribute__((used)) main(void) {
serial_early_init();
// Wait briefly to give a reset window where we'll enter safe mode after the reset.
if (safe_mode == NO_SAFE_MODE) {
safe_mode = wait_for_safe_mode_reset();
if (get_safe_mode() == SAFE_MODE_NONE) {
set_safe_mode(wait_for_safe_mode_reset());
}
stack_init();
@ -956,8 +982,8 @@ int __attribute__((used)) main(void) {
// Check whether CIRCUITPY is available. No need to reset to get safe mode
// since we haven't run user code yet.
if (!filesystem_init(safe_mode == NO_SAFE_MODE, false)) {
safe_mode = NO_CIRCUITPY;
if (!filesystem_init(get_safe_mode() == SAFE_MODE_NONE, false)) {
set_safe_mode(SAFE_MODE_NO_CIRCUITPY);
}
#if CIRCUITPY_ALARM
@ -982,16 +1008,23 @@ int __attribute__((used)) main(void) {
supervisor_set_run_reason(RUN_REASON_STARTUP);
// If not in safe mode turn on autoreload by default but before boot.py in case it wants to change it.
if (safe_mode == NO_SAFE_MODE) {
if (get_safe_mode() == SAFE_MODE_NONE) {
autoreload_enable();
}
// By default our internal flash is readonly to local python code and
// writable over USB. Set it here so that boot.py can change it.
// writable over USB. Set it here so that safemode.py or boot.py can change it.
filesystem_set_internal_concurrent_write_protection(true);
filesystem_set_internal_writable_by_usb(CIRCUITPY_USB == 1);
run_boot_py(safe_mode);
#if CIRCUITPY_SAFEMODE_PY
// Run safemode.py if we ARE in safe mode.
// If safemode.py does not do a hard reset, and exits normally, we will continue on
// and report the safe mode as usual.
run_safemode_py(get_safe_mode());
#endif
run_boot_py(get_safe_mode());
supervisor_workflow_start();
@ -1016,7 +1049,7 @@ int __attribute__((used)) main(void) {
// If code.py did a fake deep sleep, pretend that we
// are running code.py for the first time after a hard
// reset. This will preserve any alarm information.
skip_repl = run_code_py(safe_mode, &simulate_reset);
skip_repl = run_code_py(get_safe_mode(), &simulate_reset);
} else {
skip_repl = false;
}
@ -1076,14 +1109,14 @@ void gc_collect(void) {
}
void NORETURN nlr_jump_fail(void *val) {
reset_into_safe_mode(MICROPY_NLR_JUMP_FAIL);
reset_into_safe_mode(SAFE_MODE_NLR_JUMP_FAIL);
while (true) {
}
}
#ifndef NDEBUG
static void NORETURN __fatal_error(const char *msg) {
reset_into_safe_mode(MICROPY_FATAL_ERROR);
reset_into_safe_mode(SAFE_MODE_HARD_FAULT);
while (true) {
}
}

View File

@ -23,7 +23,7 @@
#define CALIBRATE_CRYSTALLESS 1
// Explanation of how a user got into safe mode.
#define BOARD_USER_SAFE_MODE_ACTION translate("Both buttons were pressed at start up.\n")
#define BOARD_USER_SAFE_MODE_ACTION translate("You pressed both buttons at start up.")
// Increase stack size slightly due to CPX library import nesting
#define CIRCUITPY_DEFAULT_STACK_SIZE (4248) // divisible by 8

View File

@ -23,7 +23,7 @@
#define CALIBRATE_CRYSTALLESS 1
// Explanation of how a user got into safe mode.
#define BOARD_USER_SAFE_MODE_ACTION translate("Both buttons were pressed at start up.\n")
#define BOARD_USER_SAFE_MODE_ACTION translate("You pressed both buttons at start up.")
// Increase stack size slightly due to CPX library import nesting
#define CIRCUITPY_DEFAULT_STACK_SIZE (4248) // divisible by 8

View File

@ -23,7 +23,7 @@
#define CALIBRATE_CRYSTALLESS 1
// Explanation of how a user got into safe mode.
#define BOARD_USER_SAFE_MODE_ACTION translate("Both buttons were pressed at start up.\n")
#define BOARD_USER_SAFE_MODE_ACTION translate("You pressed both buttons at start up.")
// Increase stack size slightly due to CPX library import nesting.
#define CIRCUITPY_DEFAULT_STACK_SIZE (4248) // divisible by 8

View File

@ -6,7 +6,7 @@
#define CALIBRATE_CRYSTALLESS 1
// Explanation of how a user got into safe mode.
#define BOARD_USER_SAFE_MODE_ACTION translate("Both buttons were pressed at start up.\n")
#define BOARD_USER_SAFE_MODE_ACTION translate("You pressed both buttons at start up.")
#define DEFAULT_I2C_BUS_SCL (&pin_PA01)
#define DEFAULT_I2C_BUS_SDA (&pin_PA00)

View File

@ -53,3 +53,13 @@ CIRCUITPY_DISPLAY_FONT = $(TOP)/ports/atmel-samd/boards/ugame10/brutalist-6.bdf
# Override optimization to keep binary small
OPTIMIZATION_FLAGS = -Os
# We don't have room for the fonts for terminalio for certain languages,
# so turn off terminalio, and if it's off and displayio is on,
# force a clean build.
# Note that we cannot test $(CIRCUITPY_DISPLAYIO) directly with an
# ifeq, because it's not set yet.
ifneq (,$(filter $(TRANSLATION),ja ko ru))
CIRCUITPY_TERMINALIO = 0
RELEASE_NEEDS_CLEAN_BUILD = $(CIRCUITPY_DISPLAYIO)
endif

View File

@ -51,9 +51,8 @@ void common_hal_mcu_disable_interrupts(void) {
void common_hal_mcu_enable_interrupts(void) {
if (nesting_count == 0) {
// This is very very bad because it means there was mismatched disable/enables so we
// "HardFault".
HardFault_Handler();
// This is very very bad because it means there was mismatched disable/enables.
reset_into_safe_mode(SAFE_MODE_INTERRUPT_ERROR);
}
nesting_count--;
if (nesting_count > 0) {
@ -76,7 +75,7 @@ void common_hal_mcu_on_next_reset(mcu_runmode_t runmode) {
_bootloader_dbl_tap = DBL_TAP_MAGIC_QUICK_BOOT;
}
if (runmode == RUNMODE_SAFE_MODE) {
safe_mode_on_next_reset(PROGRAMMATIC_SAFE_MODE);
safe_mode_on_next_reset(SAFE_MODE_PROGRAMMATIC);
}
}

View File

@ -57,6 +57,7 @@ CIRCUITPY_ZLIB = 0
ifeq ($(INTERNAL_FLASH_FILESYSTEM),1)
CIRCUITPY_ONEWIREIO ?= 0
CIRCUITPY_SAFEMODE_PY ?= 0
CIRCUITPY_USB_IDENTIFICATION ?= 0
endif
@ -77,10 +78,8 @@ SUPEROPT_VM = 0
CIRCUITPY_LTO_PARTITION = one
ifeq ($(CIRCUITPY_FULL_BUILD),0)
# On the smallest boards, this saves about 180 bytes. On other boards, it may -increase- space used.
# On smaller builds this saves about 180 bytes. On other boards, it may -increase- space used, so use with care.
CFLAGS_BOARD = -fweb -frename-registers
endif
endif # samd21
######################################################################

View File

@ -368,20 +368,20 @@ safe_mode_t port_init(void) {
#ifdef SAMD21
if (PM->RCAUSE.bit.BOD33 == 1 || PM->RCAUSE.bit.BOD12 == 1) {
return BROWNOUT;
return SAFE_MODE_BROWNOUT;
}
#endif
#ifdef SAM_D5X_E5X
if (RSTC->RCAUSE.bit.BODVDD == 1 || RSTC->RCAUSE.bit.BODCORE == 1) {
return BROWNOUT;
return SAFE_MODE_BROWNOUT;
}
#endif
if (board_requests_safe_mode()) {
return USER_SAFE_MODE;
return SAFE_MODE_USER;
}
return NO_SAFE_MODE;
return SAFE_MODE_NONE;
}
void reset_port(void) {
@ -720,7 +720,7 @@ __attribute__((used)) void HardFault_Handler(void) {
REG_MTB_MASTER = 0x00000000 + 6;
#endif
reset_into_safe_mode(HARD_CRASH);
reset_into_safe_mode(SAFE_MODE_HARD_FAULT);
while (true) {
asm ("nop;");
}

View File

@ -46,7 +46,7 @@ void common_hal_mcu_disable_interrupts(void) {
void common_hal_mcu_enable_interrupts(void) {
if (nesting_count == 0) {
// reset_into_safe_mode(LOCKING_ERROR);
// reset_into_safe_mode(SAFE_MODE_INTERRUPT_ERROR);
}
nesting_count--;
if (nesting_count > 0) {

View File

@ -78,10 +78,10 @@ safe_mode_t port_init(void) {
// Check brownout.
if (board_requests_safe_mode()) {
return USER_SAFE_MODE;
return SAFE_MODE_USER;
}
return NO_SAFE_MODE;
return SAFE_MODE_NONE;
}
void reset_port(void) {

View File

@ -74,7 +74,7 @@ void common_hal_mcu_on_next_reset(mcu_runmode_t runmode) {
if (runmode == RUNMODE_BOOTLOADER) {
mp_raise_ValueError(translate("Cannot reset into bootloader because no bootloader is present"));
} else if (runmode == RUNMODE_SAFE_MODE) {
safe_mode_on_next_reset(PROGRAMMATIC_SAFE_MODE);
safe_mode_on_next_reset(SAFE_MODE_PROGRAMMATIC);
}
}

View File

@ -66,10 +66,10 @@ safe_mode_t port_init(void) {
heap_size = size / sizeof(uint32_t);
if (board_requests_safe_mode()) {
return USER_SAFE_MODE;
return SAFE_MODE_USER;
}
return NO_SAFE_MODE;
return SAFE_MODE_NONE;
}
void reset_cpu(void) {

View File

@ -47,7 +47,7 @@
#define CIRCUITPY_BOOT_BUTTON (&pin_GPIO38)
// Explanation of how a user got into safe mode
#define BOARD_USER_SAFE_MODE_ACTION translate("The SW38 button was pressed at start up.\n")
#define BOARD_USER_SAFE_MODE_ACTION translate("You pressed the SW38 button at start up.")
// UART pins attached to the USB-serial converter chip
#define CIRCUITPY_CONSOLE_UART_TX (&pin_GPIO1)

View File

@ -35,7 +35,7 @@
#define CIRCUITPY_BOOT_BUTTON (&pin_GPIO0)
// Explanation of how a user got into safe mode
#define BOARD_USER_SAFE_MODE_ACTION translate("The VOLUME button was pressed at start up.\n")
#define BOARD_USER_SAFE_MODE_ACTION translate("You pressed the VOLUME button at start up.")
// UART pins attached to the USB-serial converter chip
#define CIRCUITPY_CONSOLE_UART_TX (&pin_GPIO1)

View File

@ -38,7 +38,7 @@
#define CIRCUITPY_BOOT_BUTTON (&pin_GPIO39)
// Explanation of how a user got into safe mode
#define BOARD_USER_SAFE_MODE_ACTION translate("The central button was pressed at start up.\n")
#define BOARD_USER_SAFE_MODE_ACTION translate("You pressed the central button at start up.")
// UART pins attached to the USB-serial converter chip
#define CIRCUITPY_CONSOLE_UART_TX (&pin_GPIO1)

View File

@ -42,7 +42,7 @@
#define CIRCUITPY_BOOT_BUTTON (&pin_GPIO39)
// Explanation of how a user got into safe mode
#define BOARD_USER_SAFE_MODE_ACTION translate("The central button was pressed at start up.\n")
#define BOARD_USER_SAFE_MODE_ACTION translate("You pressed the central button at start up.")
// UART pins attached to the USB-serial converter chip
#define CIRCUITPY_CONSOLE_UART_TX (&pin_GPIO1)

View File

@ -42,7 +42,7 @@
#define CIRCUITPY_BOOT_BUTTON (&pin_GPIO39)
// Explanation of how a user got into safe mode
#define BOARD_USER_SAFE_MODE_ACTION translate("The central button was pressed at start up.\n")
#define BOARD_USER_SAFE_MODE_ACTION translate("You pressed the central button at start up.")
// UART pins attached to the USB-serial converter chip
#define CIRCUITPY_CONSOLE_UART_TX (&pin_GPIO1)

View File

@ -41,7 +41,7 @@
#define CIRCUITPY_BOOT_BUTTON (&pin_GPIO39)
// Explanation of how a user got into safe mode
#define BOARD_USER_SAFE_MODE_ACTION translate("The central button was pressed at start up.\n")
#define BOARD_USER_SAFE_MODE_ACTION translate("You pressed the central button at start up.")
// UART pins attached to the USB-serial converter chip
#define CIRCUITPY_CONSOLE_UART_TX (&pin_GPIO1)

View File

@ -42,7 +42,7 @@
#define CIRCUITPY_BOOT_BUTTON (&pin_GPIO39)
// Explanation of how a user got into safe mode
#define BOARD_USER_SAFE_MODE_ACTION translate("Button A was pressed at start up.\n")
#define BOARD_USER_SAFE_MODE_ACTION translate("You pressed button A at start up.")
// UART pins attached to the USB-serial converter chip
#define CIRCUITPY_CONSOLE_UART_TX (&pin_GPIO1)

View File

@ -43,7 +43,7 @@
#define CIRCUITPY_BOOT_BUTTON (&pin_GPIO39)
// Explanation of how a user got into safe mode
#define BOARD_USER_SAFE_MODE_ACTION translate("Button A was pressed at start up.\n")
#define BOARD_USER_SAFE_MODE_ACTION translate("You pressed button A at start up.")
// UART pins attached to the USB-serial converter chip
#define CIRCUITPY_CONSOLE_UART_TX (&pin_GPIO1)

View File

@ -39,7 +39,7 @@
#define CIRCUITPY_BOOT_BUTTON (&pin_GPIO37)
// Explanation of how a user got into safe mode
#define BOARD_USER_SAFE_MODE_ACTION translate("Button A was pressed at start up.\n")
#define BOARD_USER_SAFE_MODE_ACTION translate("You pressed button A at start up.")
// UART pins attached to the USB-serial converter chip
#define CIRCUITPY_CONSOLE_UART_TX (&pin_GPIO1)

View File

@ -108,7 +108,7 @@ void common_hal_mcu_on_next_reset(mcu_runmode_t runmode) {
break;
case RUNMODE_SAFE_MODE:
// enter safe mode on next boot
safe_mode_on_next_reset(PROGRAMMATIC_SAFE_MODE);
safe_mode_on_next_reset(SAFE_MODE_PROGRAMMATIC);
break;
case RUNMODE_BOOTLOADER:
// DFU download

View File

@ -318,25 +318,25 @@ safe_mode_t port_init(void) {
}
if (heap == NULL) {
heap_size = 0;
return NO_HEAP;
return SAFE_MODE_NO_HEAP;
}
esp_reset_reason_t reason = esp_reset_reason();
switch (reason) {
case ESP_RST_BROWNOUT:
return BROWNOUT;
return SAFE_MODE_BROWNOUT;
case ESP_RST_PANIC:
return HARD_CRASH;
return SAFE_MODE_HARD_FAULT;
case ESP_RST_INT_WDT:
// The interrupt watchdog is used internally to make sure that latency sensitive
// interrupt code isn't blocked. User watchdog resets come through ESP_RST_WDT.
return WATCHDOG_RESET;
return SAFE_MODE_WATCHDOG;
case ESP_RST_WDT:
default:
break;
}
return NO_SAFE_MODE;
return SAFE_MODE_NONE;
}
void reset_port(void) {

View File

@ -70,9 +70,8 @@ void common_hal_mcu_disable_interrupts(void) {
__attribute__((section(".ramtext")))
void common_hal_mcu_enable_interrupts(void) {
if (nesting_count == 0) {
// This is very very bad because it means there was mismatched disable/enables so we
// "HardFault".
asm ("ebreak");
// This is very very bad because it means there was mismatched disable/enables.
reset_into_safe_mode(SAFE_MODE_INTERRUPT_ERROR);
}
nesting_count--;
if (nesting_count > 0) {
@ -83,7 +82,7 @@ void common_hal_mcu_enable_interrupts(void) {
void common_hal_mcu_on_next_reset(mcu_runmode_t runmode) {
if (runmode == RUNMODE_SAFE_MODE) {
safe_mode_on_next_reset(PROGRAMMATIC_SAFE_MODE);
safe_mode_on_next_reset(SAFE_MODE_PROGRAMMATIC);
}
}

View File

@ -71,7 +71,7 @@ safe_mode_t port_init(void) {
irq_setmask(0);
irq_setie(1);
tick_init();
return NO_SAFE_MODE;
return SAFE_MODE_NONE;
}
extern uint32_t _ebss;

View File

@ -52,12 +52,10 @@ void common_hal_mcu_disable_interrupts(void) {
nesting_count++;
}
void HardFault_Handler(void);
void common_hal_mcu_enable_interrupts(void) {
if (nesting_count == 0) {
// This is very very bad because it means there was mismatched disable/enables so we
// "HardFault".
HardFault_Handler();
// This is very very bad because it means there was mismatched disable/enables
reset_into_safe_mode(SAFE_MODE_INTERRUPT_ERROR);
}
nesting_count--;
if (nesting_count > 0) {
@ -80,7 +78,7 @@ void common_hal_mcu_on_next_reset(mcu_runmode_t runmode) {
DBL_TAP_REG = DBL_TAP_MAGIC_QUICK_BOOT;
}
if (runmode == RUNMODE_SAFE_MODE) {
safe_mode_on_next_reset(PROGRAMMATIC_SAFE_MODE);
safe_mode_on_next_reset(SAFE_MODE_PROGRAMMATIC);
}
}

View File

@ -259,10 +259,10 @@ safe_mode_t port_init(void) {
// run yet, which uses `never_reset` to protect critical pins from being reset by `reset_port`.
if (board_requests_safe_mode()) {
return USER_SAFE_MODE;
return SAFE_MODE_USER;
}
return NO_SAFE_MODE;
return SAFE_MODE_NONE;
}
void reset_port(void) {
@ -419,7 +419,7 @@ void port_idle_until_interrupt(void) {
*/
void MemManage_Handler(void);
__attribute__((used)) void MemManage_Handler(void) {
reset_into_safe_mode(MEM_MANAGE);
reset_into_safe_mode(SAFE_MODE_HARD_FAULT);
while (true) {
asm ("nop;");
}
@ -430,7 +430,7 @@ __attribute__((used)) void MemManage_Handler(void) {
*/
void BusFault_Handler(void);
__attribute__((used)) void BusFault_Handler(void) {
reset_into_safe_mode(MEM_MANAGE);
reset_into_safe_mode(SAFE_MODE_HARD_FAULT);
while (true) {
asm ("nop;");
}
@ -441,7 +441,7 @@ __attribute__((used)) void BusFault_Handler(void) {
*/
void UsageFault_Handler(void);
__attribute__((used)) void UsageFault_Handler(void) {
reset_into_safe_mode(MEM_MANAGE);
reset_into_safe_mode(SAFE_MODE_HARD_FAULT);
while (true) {
asm ("nop;");
}
@ -452,7 +452,7 @@ __attribute__((used)) void UsageFault_Handler(void) {
*/
void HardFault_Handler(void);
__attribute__((used)) void HardFault_Handler(void) {
reset_into_safe_mode(HARD_CRASH);
reset_into_safe_mode(SAFE_MODE_HARD_FAULT);
while (true) {
asm ("nop;");
}

View File

@ -52,7 +52,7 @@
#define CIRCUITPY_BOOT_BUTTON (&pin_P0_29)
#define BOARD_USER_SAFE_MODE_ACTION translate("The left button was pressed at start up.\n")
#define BOARD_USER_SAFE_MODE_ACTION translate("You pressed the left button at start up.")
#define CIRCUITPY_INTERNAL_NVM_SIZE (4096)

View File

@ -91,7 +91,7 @@ const nvm_bytearray_obj_t common_hal_bleio_nvm_obj = {
};
STATIC void softdevice_assert_handler(uint32_t id, uint32_t pc, uint32_t info) {
reset_into_safe_mode(NORDIC_SOFT_DEVICE_ASSERT);
reset_into_safe_mode(SAFE_MODE_SDK_FATAL_ERROR);
}
bleio_connection_internal_t bleio_connections[BLEIO_TOTAL_CONNECTION_COUNT];

View File

@ -68,9 +68,8 @@ void common_hal_mcu_disable_interrupts() {
void common_hal_mcu_enable_interrupts() {
if (nesting_count == 0) {
// This is very very bad because it means there was mismatched disable/enables so we
// crash.
reset_into_safe_mode(HARD_CRASH);
// This is very very bad because it means there was mismatched disable/enables.
reset_into_safe_mode(SAFE_MODE_INTERRUPT_ERROR);
}
nesting_count--;
if (nesting_count > 0) {
@ -88,7 +87,7 @@ void common_hal_mcu_on_next_reset(mcu_runmode_t runmode) {
sd_power_gpregret_set(0,0);
}
if (runmode == RUNMODE_SAFE_MODE) {
safe_mode_on_next_reset(PROGRAMMATIC_SAFE_MODE);
safe_mode_on_next_reset(SAFE_MODE_PROGRAMMATIC);
}
}

View File

@ -75,7 +75,7 @@ void port_internal_flash_flush(void) {
// Skip if data is the same
if (memcmp(_flash_cache, (void *)_flash_page_addr, FLASH_PAGE_SIZE) != 0) {
if (!nrf_nvm_safe_flash_page_write(_flash_page_addr, _flash_cache)) {
reset_into_safe_mode(FLASH_WRITE_FAIL);
reset_into_safe_mode(SAFE_MODE_FLASH_WRITE_FAIL);
}
}
}

View File

@ -77,7 +77,7 @@ extern void qspi_disable(void);
#endif
static void power_warning_handler(void) {
reset_into_safe_mode(BROWNOUT);
reset_into_safe_mode(SAFE_MODE_BROWNOUT);
}
uint32_t reset_reason_saved = 0;
@ -204,11 +204,11 @@ safe_mode_t port_init(void) {
// If USB is connected, then the user might be editing `code.py`,
// in which case we should reboot into Safe Mode.
if (usb_reg & POWER_USBREGSTATUS_VBUSDETECT_Msk) {
return WATCHDOG_RESET;
return SAFE_MODE_WATCHDOG;
}
}
return NO_SAFE_MODE;
return SAFE_MODE_NONE;
}
void reset_port(void) {
@ -399,7 +399,7 @@ void port_idle_until_interrupt(void) {
extern void HardFault_Handler(void);
void HardFault_Handler(void) {
reset_into_safe_mode(HARD_CRASH);
reset_into_safe_mode(SAFE_MODE_HARD_FAULT);
while (true) {
asm ("nop;");
}

View File

@ -60,7 +60,7 @@ void common_hal_mcu_disable_interrupts(void) {
void common_hal_mcu_enable_interrupts(void) {
if (nesting_count == 0) {
// reset_into_safe_mode(LOCKING_ERROR);
// reset_into_safe_mode(SAFE_MODE_INTERRUPT_ERROR);
}
nesting_count--;
if (nesting_count > 0) {
@ -79,7 +79,7 @@ void common_hal_mcu_on_next_reset(mcu_runmode_t runmode) {
next_reset_to_bootloader = true;
break;
case RUNMODE_SAFE_MODE:
safe_mode_on_next_reset(PROGRAMMATIC_SAFE_MODE);
safe_mode_on_next_reset(SAFE_MODE_PROGRAMMATIC);
break;
default:
break;

View File

@ -161,10 +161,10 @@ safe_mode_t port_init(void) {
}
#endif
if (board_requests_safe_mode()) {
return USER_SAFE_MODE;
return SAFE_MODE_USER;
}
return NO_SAFE_MODE;
return SAFE_MODE_NONE;
}
void reset_port(void) {
@ -312,7 +312,7 @@ __attribute__((used)) void HardFault_Handler(void) {
REG_MTB_MASTER = 0x00000000 + 6;
#endif
reset_into_safe_mode(HARD_CRASH);
reset_into_safe_mode(SAFE_MODE_HARD_FAULT);
while (true) {
asm ("nop;");
}

View File

@ -105,8 +105,7 @@ STATIC int next_filter(canio_can_obj_t *can) {
return i;
}
}
reset_into_safe_mode(MICROPY_FATAL_ERROR);
return -1;
mp_raise_msg_varg(&mp_type_RuntimeError, translate("%q"), MP_QSTR_Listener);
}
// IDE = "extended ID" flag of packet header. We always add this bit to the

View File

@ -61,9 +61,8 @@ void common_hal_mcu_disable_interrupts(void) {
void common_hal_mcu_enable_interrupts(void) {
if (nesting_count == 0) {
// This is very very bad because it means there was mismatched disable/enables so we
// "HardFault".
asm ("bkpt");
// This is very very bad because it means there was mismatched disable/enables.
reset_into_safe_mode(SAFE_MODE_INTERRUPT_ERROR);
}
nesting_count--;
if (nesting_count > 0) {
@ -77,7 +76,7 @@ static bool next_reset_to_bootloader = false;
void common_hal_mcu_on_next_reset(mcu_runmode_t runmode) {
if (runmode == RUNMODE_SAFE_MODE) {
safe_mode_on_next_reset(PROGRAMMATIC_SAFE_MODE);
safe_mode_on_next_reset(SAFE_MODE_PROGRAMMATIC);
}
if (runmode == RUNMODE_BOOTLOADER) {
next_reset_to_bootloader = true;

View File

@ -215,7 +215,7 @@ void port_internal_flash_flush(void) {
EraseInitStruct.NbSectors = 1;
#endif
if (sector_size > sizeof(_flash_cache) || sector_start_addr == 0xffffffff) {
reset_into_safe_mode(FLASH_WRITE_FAIL);
reset_into_safe_mode(SAFE_MODE_FLASH_WRITE_FAIL);
}
// Skip if data is the same
@ -228,7 +228,7 @@ void port_internal_flash_flush(void) {
if (HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError) != HAL_OK) {
// error occurred during sector erase
HAL_FLASH_Lock(); // lock the flash
reset_into_safe_mode(FLASH_WRITE_FAIL);
reset_into_safe_mode(SAFE_MODE_FLASH_WRITE_FAIL);
}
uint32_t *cache_addr = (uint32_t *)_flash_cache;
@ -240,7 +240,7 @@ void port_internal_flash_flush(void) {
(uint32_t)cache_addr) != HAL_OK) {
// error occurred during flash write
HAL_FLASH_Lock(); // lock the flash
reset_into_safe_mode(FLASH_WRITE_FAIL);
reset_into_safe_mode(SAFE_MODE_FLASH_WRITE_FAIL);
}
// RAM memory is by word (4 byte), but flash memory is by byte
cache_addr += 8;
@ -253,7 +253,7 @@ void port_internal_flash_flush(void) {
*(uint64_t *)cache_addr) != HAL_OK) {
// error occurred during flash write
HAL_FLASH_Lock(); // lock the flash
reset_into_safe_mode(FLASH_WRITE_FAIL);
reset_into_safe_mode(SAFE_MODE_FLASH_WRITE_FAIL);
}
// RAM memory is by word (4 byte), but flash memory is by byte
cache_addr += 2;
@ -267,7 +267,7 @@ void port_internal_flash_flush(void) {
(uint64_t)*cache_addr) != HAL_OK) {
// error occurred during flash write
HAL_FLASH_Lock(); // lock the flash
reset_into_safe_mode(FLASH_WRITE_FAIL);
reset_into_safe_mode(SAFE_MODE_FLASH_WRITE_FAIL);
}
// RAM memory is by word (4 byte), but flash memory is by byte
cache_addr += 1;
@ -335,7 +335,7 @@ mp_uint_t supervisor_flash_write_blocks(const uint8_t *src, uint32_t block_num,
// Fail for any sector outside what's supported by the cache
if (sector_size > sizeof(_flash_cache)) {
reset_into_safe_mode(FLASH_WRITE_FAIL);
reset_into_safe_mode(SAFE_MODE_FLASH_WRITE_FAIL);
}
// Find how many blocks are left in the sector

View File

@ -211,7 +211,7 @@ safe_mode_t port_init(void) {
// Turn off SysTick
SysTick->CTRL = 0;
return NO_SAFE_MODE;
return SAFE_MODE_NONE;
}
void HAL_Delay(uint32_t delay_ms) {
@ -357,28 +357,28 @@ uint32_t port_get_saved_word(void) {
}
__attribute__((used)) void MemManage_Handler(void) {
reset_into_safe_mode(MEM_MANAGE);
reset_into_safe_mode(SAFE_MODE_HARD_FAULT);
while (true) {
asm ("nop;");
}
}
__attribute__((used)) void BusFault_Handler(void) {
reset_into_safe_mode(MEM_MANAGE);
reset_into_safe_mode(SAFE_MODE_HARD_FAULT);
while (true) {
asm ("nop;");
}
}
__attribute__((used)) void UsageFault_Handler(void) {
reset_into_safe_mode(MEM_MANAGE);
reset_into_safe_mode(SAFE_MODE_HARD_FAULT);
while (true) {
asm ("nop;");
}
}
__attribute__((used)) void HardFault_Handler(void) {
reset_into_safe_mode(HARD_CRASH);
reset_into_safe_mode(SAFE_MODE_HARD_FAULT);
while (true) {
asm ("nop;");
}

View File

@ -530,6 +530,11 @@ $(filter $(SRC_PATTERNS), \
wifi/Packet.c \
)
ifeq ($(CIRCUITPY_SAFEMODE_PY),1)
SRC_BINDINGS_ENUMS += \
supervisor/SafeModeReason.c
endif
SRC_BINDINGS_ENUMS += \
util.c

View File

@ -372,6 +372,10 @@ CFLAGS += -DCIRCUITPY_ROTARYIO_SOFTENCODER=$(CIRCUITPY_ROTARYIO_SOFTENCODER)
CIRCUITPY_RTC ?= 1
CFLAGS += -DCIRCUITPY_RTC=$(CIRCUITPY_RTC)
# Enable support for safemode.py
CIRCUITPY_SAFEMODE_PY ?= 1
CFLAGS += -DCIRCUITPY_SAFEMODE_PY=$(CIRCUITPY_SAFEMODE_PY)
# CIRCUITPY_SAMD is handled in the atmel-samd tree.
# Only for SAMD chips.
# Assume not a SAMD build.

View File

@ -518,7 +518,7 @@ void *gc_alloc(size_t n_bytes, unsigned int alloc_flags, bool long_lived) {
}
if (MP_STATE_MEM(gc_pool_start) == 0) {
reset_into_safe_mode(GC_ALLOC_OUTSIDE_VM);
reset_into_safe_mode(SAFE_MODE_GC_ALLOC_OUTSIDE_VM);
}
GC_ENTER();
@ -712,7 +712,7 @@ void gc_free(void *ptr) {
GC_EXIT();
} else {
if (MP_STATE_MEM(gc_pool_start) == 0) {
reset_into_safe_mode(GC_ALLOC_OUTSIDE_VM);
reset_into_safe_mode(SAFE_MODE_GC_ALLOC_OUTSIDE_VM);
}
// get the GC block number corresponding to this pointer
assert(VERIFY_PTR(ptr));

View File

@ -42,7 +42,7 @@ MAKE_ENUM_VALUE(mcu_reset_reason_type, reset_reason, RESCUE_DEBUG, RESET_REASON_
//| """The reason the microntroller was last reset"""
//|
//| POWER_ON: object
//| """The microntroller was started from power off."""
//| """The microcontroller was started from power off."""
//|
//| BROWNOUT: object
//| """The microntroller was reset due to too low a voltage."""

View File

@ -32,6 +32,7 @@
#include "shared-bindings/supervisor/RunReason.h"
#include "shared-bindings/supervisor/Runtime.h"
#include "shared-bindings/supervisor/SafeModeReason.h"
#include "supervisor/shared/reload.h"
#include "supervisor/shared/stack.h"
@ -106,7 +107,7 @@ void supervisor_set_run_reason(supervisor_run_reason_t run_reason) {
}
//| run_reason: RunReason
//| """Why CircuitPython started running this particular time."""
//| """Why CircuitPython started running this particular time (read-only)."""
STATIC mp_obj_t supervisor_runtime_get_run_reason(mp_obj_t self) {
return cp_enum_find(&supervisor_run_reason_type, _run_reason);
}
@ -115,6 +116,23 @@ MP_DEFINE_CONST_FUN_OBJ_1(supervisor_runtime_get_run_reason_obj, supervisor_runt
MP_PROPERTY_GETTER(supervisor_runtime_run_reason_obj,
(mp_obj_t)&supervisor_runtime_get_run_reason_obj);
//| safe_mode_reason: SafeModeReason
//| """Why CircuitPython went into safe mode this particular time (read-only).
//|
//| **Limitations**: Raises ``NotImplementedError`` on builds that do not implement ``safemode.py``.
//| """
STATIC mp_obj_t supervisor_runtime_get_safe_mode_reason(mp_obj_t self) {
#if CIRCUITPY_SAFEMODE_PY
return cp_enum_find(&supervisor_safe_mode_reason_type, get_safe_mode());
#else
mp_raise_NotImplementedError(NULL);
#endif
}
MP_DEFINE_CONST_FUN_OBJ_1(supervisor_runtime_get_safe_mode_reason_obj, supervisor_runtime_get_safe_mode_reason);
MP_PROPERTY_GETTER(supervisor_runtime_safe_mode_reason_obj,
(mp_obj_t)&supervisor_runtime_get_safe_mode_reason_obj);
//| autoreload: bool
//| """Whether CircuitPython may autoreload based on workflow writes to the filesystem."""
//|
@ -218,6 +236,7 @@ STATIC const mp_rom_map_elem_t supervisor_runtime_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_serial_connected), MP_ROM_PTR(&supervisor_runtime_serial_connected_obj) },
{ MP_ROM_QSTR(MP_QSTR_serial_bytes_available), MP_ROM_PTR(&supervisor_runtime_serial_bytes_available_obj) },
{ MP_ROM_QSTR(MP_QSTR_run_reason), MP_ROM_PTR(&supervisor_runtime_run_reason_obj) },
{ MP_ROM_QSTR(MP_QSTR_safe_mode_reason), MP_ROM_PTR(&supervisor_runtime_safe_mode_reason_obj) },
{ MP_ROM_QSTR(MP_QSTR_autoreload), MP_ROM_PTR(&supervisor_runtime_autoreload_obj) },
{ MP_ROM_QSTR(MP_QSTR_ble_workflow), MP_ROM_PTR(&supervisor_runtime_ble_workflow_obj) },
{ MP_ROM_QSTR(MP_QSTR_next_stack_limit), MP_ROM_PTR(&supervisor_runtime_next_stack_limit_obj) },

View File

@ -31,12 +31,16 @@
#include "py/obj.h"
#include "shared-bindings/supervisor/RunReason.h"
#include "shared-bindings/supervisor/SafeModeReason.h"
extern const mp_obj_type_t supervisor_runtime_type;
supervisor_run_reason_t supervisor_get_run_reason(void);
void supervisor_set_run_reason(supervisor_run_reason_t run_reason);
safe_mode_t supervisor_get_safe_mode(void);
void supervisor_set_safe_mode(safe_mode_t safe_mode);
bool common_hal_supervisor_runtime_get_serial_connected(void);
bool common_hal_supervisor_runtime_get_serial_bytes_available(void);

View File

@ -0,0 +1,158 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2023 Dan Halbert 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 "py/enum.h"
#include "shared-bindings/supervisor/SafeModeReason.h"
// Reuse the non-Python safe_mode_t enum
#include "supervisor/shared/safe_mode.h"
MAKE_ENUM_VALUE(supervisor_safe_mode_reason_type, safe_mode_reason, NONE, SAFE_MODE_NONE);
MAKE_ENUM_VALUE(supervisor_safe_mode_reason_type, safe_mode_reason, BROWNOUT, SAFE_MODE_BROWNOUT);
// alphabetical from here down
MAKE_ENUM_VALUE(supervisor_safe_mode_reason_type, safe_mode_reason, FLASH_WRITE_FAIL, SAFE_MODE_FLASH_WRITE_FAIL);
MAKE_ENUM_VALUE(supervisor_safe_mode_reason_type, safe_mode_reason, GC_ALLOC_OUTSIDE_VM, SAFE_MODE_GC_ALLOC_OUTSIDE_VM);
MAKE_ENUM_VALUE(supervisor_safe_mode_reason_type, safe_mode_reason, HARD_FAULT, SAFE_MODE_HARD_FAULT);
MAKE_ENUM_VALUE(supervisor_safe_mode_reason_type, safe_mode_reason, INTERRUPT_ERROR, SAFE_MODE_INTERRUPT_ERROR);
MAKE_ENUM_VALUE(supervisor_safe_mode_reason_type, safe_mode_reason, NLR_JUMP_FAIL, SAFE_MODE_NLR_JUMP_FAIL);
MAKE_ENUM_VALUE(supervisor_safe_mode_reason_type, safe_mode_reason, NO_CIRCUITPY, SAFE_MODE_NO_CIRCUITPY);
MAKE_ENUM_VALUE(supervisor_safe_mode_reason_type, safe_mode_reason, NO_HEAP, SAFE_MODE_NO_HEAP);
MAKE_ENUM_VALUE(supervisor_safe_mode_reason_type, safe_mode_reason, PROGRAMMATIC, SAFE_MODE_PROGRAMMATIC);
MAKE_ENUM_VALUE(supervisor_safe_mode_reason_type, safe_mode_reason, SDK_FATAL_ERROR, SAFE_MODE_SDK_FATAL_ERROR);
MAKE_ENUM_VALUE(supervisor_safe_mode_reason_type, safe_mode_reason, STACK_OVERFLOW, SAFE_MODE_STACK_OVERFLOW);
MAKE_ENUM_VALUE(supervisor_safe_mode_reason_type, safe_mode_reason, USB_BOOT_DEVICE_NOT_INTERFACE_ZERO, SAFE_MODE_USB_BOOT_DEVICE_NOT_INTERFACE_ZERO);
MAKE_ENUM_VALUE(supervisor_safe_mode_reason_type, safe_mode_reason, USB_TOO_MANY_ENDPOINTS, SAFE_MODE_USB_TOO_MANY_ENDPOINTS);
MAKE_ENUM_VALUE(supervisor_safe_mode_reason_type, safe_mode_reason, USB_TOO_MANY_INTERFACE_NAMES, SAFE_MODE_USB_TOO_MANY_INTERFACE_NAMES);
MAKE_ENUM_VALUE(supervisor_safe_mode_reason_type, safe_mode_reason, USER, SAFE_MODE_USER);
MAKE_ENUM_VALUE(supervisor_safe_mode_reason_type, safe_mode_reason, WATCHDOG, SAFE_MODE_WATCHDOG);
//| class SafeModeReason:
//| """The reason that CircuitPython went into safe mode.
//|
//| **Limitations**: Class not available on builds that do not implement ``safemode.py``.
//| """
//|
MAKE_ENUM_MAP(supervisor_safe_mode_reason) {
//| NONE: object
//| """CircuitPython is not in safe mode."""
//|
MAKE_ENUM_MAP_ENTRY(safe_mode_reason, NONE),
//| BROWNOUT: object
//| """The microcontroller voltage dropped too low."""
//|
MAKE_ENUM_MAP_ENTRY(safe_mode_reason, BROWNOUT),
// alphabetical from here down
//| FLASH_WRITE_FAIL: object
//| """Could not write to flash memory."""
//|
MAKE_ENUM_MAP_ENTRY(safe_mode_reason, FLASH_WRITE_FAIL),
//| GC_ALLOC_OUTSIDE_VM: object
//| """CircuitPython tried to allocate storage when its virtual machine was not running."""
//|
MAKE_ENUM_MAP_ENTRY(safe_mode_reason, GC_ALLOC_OUTSIDE_VM),
//| HARD_FAULT: object
//| """The microcontroller detected a fault, such as an out-of-bounds memory write."""
//|
MAKE_ENUM_MAP_ENTRY(safe_mode_reason, HARD_FAULT),
//| INTERRUPT_ERROR: object
//| """Internal error related to interrupts."""
//|
MAKE_ENUM_MAP_ENTRY(safe_mode_reason, INTERRUPT_ERROR),
//| NLR_JUMP_FAIL: object
//| """An error occurred during exception handling, possibly due to memory corruption."""
//|
MAKE_ENUM_MAP_ENTRY(safe_mode_reason, NLR_JUMP_FAIL),
//| NO_CIRCUITPY: object
//| """The CIRCUITPY drive was not available."""
//|
MAKE_ENUM_MAP_ENTRY(safe_mode_reason, NO_CIRCUITPY),
//| NO_HEAP: object
//| """Heap storage was not present."""
//|
MAKE_ENUM_MAP_ENTRY(safe_mode_reason, NO_HEAP),
//| PROGRAMMATIC: object
//| """The program entered safe mode using the `supervisor` module."""
//|
MAKE_ENUM_MAP_ENTRY(safe_mode_reason, PROGRAMMATIC),
//| SDK_FATAL_ERROR: object
//| """Third party firmware reported a fatal error."""
//|
MAKE_ENUM_MAP_ENTRY(safe_mode_reason, SDK_FATAL_ERROR),
//| STACK_OVERFLOW: object
//| """The CircuitPython heap was corrupted because the stack was too small."""
//|
MAKE_ENUM_MAP_ENTRY(safe_mode_reason, STACK_OVERFLOW),
//| USB_BOOT_DEVICE_NOT_INTERFACE_ZERO: object
//| """The USB HID boot device was not set up to be the first device, on interface #0."""
//|
MAKE_ENUM_MAP_ENTRY(safe_mode_reason, USB_BOOT_DEVICE_NOT_INTERFACE_ZERO),
//| USB_TOO_MANY_ENDPOINTS: object
//| """USB devices need more endpoints than are available."""
//|
MAKE_ENUM_MAP_ENTRY(safe_mode_reason, USB_TOO_MANY_ENDPOINTS),
//| USB_TOO_MANY_INTERFACE_NAMES: object
//| """USB devices specify too many interface names."""
//|
MAKE_ENUM_MAP_ENTRY(safe_mode_reason, USB_TOO_MANY_INTERFACE_NAMES),
//| USER: object
//| """The user pressed one or more buttons to enter safe mode.
//| This safe mode does **not** cause ``safemode.py`` to be run, since its purpose
//| is to prevent all user code from running.
//| This allows errors in ``safemode.py`` to be corrected easily.
//| """
//|
MAKE_ENUM_MAP_ENTRY(safe_mode_reason, USER),
//| SAFE_MODE_WATCHDOG: object
//| """An internal watchdog timer expired."""
//|
MAKE_ENUM_MAP_ENTRY(safe_mode_reason, WATCHDOG),
};
STATIC MP_DEFINE_CONST_DICT(supervisor_safe_mode_reason_locals_dict, supervisor_safe_mode_reason_locals_table);
MAKE_PRINTER(supervisor, supervisor_safe_mode_reason);
MAKE_ENUM_TYPE(supervisor, SafeModeReason, supervisor_safe_mode_reason);

View File

@ -0,0 +1,31 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2023 Dan Halbert 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.
*/
#pragma once
#include "supervisor/shared/safe_mode.h"
extern const mp_obj_type_t supervisor_safe_mode_reason_type;

View File

@ -336,6 +336,11 @@ STATIC const mp_rom_map_elem_t supervisor_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_runtime), MP_ROM_PTR(&common_hal_supervisor_runtime_obj) },
{ MP_ROM_QSTR(MP_QSTR_reload), MP_ROM_PTR(&supervisor_reload_obj) },
{ MP_ROM_QSTR(MP_QSTR_RunReason), MP_ROM_PTR(&supervisor_run_reason_type) },
#if CIRCUITPY_SAFEMODE_PY
{ MP_ROM_QSTR(MP_QSTR_SafeModeReason), MP_ROM_PTR(&supervisor_safe_mode_reason_type) },
#else
{ MP_ROM_QSTR(MP_QSTR_SafeModeReason), MP_ROM_NONE },
#endif
{ MP_ROM_QSTR(MP_QSTR_set_next_code_file), MP_ROM_PTR(&supervisor_set_next_code_file_obj) },
{ MP_ROM_QSTR(MP_QSTR_ticks_ms), MP_ROM_PTR(&supervisor_ticks_ms_obj) },
{ MP_ROM_QSTR(MP_QSTR_get_previous_traceback), MP_ROM_PTR(&supervisor_get_previous_traceback_obj) },

View File

@ -1,99 +0,0 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2017 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.
*/
#ifndef MICROPY_SUPERVISOR_MESSAGES_DEFAULT_H
#define MICROPY_SUPERVISOR_MESSAGES_DEFAULT_H
#ifndef MSG_OUTPUT_SUFFIX
#define MSG_OUTPUT_SUFFIX " output:\r\n"
#endif
#ifndef MSG_NEWLINE
#define MSG_NEWLINE "\r\n"
#endif
#ifndef MSG_AUTORELOAD_ON
#define MSG_AUTORELOAD_ON "Auto-reload is on. Simply save files over USB to run them or enter REPL to disable.\r\n"
#endif
#ifndef MSG_AUTORELOAD_OFF
#define MSG_AUTORELOAD_OFF "Auto-reload is off.\r\n"
#endif
#ifndef MSG_SAFE_MODE_ON
#define MSG_SAFE_MODE_ON "Running in safe mode! Auto-reload is off.\r\n"
#endif
#ifndef MSG_SAFE_MODE_NO_MAIN
#define MSG_SAFE_MODE_NO_MAIN "Running in safe mode! Not running saved code.\r\n"
#endif
#ifndef MSG_SAFE_MODE_USER_REQUESTED
#define MSG_SAFE_MODE_USER_REQUESTED "You requested starting safe mode by "
#endif
#ifndef MSG_SAFE_MODE_USER_EXIT
#define MSG_SAFE_MODE_USER_EXIT "To exit, please reset the board without "
#endif
#ifndef MSG_BAD_SAFE_MODE
#define MSG_BAD_SAFE_MODE "You are running in safe mode which means something really bad happened."
#endif
#ifndef MSG_SAFE_MODE_CRASH
#define MSG_SAFE_MODE_CRASH "Looks like our core CircuitPython code crashed hard. Whoops!"
#endif
#ifndef MSG_SAFE_MODE_FILE_ISSUE
#define MSG_SAFE_MODE_FILE_ISSUE "Please file an issue here with the contents of your CIRCUITPY drive:"
#endif
#ifndef MSG_SAFE_MODE_ISSUE_LINK
#define MSG_SAFE_MODE_ISSUE_LINK "https://github.com/adafruit/circuitpython/issues"
#endif
#ifndef MSG_SAFE_MODE_BROWN_OUT_LINE_1
#define MSG_SAFE_MODE_BROWN_OUT_LINE_1 "The microcontroller's power dipped. Please make sure your power supply provides"
#endif
#ifndef MSG_SAFE_MODE_BROWN_OUT_LINE_2
#define MSG_SAFE_MODE_BROWN_OUT_LINE_2 "enough power for the whole circuit and press reset (after ejecting CIRCUITPY)."
#endif
#ifndef MSG_WAIT_BEFORE_REPL
#define MSG_WAIT_BEFORE_REPL "Press any key to enter the REPL. Use CTRL-D to reload."
#endif
// Be careful, some tools depend on this.
#ifndef MSG_SOFT_REBOOT
#define MSG_SOFT_REBOOT "soft reboot"
#endif
#ifndef MSG_DOUBLE_FILE_EXTENSION
#define MSG_DOUBLE_FILE_EXTENSION "WARNING: Your code filename has two extensions\r\n"
#endif
#endif // MICROPY_SUPERVISOR_MESSAGES_DEFAULT_H

View File

@ -43,20 +43,28 @@
#define SAFE_MODE_DATA_GUARD 0xad0000af
#define SAFE_MODE_DATA_GUARD_MASK 0xff0000ff
static safe_mode_t current_safe_mode;
static safe_mode_t _safe_mode;
safe_mode_t get_safe_mode(void) {
return _safe_mode;
}
void set_safe_mode(safe_mode_t safe_mode) {
_safe_mode = 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;
safe_mode_t safe_mode = SAFE_MODE_NONE;
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) {
if (safe_mode != SAFE_MODE_NONE) {
port_set_saved_word(SAFE_MODE_DATA_GUARD);
current_safe_mode = safe_mode;
_safe_mode = safe_mode;
return safe_mode;
} else {
current_safe_mode = 0;
_safe_mode = SAFE_MODE_NONE;
}
const mcu_reset_reason_t reset_reason = common_hal_mcu_processor_get_reset_reason();
@ -64,12 +72,12 @@ safe_mode_t wait_for_safe_mode_reset(void) {
reset_reason != RESET_REASON_RESET_PIN &&
reset_reason != RESET_REASON_UNKNOWN &&
reset_reason != RESET_REASON_SOFTWARE) {
return NO_SAFE_MODE;
return SAFE_MODE_NONE;
}
#if CIRCUITPY_SKIP_SAFE_MODE_WAIT
return NO_SAFE_MODE;
return SAFE_MODE_NONE;
#endif
port_set_saved_word(SAFE_MODE_DATA_GUARD | (MANUAL_SAFE_MODE << 8));
port_set_saved_word(SAFE_MODE_DATA_GUARD | (SAFE_MODE_USER << 8));
// Wait for a while to allow for reset.
#if CIRCUITPY_STATUS_LED
@ -106,11 +114,11 @@ safe_mode_t wait_for_safe_mode_reset(void) {
status_led_deinit();
#endif
if (boot_in_safe_mode) {
return USER_SAFE_MODE;
return SAFE_MODE_USER;
}
// Restore the original state of the saved word if no reset occured during our wait period.
port_set_saved_word(reset_state);
return NO_SAFE_MODE;
return SAFE_MODE_NONE;
}
void safe_mode_on_next_reset(safe_mode_t reason) {
@ -119,7 +127,7 @@ void safe_mode_on_next_reset(safe_mode_t reason) {
// Don't inline this so it's easy to break on it from GDB.
void __attribute__((noinline,)) reset_into_safe_mode(safe_mode_t reason) {
if (current_safe_mode > BROWNOUT && reason > BROWNOUT) {
if (_safe_mode > SAFE_MODE_BROWNOUT && reason > SAFE_MODE_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.
@ -133,105 +141,95 @@ void __attribute__((noinline,)) reset_into_safe_mode(safe_mode_t reason) {
void print_safe_mode_message(safe_mode_t reason) {
if (reason == NO_SAFE_MODE) {
if (reason == SAFE_MODE_NONE) {
return;
}
serial_write("\r\n");
serial_write_compressed(translate("You are in safe mode because:\n"));
serial_write_compressed(translate("\nYou are in safe mode because:\n"));
const compressed_string_t *message = NULL;
// First check for safe mode reasons that do not necessarily reflect bugs.
switch (reason) {
case USER_SAFE_MODE:
case SAFE_MODE_BROWNOUT:
message = translate("The power dipped. Make sure you are providing enough power.");
break;
case SAFE_MODE_USER:
#if defined(BOARD_USER_SAFE_MODE_ACTION)
message = BOARD_USER_SAFE_MODE_ACTION;
#elif defined(CIRCUITPY_BOOT_BUTTON)
message = translate("The BOOT button was pressed at start up.\n");
#endif
#if defined(BOARD_USER_SAFE_MODE_ACTION) || defined(CIRCUITPY_BOOT_BUTTON)
// Output a user safe mode string if it's set.
serial_write_compressed(message);
message = translate("To exit, please reset the board without requesting safe mode.");
// The final piece is printed below.
message = translate("You pressed the BOOT button at start up");
#else
message = translate("You pressed the reset button during boot.");
#endif
break;
case MANUAL_SAFE_MODE:
message = translate("You pressed the reset button during boot. Press again to exit safe mode.");
case SAFE_MODE_NO_CIRCUITPY:
message = translate("CIRCUITPY drive could not be found or created.");
break;
case PROGRAMMATIC_SAFE_MODE:
message = translate("The `microcontroller` module was used to boot into safe mode. Press reset to exit safe mode.");
case SAFE_MODE_PROGRAMMATIC:
message = translate("The `microcontroller` module was used to boot into safe mode.");
break;
case BROWNOUT:
message = translate("The microcontroller's power dipped. Make sure your power supply provides\nenough power for the whole circuit and press reset (after ejecting CIRCUITPY).");
#if CIRCUITPY_SAFEMODE_PY
case SAFE_MODE_SAFEMODE_PY_ERROR:
message = translate("Error in safemode.py.");
break;
case USB_TOO_MANY_ENDPOINTS:
#endif
case SAFE_MODE_STACK_OVERFLOW:
message = translate("Heap was corrupted because the stack was too small. Increase stack size.");
break;
case SAFE_MODE_USB_TOO_MANY_ENDPOINTS:
message = translate("USB devices need more endpoints than are available.");
break;
case USB_TOO_MANY_INTERFACE_NAMES:
case SAFE_MODE_USB_TOO_MANY_INTERFACE_NAMES:
message = translate("USB devices specify too many interface names.");
break;
case USB_BOOT_DEVICE_NOT_INTERFACE_ZERO:
message = translate("Boot device must be first device (interface #0).");
case SAFE_MODE_USB_BOOT_DEVICE_NOT_INTERFACE_ZERO:
message = translate("Boot device must be first (interface #0).");
break;
case WATCHDOG_RESET:
case SAFE_MODE_WATCHDOG:
message = translate("Internal watchdog timer expired.");
break;
case NO_CIRCUITPY:
message = translate("CIRCUITPY drive could not be found or created.");
break;
default:
break;
}
if (message) {
// Non-crash safe mode.
serial_write_compressed(message);
serial_write("\r\n");
return;
} else {
// Something worse happened.
serial_write_compressed(translate("CircuitPython core code crashed hard. Whoops!\n"));
switch (reason) {
case SAFE_MODE_GC_ALLOC_OUTSIDE_VM:
message = translate("Heap allocation when VM not running.");
break;
case SAFE_MODE_FLASH_WRITE_FAIL:
message = translate("Failed to write internal flash.");
break;
case SAFE_MODE_HARD_FAULT:
message = translate("Fault detected by hardware.");
break;
case SAFE_MODE_INTERRUPT_ERROR:
message = translate("Interrupt error.");
break;
case SAFE_MODE_NLR_JUMP_FAIL:
message = translate("NLR jump failed. Likely memory corruption.");
break;
case SAFE_MODE_NO_HEAP:
message = translate("Unable to allocate the heap.");
break;
case SAFE_MODE_SDK_FATAL_ERROR:
message = translate("Third-party firmware fatal error.");
break;
default:
message = translate("Unknown reason.");
break;
}
serial_write_compressed(message);
serial_write_compressed(translate("\nPlease file an issue with your program at https://github.com/adafruit/circuitpython/issues."));
}
// Something worse happened.
serial_write_compressed(translate("CircuitPython core code crashed hard. Whoops!\n"));
switch (reason) {
case HARD_CRASH:
message = translate("Crash into the HardFault_Handler.");
break;
case MICROPY_NLR_JUMP_FAIL:
message = translate("NLR jump failed. Likely memory corruption.");
break;
case MICROPY_FATAL_ERROR:
message = translate("Fatal error.");
break;
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.\nIncrease the stack size if you know how. If not:");
break;
case GC_ALLOC_OUTSIDE_VM:
message = translate("Attempted heap allocation when 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:
message = translate("Nordic system firmware failure assertion.");
break;
#endif
case FLASH_WRITE_FAIL:
message = translate("Failed to write internal flash.");
break;
case MEM_MANAGE:
message = translate("Invalid memory access.");
break;
default:
message = translate("Unknown reason.");
break;
}
serial_write_compressed(message);
serial_write_compressed(translate("\nPlease file an issue with the contents of your CIRCUITPY drive at \nhttps://github.com/adafruit/circuitpython/issues\n"));
// Always tell user how to get out of safe mode.
serial_write_compressed(translate("\nPress reset to exit safe mode.\n"));
}

View File

@ -30,27 +30,32 @@
#include "py/mpconfig.h"
typedef enum {
NO_SAFE_MODE = 0,
BROWNOUT,
HARD_CRASH,
USER_SAFE_MODE,
HEAP_OVERWRITTEN,
MANUAL_SAFE_MODE,
MICROPY_NLR_JUMP_FAIL,
MICROPY_FATAL_ERROR,
GC_ALLOC_OUTSIDE_VM,
PROGRAMMATIC_SAFE_MODE,
NORDIC_SOFT_DEVICE_ASSERT,
FLASH_WRITE_FAIL,
MEM_MANAGE,
WATCHDOG_RESET,
USB_TOO_MANY_ENDPOINTS,
USB_TOO_MANY_INTERFACE_NAMES,
USB_BOOT_DEVICE_NOT_INTERFACE_ZERO,
NO_HEAP,
NO_CIRCUITPY,
SAFE_MODE_NONE = 0,
// BROWNOUT should be lowest after SAFE_MODE_NONE.
SAFE_MODE_BROWNOUT,
// alphabetical from here down
SAFE_MODE_FLASH_WRITE_FAIL,
SAFE_MODE_GC_ALLOC_OUTSIDE_VM,
SAFE_MODE_HARD_FAULT,
SAFE_MODE_INTERRUPT_ERROR,
SAFE_MODE_MANUAL,
SAFE_MODE_NLR_JUMP_FAIL,
SAFE_MODE_NO_CIRCUITPY,
SAFE_MODE_NO_HEAP,
SAFE_MODE_PROGRAMMATIC,
SAFE_MODE_SAFEMODE_PY_ERROR,
SAFE_MODE_SDK_FATAL_ERROR,
SAFE_MODE_STACK_OVERFLOW,
SAFE_MODE_USB_BOOT_DEVICE_NOT_INTERFACE_ZERO,
SAFE_MODE_USB_TOO_MANY_ENDPOINTS,
SAFE_MODE_USB_TOO_MANY_INTERFACE_NAMES,
SAFE_MODE_USER,
SAFE_MODE_WATCHDOG,
} safe_mode_t;
safe_mode_t get_safe_mode(void);
void set_safe_mode(safe_mode_t safe_mode);
safe_mode_t wait_for_safe_mode_reset(void);
void safe_mode_on_next_reset(safe_mode_t reason);

View File

@ -74,7 +74,7 @@ inline bool stack_ok(void) {
inline void assert_heap_ok(void) {
if (!stack_ok()) {
reset_into_safe_mode(HEAP_OVERWRITTEN);
reset_into_safe_mode(SAFE_MODE_STACK_OVERFLOW);
}
}

View File

@ -228,7 +228,7 @@ static void usb_build_configuration_descriptor(void) {
if (usb_hid_boot_device() > 0 && descriptor_counts.current_interface > 0) {
// Hosts using boot devices generally to expect them to be at interface zero,
// and will not work properly otherwise.
reset_into_safe_mode(USB_BOOT_DEVICE_NOT_INTERFACE_ZERO);
reset_into_safe_mode(SAFE_MODE_USB_BOOT_DEVICE_NOT_INTERFACE_ZERO);
}
descriptor_buf_remaining += usb_hid_add_descriptor(
descriptor_buf_remaining, &descriptor_counts, &current_interface_string,
@ -258,14 +258,14 @@ static void usb_build_configuration_descriptor(void) {
if (descriptor_counts.current_endpoint > USB_NUM_ENDPOINT_PAIRS ||
descriptor_counts.num_in_endpoints > USB_NUM_IN_ENDPOINTS ||
descriptor_counts.num_out_endpoints > USB_NUM_OUT_ENDPOINTS) {
reset_into_safe_mode(USB_TOO_MANY_ENDPOINTS);
reset_into_safe_mode(SAFE_MODE_USB_TOO_MANY_ENDPOINTS);
}
}
// str must not be on the heap.
void usb_add_interface_string(uint8_t interface_string_index, const char str[]) {
if (interface_string_index > MAX_INTERFACE_STRINGS) {
reset_into_safe_mode(USB_TOO_MANY_INTERFACE_NAMES);
reset_into_safe_mode(SAFE_MODE_USB_TOO_MANY_INTERFACE_NAMES);
}
collected_interface_strings[interface_string_index].char_str = str;

View File

@ -29,7 +29,7 @@
#include <stdlib.h>
safe_mode_t wait_for_safe_mode_reset(void) {
return NO_SAFE_MODE;
return SAFE_MODE_NONE;
}
void reset_into_safe_mode(safe_mode_t reason) {