From 241b4ef6e4096005290aedd1bc35e56398c579dd Mon Sep 17 00:00:00 2001 From: hedger Date: Tue, 9 May 2023 01:31:39 +0300 Subject: [PATCH] [FL-3299] furi_crash: added C2 status; added fw-version gdb command (#2638) * furi_crash: added C2 status * debug: Added "fw-version" gdb command; vscode: updated configuration to use new command * debug: added fw-info command to debug_other session * Toolbox: versioned structure for Version * debug: fw-version: no longer needs an ELF file loaded * debug: flipperversion: removed unused variable * debug_other: print running fw version Co-authored-by: Aleksandr Kutuzov --- .vscode/example/launch.json | 17 ++- SConstruct | 16 ++- debug/flipperversion.py | 109 ++++++++++++++++++ firmware/targets/f18/api_symbols.csv | 3 +- firmware/targets/f7/api_symbols.csv | 3 +- firmware/targets/f7/furi_hal/furi_hal_bt.c | 11 ++ .../targets/furi_hal_include/furi_hal_bt.h | 13 +++ furi/core/check.c | 16 +++ lib/toolbox/version.c | 20 +++- scripts/fbt_tools/fbt_debugopts.py | 8 +- .../ufbt/project_template/.vscode/launch.json | 8 ++ scripts/version.py | 3 - 12 files changed, 205 insertions(+), 22 deletions(-) create mode 100644 debug/flipperversion.py diff --git a/.vscode/example/launch.json b/.vscode/example/launch.json index 4cab026fa..b0bb9e245 100644 --- a/.vscode/example/launch.json +++ b/.vscode/example/launch.json @@ -36,6 +36,8 @@ "./debug/stm32wbx.cfg", ], "postAttachCommands": [ + "source debug/flipperversion.py", + "fw-version", // "compare-sections", "source debug/flipperapps.py", "fap-set-debug-elf-root build/latest/.extapps", @@ -59,6 +61,8 @@ "attach 1", "set confirm off", "set mem inaccessible-by-default off", + "source debug/flipperversion.py", + "fw-version", "source debug/flipperapps.py", "fap-set-debug-elf-root build/latest/.extapps", // "compare-sections", @@ -77,6 +81,8 @@ "svdFile": "./debug/STM32WB55_CM4.svd", "rtos": "FreeRTOS", "postAttachCommands": [ + "source debug/flipperversion.py", + "fw-version", "source debug/flipperapps.py", "fap-set-debug-elf-root build/latest/.extapps", ] @@ -97,20 +103,13 @@ "./debug/stm32wbx.cfg", ], "postAttachCommands": [ + "source debug/flipperversion.py", + "fw-version", "source debug/flipperapps.py", "fap-set-debug-elf-root build/latest/.extapps", ], // "showDevDebugOutput": "raw", }, - { - "name": "fbt debug", - "type": "python", - "request": "launch", - "program": "./lib/scons/scripts/scons.py", - "args": [ - "plugin_dist" - ] - }, { "name": "python debug", "type": "python", diff --git a/SConstruct b/SConstruct index 12f1166eb..e2568287d 100644 --- a/SConstruct +++ b/SConstruct @@ -239,19 +239,31 @@ distenv.PhonyTarget( ) # Debug alien elf +debug_other_opts = [ + "-ex", + "source ${FBT_DEBUG_DIR}/PyCortexMDebug/PyCortexMDebug.py", + # "-ex", + # "source ${FBT_DEBUG_DIR}/FreeRTOS/FreeRTOS.py", + "-ex", + "source ${FBT_DEBUG_DIR}/flipperversion.py", + "-ex", + "fw-version", +] + distenv.PhonyTarget( "debug_other", "${GDBPYCOM}", GDBOPTS="${GDBOPTS_BASE}", GDBREMOTE="${OPENOCD_GDB_PIPE}", - GDBPYOPTS='-ex "source ${FBT_DEBUG_DIR}/PyCortexMDebug/PyCortexMDebug.py" ', + GDBPYOPTS=debug_other_opts, ) distenv.PhonyTarget( "debug_other_blackmagic", "${GDBPYCOM}", GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}", - GDBREMOTE="$${BLACKMAGIC_ADDR}", + GDBREMOTE="${BLACKMAGIC_ADDR}", + GDBPYOPTS=debug_other_opts, ) diff --git a/debug/flipperversion.py b/debug/flipperversion.py new file mode 100644 index 000000000..4ac3bd200 --- /dev/null +++ b/debug/flipperversion.py @@ -0,0 +1,109 @@ +from dataclasses import dataclass, field +from typing import Dict, Optional + +import gdb + + +# Must match FuriHalRtcRegisterVersion index in FuriHalRtcRegister enum +RTC_BACKUP_VERSION_REGISTER_IDX = 0x2 + +RTC_BASE = 0x40002800 +RTC_BACKUP_BASE = RTC_BASE + 0x50 + +VERSION_REGISTER_ADDRESS = RTC_BACKUP_BASE + RTC_BACKUP_VERSION_REGISTER_IDX * 4 + +VERSION_STRUCT_MAGIC = 0xBE40 + + +@dataclass +class VersionData: + git_hash: str + git_branch: str + build_date: str + version: str + target: int + build_is_dirty: bool + extra: Optional[Dict[str, str]] = field(default_factory=dict) + + +class VersionLoader: + def __init__(self, version_ptr): + self.version_ptr = version_ptr + self._cstr_type = gdb.lookup_type("char").pointer() + self._uint_type = gdb.lookup_type("unsigned int") + + version_signature = version_ptr.dereference().cast(self._uint_type) + is_versioned = (version_signature & (0xFFFF)) == VERSION_STRUCT_MAGIC + if is_versioned: + self._version_data = self.load_versioned( + major=version_signature >> 16 & 0xFF, + minor=version_signature >> 24 & 0xFF, + ) + else: + self._version_data = self.load_unversioned() + + @property + def version(self) -> VersionData: + return self._version_data + + def load_versioned(self, major, minor): + if major != 1: + raise ValueError("Unsupported version struct major version") + + # Struct version 1.0 + extra_data = int(self.version_ptr[5].cast(self._uint_type)) + return VersionData( + git_hash=self.version_ptr[1].cast(self._cstr_type).string(), + git_branch=self.version_ptr[2].cast(self._cstr_type).string(), + build_date=self.version_ptr[3].cast(self._cstr_type).string(), + version=self.version_ptr[4].cast(self._cstr_type).string(), + target=extra_data & 0xF, + build_is_dirty=bool((extra_data >> 8) & 0xF), + ) + + def load_unversioned(self): + """Parse an early version of the version struct.""" + extra_data = int(self.version_ptr[5].cast(self._uint_type)) + return VersionData( + git_hash=self.version_ptr[0].cast(self._cstr_type).string(), + git_branch=self.version_ptr[1].cast(self._cstr_type).string(), + # branch number is #2, but we don't care about it + build_date=self.version_ptr[3].cast(self._cstr_type).string(), + version=self.version_ptr[4].cast(self._cstr_type).string(), + target=extra_data & 0xF, + build_is_dirty=bool((extra_data >> 8) & 0xF), + ) + + +class FlipperFwVersion(gdb.Command): + """Print the version of Flipper's firmware.""" + + def __init__(self): + super(FlipperFwVersion, self).__init__("fw-version", gdb.COMMAND_USER) + + def invoke(self, arg, from_tty): + void_ptr_type = gdb.lookup_type("void").pointer().pointer() + version_ptr_ptr = gdb.Value(VERSION_REGISTER_ADDRESS).cast(void_ptr_type) + + if not version_ptr_ptr: + print("RTC version register is NULL") + return + + version_ptr = version_ptr_ptr.dereference() + if not version_ptr: + print("Pointer to version struct is NULL") + return + + version_struct = version_ptr.cast(void_ptr_type) + + v = VersionLoader(version_struct) + print("Firmware version on attached Flipper:") + print(f"\tVersion: {v.version.version}") + print(f"\tBuilt on: {v.version.build_date}") + print(f"\tGit branch: {v.version.git_branch}") + print(f"\tGit commit: {v.version.git_hash}") + print(f"\tDirty: {v.version.build_is_dirty}") + print(f"\tHW Target: {v.version.target}") + + +FlipperFwVersion() diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index 8db084e2f..7a892e1cc 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,26.0,, +Version,+,26.1,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -823,6 +823,7 @@ Function,+,furi_hal_bt_change_app,_Bool,"FuriHalBtProfile, GapEventCallback, voi Function,+,furi_hal_bt_clear_white_list,_Bool, Function,+,furi_hal_bt_dump_state,void,FuriString* Function,+,furi_hal_bt_ensure_c2_mode,_Bool,BleGlueC2Mode +Function,-,furi_hal_bt_get_hardfault_info,const FuriHalBtHardfaultInfo*, Function,+,furi_hal_bt_get_key_storage_buff,void,"uint8_t**, uint16_t*" Function,+,furi_hal_bt_get_radio_stack,FuriHalBtStack, Function,+,furi_hal_bt_get_rssi,float, diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 643835608..6cb421a1b 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,26.0,, +Version,+,26.1,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1004,6 +1004,7 @@ Function,+,furi_hal_bt_change_app,_Bool,"FuriHalBtProfile, GapEventCallback, voi Function,+,furi_hal_bt_clear_white_list,_Bool, Function,+,furi_hal_bt_dump_state,void,FuriString* Function,+,furi_hal_bt_ensure_c2_mode,_Bool,BleGlueC2Mode +Function,-,furi_hal_bt_get_hardfault_info,const FuriHalBtHardfaultInfo*, Function,+,furi_hal_bt_get_key_storage_buff,void,"uint8_t**, uint16_t*" Function,+,furi_hal_bt_get_radio_stack,FuriHalBtStack, Function,+,furi_hal_bt_get_rssi,float, diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt.c b/firmware/targets/f7/furi_hal/furi_hal_bt.c index b08c9ea27..048a8b309 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt.c @@ -19,6 +19,8 @@ /* Time, in ms, to wait for mode transition before crashing */ #define C2_MODE_SWITCH_TIMEOUT 10000 +#define FURI_HAL_BT_HARDFAULT_INFO_MAGIC 0x1170FD0F + FuriMutex* furi_hal_bt_core2_mtx = NULL; static FuriHalBtStack furi_hal_bt_stack = FuriHalBtStackUnknown; @@ -440,3 +442,12 @@ bool furi_hal_bt_ensure_c2_mode(BleGlueC2Mode mode) { FURI_LOG_E(TAG, "Failed to switch C2 mode: %d", fw_start_res); return false; } + +const FuriHalBtHardfaultInfo* furi_hal_bt_get_hardfault_info() { + /* AN5289, 4.8.2 */ + const FuriHalBtHardfaultInfo* info = (FuriHalBtHardfaultInfo*)(SRAM2A_BASE); + if(info->magic != FURI_HAL_BT_HARDFAULT_INFO_MAGIC) { + return NULL; + } + return info; +} diff --git a/firmware/targets/furi_hal_include/furi_hal_bt.h b/firmware/targets/furi_hal_include/furi_hal_bt.h index 196b2edb3..6ba38cb5e 100644 --- a/firmware/targets/furi_hal_include/furi_hal_bt.h +++ b/firmware/targets/furi_hal_include/furi_hal_bt.h @@ -224,6 +224,19 @@ uint32_t furi_hal_bt_get_transmitted_packets(); */ bool furi_hal_bt_ensure_c2_mode(BleGlueC2Mode mode); +typedef struct { + uint32_t magic; + uint32_t source_pc; + uint32_t source_lr; + uint32_t source_sp; +} FuriHalBtHardfaultInfo; + +/** Get hardfault info + * + * @return hardfault info. NULL if no hardfault + */ +const FuriHalBtHardfaultInfo* furi_hal_bt_get_hardfault_info(); + #ifdef __cplusplus } #endif diff --git a/furi/core/check.c b/furi/core/check.c index f5390639d..478f3aacc 100644 --- a/furi/core/check.c +++ b/furi/core/check.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -87,6 +88,20 @@ static void __furi_print_stack_info() { __furi_put_uint32_as_text(uxTaskGetStackHighWaterMark(NULL) * 4); } +static void __furi_print_bt_stack_info() { + const FuriHalBtHardfaultInfo* fault_info = furi_hal_bt_get_hardfault_info(); + if(fault_info == NULL) { + furi_hal_console_puts("\r\n\tcore2: not faulted"); + } else { + furi_hal_console_puts("\r\n\tcore2: hardfaulted.\r\n\tPC: "); + __furi_put_uint32_as_hex(fault_info->source_pc); + furi_hal_console_puts("\r\n\tLR: "); + __furi_put_uint32_as_hex(fault_info->source_lr); + furi_hal_console_puts("\r\n\tSP: "); + __furi_put_uint32_as_hex(fault_info->source_sp); + } +} + static void __furi_print_heap_info() { furi_hal_console_puts("\r\n\t heap total: "); __furi_put_uint32_as_text(xPortGetTotalHeapSize()); @@ -136,6 +151,7 @@ FURI_NORETURN void __furi_crash() { __furi_print_stack_info(); } __furi_print_heap_info(); + __furi_print_bt_stack_info(); #ifndef FURI_DEBUG // Check if debug enabled by DAP diff --git a/lib/toolbox/version.c b/lib/toolbox/version.c index c6c10b410..6ba68e364 100644 --- a/lib/toolbox/version.c +++ b/lib/toolbox/version.c @@ -1,23 +1,34 @@ #include "version.h" - +#include /* This header is autogenerated by build system */ #include "version.inc.h" +#define VERSION_MAGIC (0xBE40u) +#define VERSION_MAJOR (0x1u) +#define VERSION_MINOR (0x0u) + struct Version { + // Header + const uint16_t magic; + const uint8_t major; + const uint8_t minor; + // Payload const char* git_hash; const char* git_branch; - const char* git_branch_num; const char* build_date; const char* version; + // Payload bits and pieces const uint8_t target; const bool build_is_dirty; }; /* version of current running firmware (bootloader/flipper) */ static const Version version = { + .magic = VERSION_MAGIC, + .major = VERSION_MAJOR, + .minor = VERSION_MINOR, .git_hash = GIT_COMMIT, .git_branch = GIT_BRANCH, - .git_branch_num = GIT_BRANCH_NUM, .build_date = BUILD_DATE, .version = VERSION #ifdef FURI_RAM_EXEC @@ -41,7 +52,8 @@ const char* version_get_gitbranch(const Version* v) { } const char* version_get_gitbranchnum(const Version* v) { - return v ? v->git_branch_num : version.git_branch_num; + UNUSED(v); + return "0"; } const char* version_get_builddate(const Version* v) { diff --git a/scripts/fbt_tools/fbt_debugopts.py b/scripts/fbt_tools/fbt_debugopts.py index 33cc0c076..58e73e9c9 100644 --- a/scripts/fbt_tools/fbt_debugopts.py +++ b/scripts/fbt_tools/fbt_debugopts.py @@ -39,10 +39,10 @@ def generate(env, **kw): "|openocd -c 'gdb_port pipe; log_output ${FBT_DEBUG_DIR}/openocd.log' ${[SINGLEQUOTEFUNC(OPENOCD_OPTS)]}" ], GDBOPTS_BASE=[ - "-ex", - "target extended-remote ${GDBREMOTE}", "-ex", "source ${FBT_DEBUG_DIR}/gdbinit", + "-ex", + "target extended-remote ${GDBREMOTE}", ], GDBOPTS_BLACKMAGIC=[ "-q", @@ -61,6 +61,8 @@ def generate(env, **kw): "-ex", "source ${FBT_DEBUG_DIR}/flipperapps.py", "-ex", + "source ${FBT_DEBUG_DIR}/flipperversion.py", + "-ex", "fap-set-debug-elf-root ${FBT_FAP_DEBUG_ELF_ROOT}", "-ex", "source ${FBT_DEBUG_DIR}/PyCortexMDebug/PyCortexMDebug.py", @@ -68,6 +70,8 @@ def generate(env, **kw): "svd_load ${SVD_FILE}", "-ex", "compare-sections", + "-ex", + "fw-version", ], JFLASHPROJECT="${FBT_DEBUG_DIR}/fw.jflash", ) diff --git a/scripts/ufbt/project_template/.vscode/launch.json b/scripts/ufbt/project_template/.vscode/launch.json index 697de9a49..3269bab57 100644 --- a/scripts/ufbt/project_template/.vscode/launch.json +++ b/scripts/ufbt/project_template/.vscode/launch.json @@ -29,6 +29,8 @@ "@UFBT_DEBUG_DIR@/stm32wbx.cfg" ], "postAttachCommands": [ + "source @UFBT_DEBUG_DIR@/flipperversion.py", + "fw-version", "source @UFBT_DEBUG_DIR@/flipperapps.py", "fap-set-debug-elf-root @UFBT_DEBUG_ELF_DIR@" ], @@ -49,6 +51,8 @@ "@UFBT_DEBUG_DIR@/stm32wbx.cfg" ], "postAttachCommands": [ + "source @UFBT_DEBUG_DIR@/flipperversion.py", + "fw-version", "source @UFBT_DEBUG_DIR@/flipperapps.py", "fap-set-debug-elf-root @UFBT_DEBUG_ELF_DIR@" ], @@ -69,6 +73,8 @@ "attach 1", "set confirm off", "set mem inaccessible-by-default off", + "source @UFBT_DEBUG_DIR@/flipperversion.py", + "fw-version", "source @UFBT_DEBUG_DIR@/flipperapps.py", "fap-set-debug-elf-root @UFBT_DEBUG_ELF_DIR@" ] @@ -86,6 +92,8 @@ "svdFile": "@UFBT_DEBUG_DIR@/STM32WB55_CM4.svd", "rtos": "FreeRTOS", "postAttachCommands": [ + "source @UFBT_DEBUG_DIR@/flipperversion.py", + "fw-version", "source @UFBT_DEBUG_DIR@/flipperapps.py", "fap-set-debug-elf-root @UFBT_DEBUG_ELF_DIR@" ] diff --git a/scripts/version.py b/scripts/version.py index 71d201abb..3d68b2e98 100644 --- a/scripts/version.py +++ b/scripts/version.py @@ -35,8 +35,6 @@ class GitVersion: or "unknown" ) - branch_num = self._exec_git("rev-list --count HEAD") or "n/a" - try: version = self._exec_git("describe --tags --abbrev=0 --exact-match") except subprocess.CalledProcessError: @@ -45,7 +43,6 @@ class GitVersion: return { "GIT_COMMIT": commit, "GIT_BRANCH": branch, - "GIT_BRANCH_NUM": branch_num, "VERSION": version, "BUILD_DIRTY": dirty and 1 or 0, }