Просмотр исходного кода

[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 <alleteam@gmail.com>
hedger 2 лет назад
Родитель
Сommit
241b4ef6e4

+ 8 - 9
.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",

+ 14 - 2
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,
 )
 
 

+ 109 - 0
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()

+ 2 - 1
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,

+ 2 - 1
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,

+ 11 - 0
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;
+}

+ 13 - 0
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

+ 16 - 0
furi/core/check.c

@@ -6,6 +6,7 @@
 #include <furi_hal_power.h>
 #include <furi_hal_rtc.h>
 #include <furi_hal_debug.h>
+#include <furi_hal_bt.h>
 #include <stdio.h>
 
 #include <FreeRTOS.h>
@@ -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

+ 16 - 4
lib/toolbox/version.c

@@ -1,23 +1,34 @@
 #include "version.h"
-
+#include <furi.h>
 /* 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) {

+ 6 - 2
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",
     )

+ 8 - 0
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@"
             ]

+ 0 - 3
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,
         }