Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions generated/include/infuse/tdf/definitions.h
Original file line number Diff line number Diff line change
Expand Up @@ -677,6 +677,12 @@ struct tdf_network_scan_count {
uint8_t num_lte;
} __packed;

/** Define a variant of tdf_exception_stack_frame with a constant length */
#define TDF_EXCEPTION_STACK_FRAME_VAR(_name, _count) \
struct _name { \
uint32_t frame[_count]; \
} __packed;

/** Infuse-IoT builtin TDF definitions */
enum tdf_builtin_id {
/** Common announcement packet */
Expand Down Expand Up @@ -775,6 +781,8 @@ enum tdf_builtin_id {
TDF_WIFI_DISCONNECTED = 50,
/** Counts associated with scanning Wi-Fi and LTE networks */
TDF_NETWORK_SCAN_COUNT = 51,
/** Generic exception stack frame */
TDF_EXCEPTION_STACK_FRAME = 52,
/** End of builtin TDF range */
TDF_BUILTIN_END = 1024,
};
Expand Down
63 changes: 47 additions & 16 deletions include/infuse/reboot.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,45 @@ enum infuse_reboot_reason {
INFUSE_REBOOT_UNKNOWN = 255,
};

/** Type of @ref infuse_reboot_info data */
enum infuse_reboot_info_type {
/* Generic reboot, two uint32_t parameters */
INFUSE_REBOOT_INFO_GENERIC = 0,
/** Exception with only PC and LR info */
INFUSE_REBOOT_INFO_EXCEPTION_BASIC,
/** Exception with full stack frame */
INFUSE_REBOOT_INFO_EXCEPTION_ESF,
/** Hardware watchdog expiry */
INFUSE_REBOOT_INFO_WATCHDOG,
} __packed;

/** Detailed information about the reboot location/cause */
union infuse_reboot_info {
/* Generic reboot information */
struct {
/** Info 1 */
uint32_t info1;
/** Info 2 */
uint32_t info2;
} generic;
/* Basic exception information */
struct {
/** Program counter value at exception */
uint32_t program_counter;
/** Link register value at exception */
uint32_t link_register;
} exception_basic;
/* Exception stack frame */
struct arch_esf exception_full;
/* Watchdog reboot information */
struct {
/** Watchdog info1 per @ref infuse_watchdog_thread_state_lookup */
uint32_t info1;
/** Watchdog info2 per @ref infuse_watchdog_thread_state_lookup */
uint32_t info2;
} watchdog;
} __packed;

/** Reboot state information */
struct infuse_reboot_state {
/** First 3 parameters are updated a second time on delayed reboots.
Expand All @@ -74,18 +113,10 @@ struct infuse_reboot_state {
enum infuse_reboot_reason reason;
/** Hardware reboot reason flags */
uint32_t hardware_reason;
union {
/** Program counter value at exception */
uint32_t program_counter;
/** Watchdog info1 per @ref infuse_watchdog_thread_state_lookup */
uint32_t watchdog_info1;
} param_1;
union {
/** Link register value at exception */
uint32_t link_register;
/** Watchdog info2 per @ref infuse_watchdog_thread_state_lookup */
uint32_t watchdog_info2;
} param_2;
/** Type of the information in @a info */
enum infuse_reboot_info_type info_type;
/** Reboot information */
union infuse_reboot_info info;
/** Thread executing at reboot time */
char thread_name[REBOOT_STATE_THREAD_NAME_MAX];
} __packed;
Expand All @@ -98,17 +129,17 @@ struct infuse_reboot_state {
* @brief Trigger a system reboot
*
* @param reason Reason the system is rebooting
* @param info1 Program counter at exception or watchdog channel that expired
* @param info2 Link register at exception
* @param info1 Generic information to identify/diagnose the reboot
* @param info2 Generic information to identify/diagnose the reboot
*/
_NORETURN void infuse_reboot(enum infuse_reboot_reason reason, uint32_t info1, uint32_t info2);

/**
* @brief Trigger a system reboot in the future
*
* @param reason Reason the system is rebooting
* @param info1 Program counter at exception or watchdog channel that expired
* @param info2 Link register at exception
* @param info1 Generic information to identify/diagnose the reboot
* @param info2 Generic information to identify/diagnose the reboot
* @param delay Time delay or absolute time to execute the reboot
*/
void infuse_reboot_delayed(enum infuse_reboot_reason reason, uint32_t info1, uint32_t info2,
Expand Down
24 changes: 22 additions & 2 deletions include/infuse/tdf/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,20 @@ static inline void tdf_reboot_info_from_state(struct infuse_reboot_state *state,
info->hardware_flags = state->hardware_reason;
info->uptime = state->uptime;
info->count = reboot.count;
info->param_1 = state->param_1.program_counter;
info->param_2 = state->param_2.link_register;
if (state->info_type == INFUSE_REBOOT_INFO_EXCEPTION_ESF) {
#ifdef CONFIG_ARM
info->param_1 = state->info.exception_full.basic.pc;
info->param_2 = state->info.exception_full.basic.lr;
#else
/* Decoding ESF's is arch specific */
info->param_1 = 0;
info->param_2 = 0;
#endif /* CONFIG_ARG */
} else {
/* Generic, Exception Basic, Watchdog all have the same info layout */
info->param_1 = state->info.generic.info1;
info->param_2 = state->info.generic.info2;
}
strncpy(info->thread, state->thread_name, sizeof(info->thread));
}

Expand Down Expand Up @@ -162,6 +174,14 @@ static inline void tdf_reboot_info_log(uint8_t logger_mask)
tdf_reboot_info_from_state(&reboot_state, &reboot_info);
/* Push TDF at logger */
TDF_DATA_LOGGER_LOG(logger_mask, TDF_REBOOT_INFO, t, &reboot_info);
if (reboot_state.info_type == INFUSE_REBOOT_INFO_EXCEPTION_ESF) {
/* Except word aligned exception stack frames */
BUILD_ASSERT(sizeof(reboot_state.info.exception_full) % sizeof(uint32_t) == 0);
/* Push full exception stack frame at the logger */
tdf_data_logger_log(logger_mask, TDF_EXCEPTION_STACK_FRAME,
sizeof(reboot_state.info.exception_full), t,
&reboot_state.info.exception_full);
}
#endif /* CONFIG_TDF_UTIL_REBOOT_INFO_LOG */
}

Expand Down
80 changes: 59 additions & 21 deletions lib/common_boot.c
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,12 @@ static int secure_fault_info_read(void)
LOG_DBG("SecureFault");

reboot_state.reason = reason;
/* We have the basic ESF contents, ensure any other parts are zeroed out */
reboot_state.info_type = INFUSE_REBOOT_INFO_EXCEPTION_ESF;
memset(&reboot_state.info.exception_full, 0x00, sizeof(reboot_state.info.exception_full));
memcpy(&reboot_state.info.exception_full.basic, secure_fault.EXC_FRAME_COPY,
sizeof(reboot_state.info.exception_full.basic));
reboot_state.epoch_time_source = TIME_SOURCE_INVALID;
reboot_state.param_1.program_counter = secure_fault.EXC_FRAME_COPY[ARCH_ESF_PC_IDX];
reboot_state.param_2.link_register = secure_fault.EXC_FRAME_COPY[ARCH_ESF_LR_IDX];
/* We can't extract the thread name, but we can output the frame pointer as a string.
* This should point back to the stack of the offending thread.
*/
Expand All @@ -138,6 +141,49 @@ static int secure_fault_info_read(void)

#endif /* defined(CONFIG_TFM_PLATFORM_FAULT_INFO_QUERY) && defined(CONFIG_INFUSE_REBOOT) */

#ifdef CONFIG_INFUSE_REBOOT

static void reboot_info_print(int query_rc)
{
LOG_INF("");
LOG_INF("Reboot Information");
LOG_INF("\tHardware: %08X", reboot_state.hardware_reason);
if (query_rc != 0) {
LOG_INF("\t Cause: Unknown");
return;
}
LOG_INF("\t Cause: %d", reboot_state.reason);
LOG_INF("\t Uptime: %d", reboot_state.uptime);
LOG_INF("\t Thread: %s", reboot_state.thread_name);
switch (reboot_state.info_type) {
case INFUSE_REBOOT_INFO_GENERIC:
LOG_INF("\t Info 1: %08X", reboot_state.info.generic.info1);
LOG_INF("\t Info 2: %08X", reboot_state.info.generic.info2);
break;
case INFUSE_REBOOT_INFO_EXCEPTION_BASIC:
LOG_INF("\t PC: %08X", reboot_state.info.exception_basic.program_counter);
LOG_INF("\t LR: %08X", reboot_state.info.exception_basic.link_register);
break;
case INFUSE_REBOOT_INFO_EXCEPTION_ESF:
#ifdef CONFIG_ARM
LOG_INF("\t PC: %08X", reboot_state.info.exception_full.basic.pc);
LOG_INF("\t LR: %08X", reboot_state.info.exception_full.basic.lr);
#else
LOG_INF("\t ESF: Unknown");
#endif /* CONFIG_ARM */
break;
case INFUSE_REBOOT_INFO_WATCHDOG:
LOG_INF("\t Wdog 1: %08X", reboot_state.info.watchdog.info1);
LOG_INF("\t Wdog 2: %08X", reboot_state.info.watchdog.info2);
break;
default:
/* Unknown info */
break;
}
}

#endif /* CONFIG_INFUSE_REBOOT */

static int infuse_common_boot(void)
{
KV_KEY_TYPE(KV_KEY_REBOOTS) reboot = {0};
Expand Down Expand Up @@ -266,26 +312,18 @@ static int infuse_common_boot(void)
}

/* Print the reboot information/causes */
LOG_INF("");
LOG_INF("Reboot Information");
LOG_INF("\tHardware: %08X", reboot_state.hardware_reason);
if (rc == 0) {
LOG_INF("\t Cause: %d", reboot_state.reason);
LOG_INF("\t Uptime: %d", reboot_state.uptime);
LOG_INF("\t Thread: %s", reboot_state.thread_name);
LOG_INF("\t PC/WDOG: %08X", reboot_state.param_1.program_counter);
LOG_INF("\t LR/WDOG: %08X", reboot_state.param_2.link_register);

if (reboot_state.epoch_time_source != TIME_SOURCE_INVALID) {
/* Restore time knowledge (Assume reboot took 0 ms) */
reference.local = 0;
reference.ref = reboot_state.epoch_time;
epoch_time_set_reference(
TIME_SOURCE_RECOVERED | reboot_state.epoch_time_source, &reference);
}
} else {
LOG_INF("\t Cause: Unknown");
reboot_info_print(rc);

if ((rc == 0) && (reboot_state.epoch_time_source != TIME_SOURCE_INVALID)) {
/* Restore time knowledge (Assume reboot took 0 ms).
* Do this after `reboot_info_print` to avoid interrupting the debug output.
*/
reference.local = 0;
reference.ref = reboot_state.epoch_time;
epoch_time_set_reference(TIME_SOURCE_RECOVERED | reboot_state.epoch_time_source,
&reference);
}

#else
reboot_state.reason = INFUSE_REBOOT_UNKNOWN;
#endif /* CONFIG_INFUSE_REBOOT */
Expand Down
4 changes: 2 additions & 2 deletions lib/memfault/infuse_memfault.c
Original file line number Diff line number Diff line change
Expand Up @@ -216,8 +216,8 @@ void memfault_reboot_tracking_load(sMemfaultRebootTrackingStorage *dst)
.magic = MEMFAULT_REBOOT_INFO_MAGIC,
.version = MEMFAULT_REBOOT_INFO_VERSION,
.last_reboot_reason = kMfltRebootReason_SecurityViolation,
.pc = infuse_reboot.param_1.program_counter,
.lr = infuse_reboot.param_2.link_register,
.pc = infuse_reboot.info.exception_basic.program_counter,
.lr = infuse_reboot.info.exception_basic.link_register,
};

/* Defer logging of the secure fault trace event until after Memfault has finished
Expand Down
44 changes: 27 additions & 17 deletions lib/reboot.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,19 @@
#include <memfault/panics/assert.h>
#endif

static const struct device *retention = DEVICE_DT_GET(DT_CHOSEN(infuse_reboot_state));
#define RETENTION DT_CHOSEN(infuse_reboot_state)
#define RETENTION_PREFIX_LEN \
COND_CODE_1(DT_NODE_HAS_PROP(RETENTION, prefix), (DT_PROP_LEN(RETENTION, prefix)), (0))

static void reboot_state_store(enum infuse_reboot_reason reason, uint32_t info1, uint32_t info2)
static const struct device *retention = DEVICE_DT_GET(RETENTION);

BUILD_ASSERT(sizeof(struct infuse_reboot_state) <= (DT_REG_SIZE(RETENTION) - RETENTION_PREFIX_LEN));

uint32_t size = DT_REG_SIZE(RETENTION);

static void reboot_state_store(enum infuse_reboot_reason reason,
enum infuse_reboot_info_type info_type, uint32_t info1,
uint32_t info2, const struct arch_esf *esf)
{
struct infuse_reboot_state state;

Expand All @@ -37,8 +47,13 @@ static void reboot_state_store(enum infuse_reboot_reason reason, uint32_t info1,
state.epoch_time_source = epoch_time_get_source();
state.epoch_time = epoch_time_now();
state.uptime = k_uptime_seconds();
state.param_1.program_counter = info1;
state.param_2.link_register = info2;
state.info_type = info_type;
if (esf != NULL) {
memcpy(&state.info.exception_full, esf, sizeof(*esf));
} else {
state.info.generic.info1 = info1;
state.info.generic.info2 = info2;
}

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstringop-truncation"
Expand Down Expand Up @@ -81,16 +96,10 @@ static void delayed_do_reboot(struct k_work *work)

void k_sys_fatal_error_handler(unsigned int reason, const struct arch_esf *esf)
{
uint32_t pc = 0, lr = 0;

/* Extract exception info */
if (esf != NULL) {
pc = esf->basic.pc;
lr = esf->basic.lr;
}

/* Standard reboot process */
infuse_reboot(reason, pc, lr);
/* Store reboot metadata */
reboot_state_store(reason, INFUSE_REBOOT_INFO_EXCEPTION_ESF, 0, 0, esf);
/* Do the reboot */
cleanup_and_reboot();
}

void infuse_watchdog_warning(const struct device *dev, int channel_id)
Expand All @@ -115,7 +124,8 @@ void infuse_watchdog_expired(const struct device *dev, int channel_id)
#endif

/* Store reboot metadata */
reboot_state_store(INFUSE_REBOOT_HW_WATCHDOG, info1, info2);
reboot_state_store(INFUSE_REBOOT_HW_WATCHDOG, INFUSE_REBOOT_INFO_WATCHDOG, info1, info2,
NULL);
/* Wait for watchdog to reboot us */
for (;;)
;
Expand All @@ -128,7 +138,7 @@ void infuse_watchdog_expired(const struct device *dev, int channel_id)
FUNC_NORETURN void infuse_reboot(enum infuse_reboot_reason reason, uint32_t info1, uint32_t info2)
{
/* Store reboot metadata */
reboot_state_store(reason, info1, info2);
reboot_state_store(reason, INFUSE_REBOOT_INFO_GENERIC, info1, info2, NULL);
/* Do the reboot */
cleanup_and_reboot();
}
Expand All @@ -147,7 +157,7 @@ void infuse_reboot_delayed(enum infuse_reboot_reason reason, uint32_t info1, uin
k_work_init_delayable(&reboot_worker, delayed_do_reboot);

/* Store initial reboot metadata */
reboot_state_store(reason, info1, info2);
reboot_state_store(reason, INFUSE_REBOOT_INFO_GENERIC, info1, info2, NULL);

/* Schedule the reboot */
k_work_schedule(&reboot_worker, delay);
Expand Down
7 changes: 7 additions & 0 deletions scripts/west_commands/cloud_definitions/tdf.json
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,13 @@
{"name": "num_wifi", "type": "uint8_t", "description": "Number of Wi-Fi APs found"},
{"name": "num_lte", "type": "uint8_t", "description": "Number of LTE cells found"}
]
},
"52": {
"name": "EXCEPTION_STACK_FRAME",
"description": "Generic exception stack frame",
"fields": [
{"name": "frame", "type": "uint32_t", "num":0, "description": "Stack frame value"}
]
}
}
}
2 changes: 1 addition & 1 deletion scripts/west_commands/release-diff.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from west.commands import WestCommand

try:
from infuse_iot.diff import diff
from infuse_iot.cpatch import diff
except ImportError:
diff = None

Expand Down
Loading