From 1e225610ccbbfe4de53735d2ef86ecedc7fd3b36 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Mon, 2 Aug 2021 18:37:19 -0700 Subject: [PATCH 1/2] Add ability to disable BLE workflow Call `supervisor.disable_ble_workflow()` and the BLE workflow will be disabled until the chip is reset. This also includes a couple fixes: 1. Terminals can now be deinit by setting the tilegrid to NULL. This prevents using the tilegrid before display is init. 2. Fix BLE serial send amount when sending more than a single packet. Fixes #5049 --- main.c | 1 + ports/nrf/Makefile | 2 +- shared-bindings/supervisor/__init__.c | 13 ++ shared-module/terminalio/Terminal.c | 6 +- supervisor/shared/bluetooth/bluetooth.c | 55 ++++++- supervisor/shared/bluetooth/bluetooth.h | 4 + supervisor/shared/bluetooth/serial.c | 2 +- supervisor/shared/display.c | 1 + tools/gen_display_resources.py | 2 +- tools/safe_mode_finder.py | 181 ++++++++++++++++++++++++ 10 files changed, 259 insertions(+), 8 deletions(-) create mode 100644 tools/safe_mode_finder.py diff --git a/main.c b/main.c index 13caf4beb7..1e6ecedf55 100755 --- a/main.c +++ b/main.c @@ -830,6 +830,7 @@ int __attribute__((used)) main(void) { serial_init(); #if CIRCUITPY_BLEIO + supervisor_bluetooth_enable_workflow(); supervisor_start_bluetooth(); #endif diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile index ce74f5d203..30c257cc50 100755 --- a/ports/nrf/Makefile +++ b/ports/nrf/Makefile @@ -120,7 +120,7 @@ CFLAGS += \ # TODO: check this CFLAGS += -D__START=main -LDFLAGS = $(CFLAGS) -nostartfiles -Wl,-nostdlib -Wl,-T,$(GENERATED_LD_FILE) -Wl,-Map=$@.map -Wl,-cref -Wl,-gc-sections -specs=nano.specs +LDFLAGS = $(CFLAGS) -nostartfiles -Wl,-nostdlib -Wl,-z,max-page-size=0x1000 -Wl,-T,$(GENERATED_LD_FILE) -Wl,-Map=$@.map -Wl,-cref -Wl,-gc-sections -specs=nano.specs LIBS := -lgcc -lc LDFLAGS += -mthumb -mcpu=cortex-m4 diff --git a/shared-bindings/supervisor/__init__.c b/shared-bindings/supervisor/__init__.c index 055b3a9084..0d73c2eb86 100644 --- a/shared-bindings/supervisor/__init__.c +++ b/shared-bindings/supervisor/__init__.c @@ -32,6 +32,7 @@ #include "lib/utils/interrupt_char.h" #include "supervisor/shared/autoreload.h" +#include "supervisor/shared/bluetooth/bluetooth.h" #include "supervisor/shared/status_leds.h" #include "supervisor/shared/stack.h" #include "supervisor/shared/traceback.h" @@ -283,6 +284,17 @@ STATIC mp_obj_t supervisor_get_previous_traceback(void) { } MP_DEFINE_CONST_FUN_OBJ_0(supervisor_get_previous_traceback_obj, supervisor_get_previous_traceback); +//| def disable_ble_workflow() -> None: +//| """Disable ble workflow until a reset. This prevents BLE advertising outside of the VM and +//| the services used for it.""" +//| ... +//| +STATIC mp_obj_t supervisor_disable_ble_workflow(void) { + supervisor_bluetooth_disable_workflow(); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_0(supervisor_disable_ble_workflow_obj, supervisor_disable_ble_workflow); + STATIC const mp_rom_map_elem_t supervisor_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_supervisor) }, { MP_ROM_QSTR(MP_QSTR_enable_autoreload), MP_ROM_PTR(&supervisor_enable_autoreload_obj) }, @@ -295,6 +307,7 @@ STATIC const mp_rom_map_elem_t supervisor_module_globals_table[] = { { 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) }, + { MP_ROM_QSTR(MP_QSTR_disable_ble_workflow), MP_ROM_PTR(&supervisor_disable_ble_workflow_obj) }, }; STATIC MP_DEFINE_CONST_DICT(supervisor_module_globals, supervisor_module_globals_table); diff --git a/shared-module/terminalio/Terminal.c b/shared-module/terminalio/Terminal.c index 8e4979ecad..48010aa650 100644 --- a/shared-module/terminalio/Terminal.c +++ b/shared-module/terminalio/Terminal.c @@ -46,6 +46,10 @@ void common_hal_terminalio_terminal_construct(terminalio_terminal_obj_t *self, d } size_t common_hal_terminalio_terminal_write(terminalio_terminal_obj_t *self, const byte *data, size_t len, int *errcode) { + // Make sure the terminal is initialized before we do anything with it. + if (self->tilegrid == NULL) { + return len; + } const byte *i = data; uint16_t start_y = self->cursor_y; while (i < data + len) { @@ -169,5 +173,5 @@ size_t common_hal_terminalio_terminal_write(terminalio_terminal_obj_t *self, con } bool common_hal_terminalio_terminal_ready_to_tx(terminalio_terminal_obj_t *self) { - return true; + return self->tilegrid != NULL; } diff --git a/supervisor/shared/bluetooth/bluetooth.c b/supervisor/shared/bluetooth/bluetooth.c index ade20a9f07..1db027bf7d 100644 --- a/supervisor/shared/bluetooth/bluetooth.c +++ b/supervisor/shared/bluetooth/bluetooth.c @@ -83,13 +83,27 @@ uint8_t circuitpython_scan_response_data[] = { #endif }; -bool boot_in_discovery_mode = false; -bool advertising = false; + +#if CIRCUITPY_BLE_FILE_SERVICE || CIRCUITPY_SERIAL_BLE +STATIC bool boot_in_discovery_mode = false; +STATIC bool advertising = false; +STATIC bool ble_started = false; + +#define WORKFLOW_UNSET 0 +#define WORKFLOW_ENABLED 1 +#define WORKFLOW_DISABLED 2 + +STATIC uint8_t workflow_state = WORKFLOW_UNSET; +STATIC bool was_connected = false; +#endif STATIC void supervisor_bluetooth_start_advertising(void) { #if !CIRCUITPY_BLE_FILE_SERVICE && !CIRCUITPY_SERIAL_BLE return; #else + if (workflow_state != WORKFLOW_ENABLED) { + return; + } bool is_connected = common_hal_bleio_adapter_get_connected(&common_hal_bleio_adapter_obj); if (is_connected) { return; @@ -211,8 +225,10 @@ void supervisor_bluetooth_init(void) { port_set_saved_word(reset_state); } -STATIC bool was_connected; void supervisor_bluetooth_background(void) { + if (!ble_started) { + return; + } bool is_connected = common_hal_bleio_adapter_get_connected(&common_hal_bleio_adapter_obj); if (was_connected && !is_connected) { #if CIRCUITPY_BLE_FILE_SERVICE @@ -235,6 +251,10 @@ void supervisor_start_bluetooth(void) { return; #endif + if (workflow_state != WORKFLOW_ENABLED) { + return; + } + common_hal_bleio_adapter_set_enabled(&common_hal_bleio_adapter_obj, true); #if CIRCUITPY_BLE_FILE_SERVICE @@ -245,7 +265,10 @@ void supervisor_start_bluetooth(void) { supervisor_start_bluetooth_serial(); #endif - // Kick off advertisments + // Mark as started so that the background call does something. + ble_started = true; + + // Kick off advertisements supervisor_bluetooth_background(); } @@ -254,7 +277,31 @@ void supervisor_stop_bluetooth(void) { return; #endif + if (!ble_started && workflow_state != WORKFLOW_ENABLED) { + return; + } + #if CIRCUITPY_SERIAL_BLE supervisor_stop_bluetooth_serial(); #endif } + +void supervisor_bluetooth_enable_workflow(void) { + #if !CIRCUITPY_BLE_FILE_SERVICE && !CIRCUITPY_SERIAL_BLE + return; + #endif + + if (workflow_state == WORKFLOW_DISABLED) { + return; + } + + workflow_state = WORKFLOW_ENABLED; +} + +void supervisor_bluetooth_disable_workflow(void) { + #if !CIRCUITPY_BLE_FILE_SERVICE && !CIRCUITPY_SERIAL_BLE + mp_raise_NotImplementedError(); + #endif + + workflow_state = WORKFLOW_DISABLED; +} diff --git a/supervisor/shared/bluetooth/bluetooth.h b/supervisor/shared/bluetooth/bluetooth.h index 577c0c76a1..9de82719a5 100644 --- a/supervisor/shared/bluetooth/bluetooth.h +++ b/supervisor/shared/bluetooth/bluetooth.h @@ -34,4 +34,8 @@ void supervisor_bluetooth_init(void); void supervisor_start_bluetooth(void); void supervisor_stop_bluetooth(void); +// Enable only works if it hasn't been set yet. +void supervisor_bluetooth_enable_workflow(void); +void supervisor_bluetooth_disable_workflow(void); + #endif // MICROPY_INCLUDED_SUPERVISOR_SHARED_BLUETOOTH_H diff --git a/supervisor/shared/bluetooth/serial.c b/supervisor/shared/bluetooth/serial.c index d852a86d74..059f322649 100644 --- a/supervisor/shared/bluetooth/serial.c +++ b/supervisor/shared/bluetooth/serial.c @@ -166,7 +166,7 @@ void ble_serial_write(const char *text, size_t len) { } size_t sent = 0; while (sent < len) { - uint16_t packet_size = MIN(len, (size_t)common_hal_bleio_packet_buffer_get_outgoing_packet_length(&_tx_packet_buffer)); + uint16_t packet_size = MIN(len - sent, (size_t)common_hal_bleio_packet_buffer_get_outgoing_packet_length(&_tx_packet_buffer)); mp_int_t written = common_hal_bleio_packet_buffer_write(&_tx_packet_buffer, (const uint8_t *)text + sent, packet_size, NULL, 0); // Error, so we drop characters to transmit. if (written < 0) { diff --git a/supervisor/shared/display.c b/supervisor/shared/display.c index 6ee6c7386a..9cd4f18960 100644 --- a/supervisor/shared/display.c +++ b/supervisor/shared/display.c @@ -123,6 +123,7 @@ void supervisor_stop_terminal(void) { free_memory(tilegrid_tiles); tilegrid_tiles = NULL; supervisor_terminal_text_grid.tiles = NULL; + supervisor_terminal.tilegrid = NULL; } #endif } diff --git a/tools/gen_display_resources.py b/tools/gen_display_resources.py index 04ebc86579..b51c8a40e0 100644 --- a/tools/gen_display_resources.py +++ b/tools/gen_display_resources.py @@ -228,7 +228,7 @@ terminalio_terminal_obj_t supervisor_terminal = { .font = &supervisor_terminal_font, .cursor_x = 0, .cursor_y = 0, - .tilegrid = &supervisor_terminal_text_grid + .tilegrid = NULL }; """ ) diff --git a/tools/safe_mode_finder.py b/tools/safe_mode_finder.py new file mode 100644 index 0000000000..fece853127 --- /dev/null +++ b/tools/safe_mode_finder.py @@ -0,0 +1,181 @@ +# This uses pyocd to control a Cortex M core, set a breakpoint and dump the stack. +# It is expected that you modify it for your particular case. + +from pyocd.core.helpers import ConnectHelper +from pyocd.core.target import Target +from pyocd.debug.elf.symbols import ELFSymbolProvider +from pyocd.flash.file_programmer import FileProgrammer + +import logging + +# logging.basicConfig(level=logging.DEBUG) + +import sys +import time + +board = sys.argv[1] + +# Connect to the target. +with ConnectHelper.session_with_chosen_probe(target_override="nrf52840") as session: + target = session.target + + # Set ELF file on target. + filename = f"build-{board}/firmware.elf" + target.elf = filename + + e = target.elf._elf + + # Look up address of reset_into_safe_mode(). + provider = ELFSymbolProvider(target.elf) + addr = provider.get_symbol_value("reset_into_safe_mode") + print("reset_into_safe_mode() address: 0x%X" % addr) + + for section in target.elf.sections: + print(section) + print() + + print("loading", filename) + # FileProgrammer(session).program(filename, file_format="elf") + print("load done") + + # Set breakpoint. + target.set_breakpoint(addr) + # target.set_watchpoint(0x20010558, 4, Target.WatchpointType.WRITE) + + # Reset and run. + target.reset_and_halt() + for i in range(1): + target.resume() + + print("resuming") + start = time.monotonic() + # Wait 10s until breakpoint is hit. + while target.get_state() != Target.State.HALTED and time.monotonic() - start < 10: + pass + + # value = target.read_memory(0x20010558) + # print(f"{value:08x}") + + # time.sleep(2) + + target.halt() + + # print("_sidata", hex(provider.get_symbol_value("_sidata"))) + + # flash_start = 0x000affe8 + # ram_start = 0x20010000 + # for i in range(0, 0x55c, 4): + # flash_value = target.read_memory(flash_start + i) + # value = target.read_memory(ram_start + i) + # diff = "" + # if flash_value != value: + # diff = "*" + # print(f"{ram_start + i:08x} {flash_value:08x} {value:08x} {diff}") + + # Print PC. + pc = target.read_core_register("pc") + print("pc: 0x%X" % pc) + print(target.elf.symbol_decoder.get_symbol_for_address(pc)) + sp = target.read_core_register("sp") + print("sp: 0x%X" % sp) + msp = target.read_core_register("msp") + print("msp: 0x%X" % msp) + psp = target.read_core_register("psp") + print("psp: 0x%X" % psp) + + print(e.has_dwarf_info()) + d = e.get_dwarf_info() + # print(dir(d)) + aranges = d.get_aranges() + cu_offset = aranges.cu_offset_at_addr(pc) + if not cu_offset: + cu_offset = 0 + main_cu = d.get_CU_at(cu_offset) + lines = d.line_program_for_CU(main_cu).get_entries() + + lines_by_address = {} + for line in lines: + if not line.state: + # print(line) + continue + lines_by_address[line.state.address] = line.state + call_frames = None + # if d.has_CFI(): + # print("CFI") + # call_frames = d.CFI_entries() + # if d.has_EH_CFI(): + # print("EH CFI") + # call_frames = d.EH_CFI_entries() + all_frames = {} + # for frame in call_frames: + # decoded = frame.get_decoded() + # for entry in decoded.table: + # entry_pc = entry["pc"] + # all_frames[entry_pc] = decoded + # for r in d.range_lists().iter_range_lists(): + # print(r) + if pc in all_frames: + print(all_frames[pc]) + ad = target.elf.address_decoder + function = ad.get_function_for_address(pc) + if function: + print(" ", function) + line = ad.get_line_for_address(pc) + if line: + print(" ", line) + + mm = target.get_memory_map() + + ram = mm.get_region_for_address(sp) + if not ram: + sp_guess = 0x20013BCC + print("stack pointer bad using 0x{%08x}") + ram = mm.get_region_for_address(sp_guess) + stack_address = sp + offset = 0 + while ram and sp + offset <= ram.end: + stack_address = sp + offset + value = target.read_memory(stack_address) + symbol = target.elf.symbol_decoder.get_symbol_for_address(value) + print(f"{stack_address:08x} {offset:04x} {value:08x} {symbol}") + function = ad.get_function_for_address(value) + if function: + print(" ", function) + line = ad.get_line_for_address(value) + if line: + print(" ", line) + # value -= 0x27000 + # if value in all_frames: + # print(all_frames[value]) + # if value in lines_by_address: + # print(lines_by_address[value]) + offset += 4 + + top_symbol = target.elf.symbol_decoder.get_symbol_for_address(target.read_memory(sp)) + if True or (top_symbol and top_symbol.name == "HardFault_Handler"): + lr = target.read_core_register("lr") + print("lr: 0x%08X" % lr) + cfsr = target.read_memory(0xE000ED28) + print("cfsr: 0x%08X" % cfsr) + hfsr = target.read_memory(0xE000ED2C) + print("hfsr: 0x%08X" % hfsr) + if hfsr & 0x4000_0000: + print("hard fault forced") + bfsr = (cfsr >> 8) & 0xFF + if bfsr & 0x80 != 0: + bad_address = target.read_memory(0xE000ED38) + print(f"bus fault with address 0x{bad_address:08x}") + bits = ["IBUSERR", "PRECISERR", "IMPRECISERR", "UNSTKERR", "STKERR", "LSPERR"] + for i, bit in enumerate(bits): + if bfsr & (1 << i): + print(bit) + + for i in range(13): + reg = f"r{i}" + value = target.read_core_register(reg) + print(f"{reg} 0x{value:08x}") + + # print(hex(target.read_memory(provider.get_symbol_value("prescaler")))) + + # Remove breakpoint. + target.remove_breakpoint(addr) From 713c8e7b3fd7dfd0af25a611dab4fa4f276767a4 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Tue, 3 Aug 2021 14:36:41 -0700 Subject: [PATCH 2/2] Fix builds without the ble workflow --- shared-bindings/supervisor/__init__.c | 4 +++ supervisor/shared/bluetooth/bluetooth.c | 38 +++++++++++-------------- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/shared-bindings/supervisor/__init__.c b/shared-bindings/supervisor/__init__.c index 0d73c2eb86..05d3827489 100644 --- a/shared-bindings/supervisor/__init__.c +++ b/shared-bindings/supervisor/__init__.c @@ -290,7 +290,11 @@ MP_DEFINE_CONST_FUN_OBJ_0(supervisor_get_previous_traceback_obj, supervisor_get_ //| ... //| STATIC mp_obj_t supervisor_disable_ble_workflow(void) { + #if !CIRCUITPY_BLE_FILE_SERVICE && !CIRCUITPY_SERIAL_BLE + mp_raise_NotImplementedError(NULL); + #else supervisor_bluetooth_disable_workflow(); + #endif return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_0(supervisor_disable_ble_workflow_obj, supervisor_disable_ble_workflow); diff --git a/supervisor/shared/bluetooth/bluetooth.c b/supervisor/shared/bluetooth/bluetooth.c index 1db027bf7d..7762a5655d 100644 --- a/supervisor/shared/bluetooth/bluetooth.c +++ b/supervisor/shared/bluetooth/bluetooth.c @@ -95,12 +95,8 @@ STATIC bool ble_started = false; STATIC uint8_t workflow_state = WORKFLOW_UNSET; STATIC bool was_connected = false; -#endif STATIC void supervisor_bluetooth_start_advertising(void) { - #if !CIRCUITPY_BLE_FILE_SERVICE && !CIRCUITPY_SERIAL_BLE - return; - #else if (workflow_state != WORKFLOW_ENABLED) { return; } @@ -144,16 +140,15 @@ STATIC void supervisor_bluetooth_start_advertising(void) { NULL); // This may fail if we are already advertising. advertising = status == NRF_SUCCESS; - #endif } +#endif // CIRCUITPY_BLE_FILE_SERVICE || CIRCUITPY_SERIAL_BLE + #define BLE_DISCOVERY_DATA_GUARD 0xbb0000bb #define BLE_DISCOVERY_DATA_GUARD_MASK 0xff0000ff void supervisor_bluetooth_init(void) { - #if !CIRCUITPY_BLE_FILE_SERVICE && !CIRCUITPY_SERIAL_BLE - return; - #endif + #if CIRCUITPY_BLE_FILE_SERVICE || CIRCUITPY_SERIAL_BLE uint32_t reset_state = port_get_saved_word(); uint32_t ble_mode = 0; if ((reset_state & BLE_DISCOVERY_DATA_GUARD_MASK) == BLE_DISCOVERY_DATA_GUARD) { @@ -223,9 +218,11 @@ void supervisor_bluetooth_init(void) { status_led_deinit(); #endif port_set_saved_word(reset_state); + #endif } void supervisor_bluetooth_background(void) { + #if CIRCUITPY_BLE_FILE_SERVICE || CIRCUITPY_SERIAL_BLE if (!ble_started) { return; } @@ -244,12 +241,11 @@ void supervisor_bluetooth_background(void) { #if CIRCUITPY_BLE_FILE_SERVICE supervisor_bluetooth_file_transfer_background(); #endif + #endif } void supervisor_start_bluetooth(void) { - #if !CIRCUITPY_BLE_FILE_SERVICE && !CIRCUITPY_SERIAL_BLE - return; - #endif + #if CIRCUITPY_BLE_FILE_SERVICE || CIRCUITPY_SERIAL_BLE if (workflow_state != WORKFLOW_ENABLED) { return; @@ -270,12 +266,12 @@ void supervisor_start_bluetooth(void) { // Kick off advertisements supervisor_bluetooth_background(); + + #endif } void supervisor_stop_bluetooth(void) { - #if !CIRCUITPY_BLE_FILE_SERVICE && !CIRCUITPY_SERIAL_BLE - return; - #endif + #if CIRCUITPY_BLE_FILE_SERVICE || CIRCUITPY_SERIAL_BLE if (!ble_started && workflow_state != WORKFLOW_ENABLED) { return; @@ -284,24 +280,22 @@ void supervisor_stop_bluetooth(void) { #if CIRCUITPY_SERIAL_BLE supervisor_stop_bluetooth_serial(); #endif + + #endif } void supervisor_bluetooth_enable_workflow(void) { - #if !CIRCUITPY_BLE_FILE_SERVICE && !CIRCUITPY_SERIAL_BLE - return; - #endif - + #if CIRCUITPY_BLE_FILE_SERVICE || CIRCUITPY_SERIAL_BLE if (workflow_state == WORKFLOW_DISABLED) { return; } workflow_state = WORKFLOW_ENABLED; + #endif } void supervisor_bluetooth_disable_workflow(void) { - #if !CIRCUITPY_BLE_FILE_SERVICE && !CIRCUITPY_SERIAL_BLE - mp_raise_NotImplementedError(); - #endif - + #if CIRCUITPY_BLE_FILE_SERVICE || CIRCUITPY_SERIAL_BLE workflow_state = WORKFLOW_DISABLED; + #endif }