[no changelog]cepetr/low-power
parent
c7add9006d
commit
eeda4b067a
@ -0,0 +1,141 @@
|
||||
/*
|
||||
* This file is part of the Trezor project, https://trezor.io/
|
||||
*
|
||||
* Copyright (c) SatoshiLabs
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "rsod.h"
|
||||
#include "display.h"
|
||||
#include "mini_printf.h"
|
||||
#include "system.h"
|
||||
#include "terminal.h"
|
||||
|
||||
#define RSOD_DEFAULT_TITLE "INTERNAL ERROR";
|
||||
#define RSOD_DEFAULT_MESSAGE "UNSPECIFIED";
|
||||
#define RSOD_DEFAULT_FOOTER "PLEASE VISIT TREZOR.IO/RSOD";
|
||||
#define RSOD_EXIT_MESSAGE "EXIT %d"
|
||||
|
||||
#ifdef KERNEL_MODE
|
||||
|
||||
#define RSOD_FG_COLOR COLOR_WHITE
|
||||
|
||||
#ifdef USE_RGB_COLORS
|
||||
#define RSOD_BG_COLOR RGB16(0x7F, 0x00, 0x00)
|
||||
#else
|
||||
#define RSOD_BG_COLOR COLOR_BLACK
|
||||
#endif
|
||||
|
||||
void rsod_terminal(const systask_postmortem_t* info) {
|
||||
display_orientation(0);
|
||||
term_set_color(RSOD_FG_COLOR, RSOD_BG_COLOR);
|
||||
|
||||
const char* title = RSOD_DEFAULT_TITLE;
|
||||
const char* message = RSOD_DEFAULT_MESSAGE;
|
||||
const char* footer = RSOD_DEFAULT_FOOTER;
|
||||
const char* file = NULL;
|
||||
char message_buf[32] = {0};
|
||||
int line = 0;
|
||||
|
||||
switch (info->reason) {
|
||||
case TASK_TERM_REASON_EXIT:
|
||||
mini_snprintf(message_buf, sizeof(message_buf), RSOD_EXIT_MESSAGE,
|
||||
info->exit.code);
|
||||
message = message_buf;
|
||||
break;
|
||||
case TASK_TERM_REASON_ERROR:
|
||||
title = info->error.title;
|
||||
message = info->error.message;
|
||||
footer = info->error.footer;
|
||||
break;
|
||||
case TASK_TERM_REASON_FATAL:
|
||||
message = info->fatal.expr;
|
||||
file = info->fatal.file;
|
||||
line = info->fatal.line;
|
||||
break;
|
||||
case TASK_TERM_REASON_FAULT:
|
||||
message = system_fault_message(&info->fault);
|
||||
break;
|
||||
}
|
||||
|
||||
if (title != NULL) {
|
||||
term_printf("%s\n", title);
|
||||
}
|
||||
|
||||
if (message != NULL) {
|
||||
term_printf("msg : %s\n", message);
|
||||
}
|
||||
|
||||
if (file) {
|
||||
term_printf("file: %s:%d\n", file, line);
|
||||
}
|
||||
|
||||
#ifdef SCM_REVISION
|
||||
const uint8_t* rev = (const uint8_t*)SCM_REVISION;
|
||||
term_printf("rev : %02x%02x%02x%02x%02x\n", rev[0], rev[1], rev[2], rev[3],
|
||||
rev[4]);
|
||||
#endif
|
||||
|
||||
if (footer != NULL) {
|
||||
term_printf("\n%s\n", footer);
|
||||
}
|
||||
|
||||
display_backlight(255);
|
||||
}
|
||||
|
||||
#endif // KERNEL_MODE
|
||||
|
||||
#if (defined(FIRMWARE) || defined(BOOTLOADER)) && defined(FANCY_FATAL_ERROR)
|
||||
|
||||
#include "rust_ui.h"
|
||||
|
||||
void rsod_gui(const systask_postmortem_t* info) {
|
||||
const char* title = RSOD_DEFAULT_TITLE;
|
||||
const char* message = RSOD_DEFAULT_MESSAGE;
|
||||
const char* footer = RSOD_DEFAULT_FOOTER;
|
||||
char message_buf[128] = {0};
|
||||
|
||||
switch (info->reason) {
|
||||
case TASK_TERM_REASON_EXIT:
|
||||
mini_snprintf(message_buf, sizeof(message_buf), RSOD_EXIT_MESSAGE,
|
||||
info->exit.code);
|
||||
message = message_buf;
|
||||
break;
|
||||
|
||||
case TASK_TERM_REASON_ERROR:
|
||||
title = info->error.title;
|
||||
message = info->error.message;
|
||||
footer = info->error.footer;
|
||||
break;
|
||||
|
||||
case TASK_TERM_REASON_FATAL:
|
||||
message = info->fatal.expr;
|
||||
if (message[0] == '\0') {
|
||||
mini_snprintf(message_buf, sizeof(message_buf), "%s:%u",
|
||||
info->fatal.file, (unsigned int)info->fatal.line);
|
||||
message = message_buf;
|
||||
}
|
||||
break;
|
||||
|
||||
case TASK_TERM_REASON_FAULT:
|
||||
message = system_fault_message(&info->fault);
|
||||
break;
|
||||
}
|
||||
|
||||
// Render the RSOD in Rust
|
||||
display_rsod_rust(title, message, footer);
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* This file is part of the Trezor project, https://trezor.io/
|
||||
*
|
||||
* Copyright (c) SatoshiLabs
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TREZORHAL_APPLET_H
|
||||
#define TREZORHAL_APPLET_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef SYSCALL_DISPATCH
|
||||
|
||||
#include "systask.h"
|
||||
|
||||
// Applet entry point
|
||||
typedef void (*applet_startup_t)(const char* args, uint32_t random);
|
||||
|
||||
// Applet header found at the beginning of the applet binary
|
||||
typedef struct {
|
||||
// Stack area
|
||||
uint32_t stack_start;
|
||||
uint32_t stack_size;
|
||||
// Applet entry point
|
||||
applet_startup_t startup;
|
||||
} applet_header_t;
|
||||
|
||||
// Applet memory layout
|
||||
typedef struct {
|
||||
// Data area 1
|
||||
uint32_t data1_start;
|
||||
uint32_t data1_size;
|
||||
// Data area 2
|
||||
uint32_t data2_start;
|
||||
uint32_t data2_size;
|
||||
|
||||
} applet_layout_t;
|
||||
|
||||
typedef struct {
|
||||
// Points to the applet header found at the beginning of the applet binary
|
||||
applet_header_t* header;
|
||||
// Applet memory layout describing the memory areas
|
||||
// the applet is allowed to use
|
||||
applet_layout_t layout;
|
||||
// Applet task
|
||||
systask_t task;
|
||||
|
||||
// + privileges
|
||||
|
||||
} applet_t;
|
||||
|
||||
// Initializes the applet structure
|
||||
void applet_init(applet_t* applet, applet_header_t* header,
|
||||
applet_layout_t* layout);
|
||||
|
||||
// Resets the applet and prepares it for execution from its entry point.
|
||||
//
|
||||
// Applet does not start immediately, it needs to be scheduled by
|
||||
// `systask_yield_to(&applet->task)` after calling this function.
|
||||
void applet_reset(applet_t* applet, uint32_t cmd, const void* arg,
|
||||
size_t arg_size);
|
||||
|
||||
#endif // SYSCALL_DISPATCH
|
||||
|
||||
#endif // TREZORHAL_APPLET_H
|
@ -1,7 +0,0 @@
|
||||
#ifndef TREZORHAL_FAULT_HANDLERS_H
|
||||
#define TREZORHAL_FAULT_HANDLERS_H
|
||||
|
||||
// Initializes and enables fault handlers
|
||||
void fault_handlers_init(void);
|
||||
|
||||
#endif // TREZORHAL_FAULT_HANDLERS_H
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* This file is part of the Trezor project, https://trezor.io/
|
||||
*
|
||||
* Copyright (c) SatoshiLabs
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "applet.h"
|
||||
#include "mpu.h"
|
||||
#include "rng.h"
|
||||
#include "systask.h"
|
||||
|
||||
#ifdef SYSCALL_DISPATCH
|
||||
|
||||
void applet_init(applet_t* applet, applet_header_t* header,
|
||||
applet_layout_t* layout) {
|
||||
memset(applet, 0, sizeof(applet_t));
|
||||
|
||||
applet->header = header;
|
||||
applet->layout = *layout;
|
||||
}
|
||||
|
||||
static void applet_clear_memory(applet_t* applet) {
|
||||
if (applet->layout.data1_size > 0) {
|
||||
memset((void*)applet->layout.data1_start, 0, applet->layout.data1_size);
|
||||
}
|
||||
if (applet->layout.data2_size > 0) {
|
||||
memset((void*)applet->layout.data2_start, 0, applet->layout.data2_size);
|
||||
}
|
||||
}
|
||||
|
||||
void applet_reset(applet_t* applet, uint32_t cmd, const void* arg,
|
||||
size_t arg_size) {
|
||||
// Clear all memory the applet is allowed to use
|
||||
applet_clear_memory(applet);
|
||||
|
||||
// Reset the applet task (stack pointer, etc.)
|
||||
systask_init(&applet->task, applet->header->stack_start,
|
||||
applet->header->stack_size);
|
||||
|
||||
// Copy the arguments onto the applet stack
|
||||
void* arg_copy = NULL;
|
||||
if (arg != NULL && arg_size > 0) {
|
||||
arg_copy = systask_push_data(&applet->task, arg, arg_size);
|
||||
}
|
||||
|
||||
// Schedule the applet task run
|
||||
uint32_t arg1 = cmd;
|
||||
uint32_t arg2 = (uint32_t)arg_copy;
|
||||
uint32_t arg3 = rng_get();
|
||||
|
||||
systask_push_call(&applet->task, applet->header->startup, arg1, arg2, arg3);
|
||||
}
|
||||
|
||||
#endif // SYSCALL_DISPATCH
|
@ -1,58 +0,0 @@
|
||||
#include TREZOR_BOARD
|
||||
|
||||
#include "common.h"
|
||||
#include "mpu.h"
|
||||
|
||||
#ifdef KERNEL_MODE
|
||||
|
||||
void fault_handlers_init(void) {
|
||||
// Enable BUS fault and USAGE fault handlers
|
||||
SCB->SHCSR |= (SCB_SHCSR_USGFAULTENA_Msk | SCB_SHCSR_BUSFAULTENA_Msk);
|
||||
}
|
||||
|
||||
void HardFault_Handler(void) {
|
||||
mpu_reconfig(MPU_MODE_DEFAULT);
|
||||
error_shutdown("(HF)");
|
||||
}
|
||||
|
||||
void MemManage_Handler_MM(void) {
|
||||
mpu_reconfig(MPU_MODE_DEFAULT);
|
||||
error_shutdown("(MM)");
|
||||
}
|
||||
|
||||
void MemManage_Handler_SO(void) {
|
||||
mpu_reconfig(MPU_MODE_DEFAULT);
|
||||
error_shutdown("(SO)");
|
||||
}
|
||||
|
||||
void BusFault_Handler(void) {
|
||||
mpu_reconfig(MPU_MODE_DEFAULT);
|
||||
error_shutdown("(BF)");
|
||||
}
|
||||
|
||||
void UsageFault_Handler(void) {
|
||||
mpu_reconfig(MPU_MODE_DEFAULT);
|
||||
error_shutdown("(UF)");
|
||||
}
|
||||
|
||||
void NMI_Handler(void) {
|
||||
mpu_mode_t mpu_mode = mpu_reconfig(MPU_MODE_DEFAULT);
|
||||
// Clock Security System triggered NMI
|
||||
if ((RCC->CIR & RCC_CIR_CSSF) != 0) {
|
||||
error_shutdown("(CS)");
|
||||
}
|
||||
mpu_restore(mpu_mode);
|
||||
}
|
||||
|
||||
// from util.s
|
||||
extern void shutdown_privileged(void);
|
||||
|
||||
void PVD_IRQHandler(void) {
|
||||
mpu_reconfig(MPU_MODE_DEFAULT);
|
||||
#ifdef BACKLIGHT_PWM_TIM
|
||||
BACKLIGHT_PWM_TIM->BACKLIGHT_PWM_TIM_CCR = 0; // turn off display backlight
|
||||
#endif
|
||||
shutdown_privileged();
|
||||
}
|
||||
|
||||
#endif // KERNEL_MODE
|
@ -0,0 +1,601 @@
|
||||
/*
|
||||
* This file is part of the Trezor project, https://trezor.io/
|
||||
*
|
||||
* Copyright (c) SatoshiLabs
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include STM32_HAL_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "bootutils.h"
|
||||
#include "irq.h"
|
||||
#include "mpu.h"
|
||||
#include "syscall.h"
|
||||
#include "systask.h"
|
||||
#include "system.h"
|
||||
|
||||
#ifdef KERNEL_MODE
|
||||
|
||||
// Disable stack protector for this file since it may interfere
|
||||
// with the stack manipulation and fault handling
|
||||
#pragma GCC optimize("no-stack-protector")
|
||||
|
||||
#define STK_FRAME_R0 0
|
||||
#define STK_FRAME_R1 1
|
||||
#define STK_FRAME_R2 2
|
||||
#define STK_FRAME_R3 3
|
||||
#define STK_FRAME_R12 4
|
||||
#define STK_FRAME_LR 5
|
||||
#define STK_FRAME_RET_ADDR 6
|
||||
#define STK_FRAME_XPSR 7
|
||||
|
||||
// Task manager state
|
||||
typedef struct {
|
||||
// Error handler called when a kernel task terminates
|
||||
systask_error_handler_t error_handler;
|
||||
// Background kernel task
|
||||
systask_t kernel_task;
|
||||
// Currently running task
|
||||
systask_t* active_task;
|
||||
// Task to be scheduled next
|
||||
systask_t* waiting_task;
|
||||
} systask_scheduler_t;
|
||||
|
||||
// Kernel stack base pointer defined in linker script
|
||||
extern uint8_t _sstack;
|
||||
extern uint8_t _estack;
|
||||
|
||||
// Global task manager state
|
||||
systask_scheduler_t g_systask_scheduler = {
|
||||
.active_task = &g_systask_scheduler.kernel_task,
|
||||
.waiting_task = &g_systask_scheduler.kernel_task,
|
||||
.kernel_task = {
|
||||
.sp_lim = (uint32_t)&_sstack,
|
||||
}};
|
||||
|
||||
void systask_scheduler_init(systask_error_handler_t error_handler) {
|
||||
systask_scheduler_t* scheduler = &g_systask_scheduler;
|
||||
|
||||
memset(scheduler, 0, sizeof(systask_scheduler_t));
|
||||
|
||||
scheduler->error_handler = error_handler;
|
||||
scheduler->active_task = &scheduler->kernel_task;
|
||||
scheduler->waiting_task = scheduler->active_task;
|
||||
|
||||
scheduler->kernel_task.sp_lim = (uint32_t)&_sstack;
|
||||
|
||||
// SVCall priority should be the lowest since it is
|
||||
// generally a blocking operation
|
||||
NVIC_SetPriority(SVCall_IRQn, IRQ_PRI_LOWEST);
|
||||
NVIC_SetPriority(PendSV_IRQn, IRQ_PRI_LOWEST);
|
||||
|
||||
// Enable BusFault and UsageFault handlers
|
||||
SCB->SHCSR |= (SCB_SHCSR_USGFAULTENA_Msk | SCB_SHCSR_BUSFAULTENA_Msk);
|
||||
}
|
||||
|
||||
systask_t* systask_active(void) {
|
||||
systask_scheduler_t* scheduler = &g_systask_scheduler;
|
||||
|
||||
return scheduler->active_task;
|
||||
}
|
||||
|
||||
static void systask_yield(void) {
|
||||
bool handler_mode = (__get_IPSR() & IPSR_ISR_Msk) != 0;
|
||||
|
||||
if (handler_mode) {
|
||||
SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;
|
||||
__DSB();
|
||||
} else {
|
||||
// SVC_SYSTASK_YIELD is the only SVC that is allowed to be invoked from
|
||||
// kernel itself, and it is used to start the unprivileged application code.
|
||||
__asm__ volatile("svc %[svid]\n"
|
||||
: // no output
|
||||
: [svid] "i"(SVC_SYSTASK_YIELD)
|
||||
: "memory");
|
||||
}
|
||||
}
|
||||
|
||||
void systask_yield_to(systask_t* task) {
|
||||
systask_scheduler_t* scheduler = &g_systask_scheduler;
|
||||
|
||||
irq_key_t irq_key = irq_lock();
|
||||
scheduler->waiting_task = task;
|
||||
irq_unlock(irq_key);
|
||||
|
||||
systask_yield();
|
||||
}
|
||||
|
||||
void systask_init(systask_t* task, uint32_t stack_ptr, uint32_t stack_size) {
|
||||
memset(task, 0, sizeof(systask_t));
|
||||
task->sp = stack_ptr + stack_size;
|
||||
task->sp_lim = stack_ptr + 256;
|
||||
task->exc_return = 0xFFFFFFED; // Thread mode, use PSP, pop FP context
|
||||
task->mpu_mode = MPU_MODE_APP;
|
||||
}
|
||||
|
||||
uint32_t* systask_push_data(systask_t* task, const void* data, size_t size) {
|
||||
uint32_t stack_remaining = task->sp - task->sp_lim;
|
||||
if (stack_remaining < size) {
|
||||
return NULL;
|
||||
}
|
||||
task->sp -= size;
|
||||
|
||||
if (data != NULL) {
|
||||
memcpy((void*)task->sp, data, size);
|
||||
} else {
|
||||
memset((void*)task->sp, 0, size);
|
||||
}
|
||||
|
||||
return (void*)task->sp;
|
||||
}
|
||||
|
||||
void systask_pop_data(systask_t* task, size_t size) { task->sp += size; }
|
||||
|
||||
void systask_push_call(systask_t* task, void* entrypoint, uint32_t arg1,
|
||||
uint32_t arg2, uint32_t arg3) {
|
||||
// Align stack pointer to 8 bytes
|
||||
task->sp &= ~7;
|
||||
|
||||
// FP extension context
|
||||
systask_push_data(task, NULL, 0x48);
|
||||
// Standard exception frame
|
||||
uint32_t* stk_frame = systask_push_data(task, NULL, 0x20);
|
||||
// Registers r4-r11
|
||||
systask_push_data(task, NULL, 0x20);
|
||||
// Registers s16-s31
|
||||
systask_push_data(task, NULL, 0x40);
|
||||
|
||||
// Return to thread mode, use PSP, pop FP context
|
||||
task->exc_return = 0xFFFFFFED;
|
||||
|
||||
stk_frame[STK_FRAME_R0] = arg1;
|
||||
stk_frame[STK_FRAME_R1] = arg2;
|
||||
stk_frame[STK_FRAME_R2] = arg3;
|
||||
stk_frame[STK_FRAME_RET_ADDR] = (uint32_t)entrypoint & ~1;
|
||||
stk_frame[STK_FRAME_XPSR] = 0x01000000; // T (Thumb state) bit set
|
||||
}
|
||||
|
||||
static void systask_kill(systask_t* task) {
|
||||
systask_scheduler_t* scheduler = &g_systask_scheduler;
|
||||
|
||||
task->killed = 1;
|
||||
|
||||
if (task == &scheduler->kernel_task) {
|
||||
// Call panic handler
|
||||
if (scheduler->error_handler != NULL) {
|
||||
scheduler->error_handler(&task->pminfo);
|
||||
}
|
||||
secure_shutdown();
|
||||
} else if (task == scheduler->active_task) {
|
||||
// Switch to the kernel task
|
||||
systask_yield_to(&scheduler->kernel_task);
|
||||
}
|
||||
}
|
||||
|
||||
void systask_exit(systask_t* task, int exit_code) {
|
||||
systask_scheduler_t* scheduler = &g_systask_scheduler;
|
||||
|
||||
if (task == NULL) {
|
||||
bool handler_mode = (__get_IPSR() & IPSR_ISR_Msk) != 0;
|
||||
task = handler_mode ? &scheduler->kernel_task : scheduler->active_task;
|
||||
}
|
||||
|
||||
systask_postmortem_t* pminfo = &task->pminfo;
|
||||
|
||||
memset(pminfo, 0, sizeof(systask_postmortem_t));
|
||||
pminfo->reason = TASK_TERM_REASON_EXIT;
|
||||
pminfo->privileged = (task == &scheduler->kernel_task);
|
||||
pminfo->exit.code = exit_code;
|
||||
|
||||
systask_kill(task);
|
||||
}
|
||||
|
||||
void systask_exit_error(systask_t* task, const char* title, const char* message,
|
||||
const char* footer) {
|
||||
systask_scheduler_t* scheduler = &g_systask_scheduler;
|
||||
|
||||
if (task == NULL) {
|
||||
bool handler_mode = (__get_IPSR() & IPSR_ISR_Msk) != 0;
|
||||
task = handler_mode ? &scheduler->kernel_task : scheduler->active_task;
|
||||
}
|
||||
|
||||
systask_postmortem_t* pminfo = &task->pminfo;
|
||||
|
||||
memset(pminfo, 0, sizeof(systask_postmortem_t));
|
||||
pminfo->reason = TASK_TERM_REASON_ERROR;
|
||||
pminfo->privileged = (task == &scheduler->kernel_task);
|
||||
|
||||
if (title != NULL) {
|
||||
strncpy(pminfo->error.title, title, sizeof(pminfo->error.title) - 1);
|
||||
}
|
||||
|
||||
if (message != NULL) {
|
||||
strncpy(pminfo->error.message, message, sizeof(pminfo->error.message) - 1);
|
||||
}
|
||||
|
||||
if (footer != NULL) {
|
||||
strncpy(pminfo->error.footer, footer, sizeof(pminfo->error.footer) - 1);
|
||||
}
|
||||
|
||||
systask_kill(task);
|
||||
}
|
||||
|
||||
void systask_exit_fatal(systask_t* task, const char* message, const char* file,
|
||||
int line) {
|
||||
systask_scheduler_t* scheduler = &g_systask_scheduler;
|
||||
|
||||
if (task == NULL) {
|
||||
bool handler_mode = (__get_IPSR() & IPSR_ISR_Msk) != 0;
|
||||
task = handler_mode ? &scheduler->kernel_task : scheduler->active_task;
|
||||
}
|
||||
|
||||
systask_postmortem_t* pminfo = &task->pminfo;
|
||||
|
||||
memset(pminfo, 0, sizeof(systask_postmortem_t));
|
||||
pminfo->reason = TASK_TERM_REASON_FATAL;
|
||||
pminfo->privileged = (task == &scheduler->kernel_task);
|
||||
|
||||
if (message != NULL) {
|
||||
strncpy(pminfo->fatal.expr, message, sizeof(pminfo->fatal.expr) - 1);
|
||||
}
|
||||
|
||||
if (file != NULL) {
|
||||
strncpy(pminfo->fatal.file, file, sizeof(pminfo->fatal.file) - 1);
|
||||
}
|
||||
|
||||
pminfo->fatal.line = line;
|
||||
|
||||
systask_kill(task);
|
||||
}
|
||||
|
||||
// Terminate active task from fault/exception handler
|
||||
__attribute((used)) static void systask_exit_fault(bool privileged,
|
||||
uint32_t sp) {
|
||||
mpu_mode_t mpu_mode = mpu_reconfig(MPU_MODE_DEFAULT);
|
||||
|
||||
systask_scheduler_t* scheduler = &g_systask_scheduler;
|
||||
|
||||
systask_t* task =
|
||||
privileged ? &scheduler->kernel_task : scheduler->active_task;
|
||||
|
||||
systask_postmortem_t* pminfo = &task->pminfo;
|
||||
|
||||
// Do not overwrite the reason if it is already set to fault
|
||||
// (exception handlers may call this function multiple times, and
|
||||
// we want to preserve the first reason)
|
||||
if (pminfo->reason != TASK_TERM_REASON_FAULT) {
|
||||
pminfo->reason = TASK_TERM_REASON_FAULT;
|
||||
pminfo->privileged = privileged;
|
||||
pminfo->fault.sp = sp;
|
||||
#if !(defined(__ARM_ARCH_8M_MAIN__) || defined(__ARM_ARCH_8M_BASE__))
|
||||
pminfo->fault.sp_lim = task->sp_lim;
|
||||
#endif
|
||||
pminfo->fault.irqn = (__get_IPSR() & IPSR_ISR_Msk) - 16;
|
||||
pminfo->fault.cfsr = SCB->CFSR;
|
||||
pminfo->fault.mmfar = SCB->MMFAR;
|
||||
pminfo->fault.bfar = SCB->BFAR;
|
||||
pminfo->fault.hfsr = SCB->HFSR;
|
||||
}
|
||||
|
||||
systask_kill(task);
|
||||
|
||||
mpu_restore(mpu_mode);
|
||||
}
|
||||
|
||||
// C part of PendSV handler that switches tasks
|
||||
//
|
||||
// `sp` is the stack pointer of the current task
|
||||
// `sp_lim` is the stack pointer limit of the current task
|
||||
// `exc_return` is the execution state of the current task
|
||||
//
|
||||
// Returns the context struct of the next task
|
||||
__attribute((no_stack_protector, used)) static uint32_t scheduler_pendsv(
|
||||
uint32_t sp, uint32_t sp_lim, uint32_t exc_return) {
|
||||
systask_scheduler_t* scheduler = &g_systask_scheduler;
|
||||
|
||||
// Save the current task context
|
||||
systask_t* prev_task = scheduler->active_task;
|
||||
prev_task->sp = sp;
|
||||
#if defined(__ARM_ARCH_8M_MAIN__) || defined(__ARM_ARCH_8M_BASE__)
|
||||
// sp_lim is not valid on ARMv7-M
|
||||
prev_task->sp_lim = sp_lim;
|
||||
#endif
|
||||
prev_task->exc_return = exc_return;
|
||||
prev_task->mpu_mode = mpu_get_mode();
|
||||
|
||||
// Switch to the next task
|
||||
scheduler->active_task = scheduler->waiting_task;
|
||||
|
||||
// Load the scheduled task context
|
||||
systask_t* next_task = scheduler->active_task;
|
||||
|
||||
// Set task privilege level
|
||||
uint32_t control = __get_CONTROL();
|
||||
if (next_task == &scheduler->kernel_task) {
|
||||
control &= ~CONTROL_nPRIV_Msk;
|
||||
} else {
|
||||
control |= CONTROL_nPRIV_Msk;
|
||||
}
|
||||
__set_CONTROL(control);
|
||||
|
||||
// Setup the MPU for the new task
|
||||
mpu_reconfig(next_task->mpu_mode);
|
||||
|
||||
return (uint32_t)next_task;
|
||||
}
|
||||
|
||||
__attribute__((naked, no_stack_protector)) void PendSV_Handler(void) {
|
||||
__asm__ volatile(
|
||||
"LDR R0, =%[active_task] \n"
|
||||
"LDR R1, =%[waiting_task]\n"
|
||||
"CMP R0, R1 \n"
|
||||
"BEQ 3f \n" // No task switch needed
|
||||
|
||||
"LDR R0, [R0] \n" // R0 = active_task
|
||||
"LDR R0, [R0, #12] \n" // R0 = active_task->killed
|
||||
"CMP R0, #0 \n"
|
||||
"BEQ 1f \n" // =0 => normal processing
|
||||
|
||||
// We are switching from a killed task to the kernel task.
|
||||
// Since the reason might be a stack overflow, we must not
|
||||
// attempt to save the task context.
|
||||
|
||||
"LDR R1, = 0xE000EF34 \n" // FPU->FPCCR
|
||||
"LDR R0, [R1] \n"
|
||||
"BIC R0, R0, #1 \n" // Clear LSPACT to suppress later lazy
|
||||
"STR R0, [R1] \n" // stacking to the killed task stack
|
||||
|
||||
"MOV R0, #0 \n" // Skip context save
|
||||
"MOV R1, R0 \n" //
|
||||
"MOV R2, R0 \n" //
|
||||
"B 2f \n" //
|
||||
|
||||
"1: \n"
|
||||
|
||||
// Save the current task context on its stack before switching
|
||||
|
||||
"TST LR, #0x4 \n" // Return stack (1=>PSP, 0=>MSP)
|
||||
|
||||
#if defined(__ARM_ARCH_8M_MAIN__) || defined(__ARM_ARCH_8M_BASE__)
|
||||
"ITTEE EQ \n"
|
||||
"MRSEQ R0, MSP \n" // Get current SP
|
||||
"MRSEQ R1, MSPLIM \n" // Get current SP Limit
|
||||
"MRSNE R0, PSP \n"
|
||||
"MRSNE R1, PSPLIM \n"
|
||||
#else
|
||||
"ITE EQ \n"
|
||||
"MRSEQ R0, MSP \n" // Get current SP
|
||||
"MRSNE R0, PSP \n"
|
||||
"MOV R1, #0 \n" // (fake SPLIM)
|
||||
#endif
|
||||
"IT EQ \n" // If using main stack:
|
||||
"SUBEQ SP, SP, #0x60 \n" // reserve space for R4-11 and S16-S31
|
||||
|
||||
"MOV R2, LR \n" // Get current EXC_RETURN
|
||||
|
||||
"STMDB R0!, {R4-R11} \n" // Save R4-R11 to SP Frame Stack
|
||||
"TST LR, #0x10 \n" // Check EXC_RETURN.Ftype bit to see if
|
||||
// the current thread has a FP context
|
||||
"IT EQ \n"
|
||||
"VSTMDBEQ R0!, {S16-S31} \n" // If so, save S16-S31 FP addition
|
||||
// context, that will also trigger lazy
|
||||
// fp context preservation of S0-S15
|
||||
"2: \n"
|
||||
|
||||
"BL scheduler_pendsv \n" // Save SP value of current task
|
||||
"LDR LR, [R0, #8] \n" // Get the EXC_RETURN value
|
||||
"LDR R1, [R0, #4] \n" // Get the SP_LIM value
|
||||
"LDR R0, [R0, #0] \n" // Get the SP value
|
||||
|
||||
"TST LR, #0x10 \n" // Check EXC_RETURN.Ftype bit to see if
|
||||
// the next thread has a FP context
|
||||
"IT EQ \n"
|
||||
"VLDMIAEQ R0!, {S16-S31} \n" // If so, restore S16-S31
|
||||
"LDMIA R0!, {R4-R11} \n" // Restore R4-R11
|
||||
|
||||
"TST LR, #0x4 \n" // Return stack (1=>PSP, 0=>MSP)
|
||||
#if defined(__ARM_ARCH_8M_MAIN__) || defined(__ARM_ARCH_8M_BASE__)
|
||||
"ITEE EQ \n"
|
||||
"MSREQ MSP, R0 \n" // Update MSP
|
||||
"MSRNE PSPLIM, R1 \n" // Update PSPLIM & PSP
|
||||
"MSRNE PSP, R0 \n"
|
||||
#else
|
||||
"ITE EQ \n"
|
||||
"MSREQ MSP, R0 \n" // Update the MSP
|
||||
"MSRNE PSP, R0 \n" // Update the PSP
|
||||
#endif
|
||||
"3: "
|
||||
"BX LR \n"
|
||||
: // No output
|
||||
: [active_task] "i"(&g_systask_scheduler.active_task), // Input
|
||||
[waiting_task] "i"(&g_systask_scheduler.waiting_task) // Input
|
||||
: // Clobber
|
||||
);
|
||||
}
|
||||
|
||||
__attribute__((no_stack_protector, used)) static uint32_t svc_handler(
|
||||
uint32_t* stack, uint32_t* msp, uint32_t exc_return, uint32_t r4,
|
||||
uint32_t r5, uint32_t r6) {
|
||||
uint8_t svc_number = ((uint8_t*)stack[6])[-2];
|
||||
|
||||
#ifdef SYSCALL_DISPATCH
|
||||
uint32_t args[6] = {stack[0], stack[1], stack[2], stack[3], r4, r5};
|
||||
#endif
|
||||
|
||||
mpu_mode_t mpu_mode = mpu_reconfig(MPU_MODE_DEFAULT);
|
||||
|
||||
switch (svc_number) {
|
||||
#ifdef SYSTEM_VIEW
|
||||
case SVC_GET_DWT_CYCCNT:
|
||||
cyccnt_cycles = *DWT_CYCCNT_ADDR;
|
||||
break;
|
||||
#endif
|
||||
case SVC_SYSTASK_YIELD:
|
||||
// Yield to the waiting task
|
||||
systask_yield();
|
||||
break;
|
||||
#ifdef SYSCALL_DISPATCH
|
||||
case SVC_SYSCALL:
|
||||
syscall_handler(args, r6);
|
||||
stack[0] = args[0];
|
||||
stack[1] = args[1];
|
||||
break;
|
||||
case SVC_CALLBACK_RETURN:
|
||||
// g_return_value = args[0]
|
||||
// exc_return = return_from_callback;
|
||||
mpu_restore(mpu_mode);
|
||||
return_from_app_callback(args[0], msp);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
mpu_restore(mpu_mode);
|
||||
return exc_return;
|
||||
}
|
||||
|
||||
__attribute((naked, no_stack_protector)) void SVC_Handler(void) {
|
||||
__asm__ volatile(
|
||||
"TST LR, #0x4 \n" // Return stack (1=>PSP, 0=>MSP)
|
||||
"ITE EQ \n"
|
||||
"MRSEQ R0, MSP \n" // `stack` argument
|
||||
"MRSNE R0, PSP \n"
|
||||
"TST LR, #0x20 \n"
|
||||
"IT EQ \n"
|
||||
"ADDEQ R0, R0, #0x40 \n"
|
||||
"MRS R1, MSP \n" // `msp` argument
|
||||
"MOV R2, LR \n" // `exc_return` argument
|
||||
"MOV R3, R4 \n" // 'r4' argument
|
||||
"PUSH {R5, R6} \n" // 'r5' and 'r6' arguments on stack
|
||||
"BL svc_handler \n"
|
||||
"POP {R5, R6} \n"
|
||||
"BX R0 \n" // Branch to the returned value
|
||||
);
|
||||
}
|
||||
|
||||
__attribute__((naked, no_stack_protector)) void HardFault_Handler(void) {
|
||||
// A HardFault may also be caused by exception escalation.
|
||||
// To ensure we have enough space to handle the exception,
|
||||
// we set the stack pointer to the end of the stack.
|
||||
|
||||
extern uint8_t _estack;
|
||||
|
||||
__asm__ volatile(
|
||||
"MRS R1, MSP \n" // R1 = MSP
|
||||
"LDR R0, =%[estack] \n" // Reset main stack
|
||||
"MSR MSP, R0 \n" //
|
||||
"MOV R0, #1 \n" // R0 = 1 (Privileged)
|
||||
"B systask_exit_fault \n" // Exit task with fault
|
||||
:
|
||||
: [estack] "i"(&_estack)
|
||||
: "memory");
|
||||
}
|
||||
|
||||
__attribute__((naked, no_stack_protector)) void MemManage_Handler(void) {
|
||||
__asm__ volatile(
|
||||
"TST LR, #0x4 \n" // Return stack (1=>PSP, 0=>MSP)
|
||||
"ITTEE EQ \n"
|
||||
"MOVEQ R0, #1 \n" // R0 = 1 (Privileged)
|
||||
"MRSEQ R1, MSP \n" // R1 = MSP
|
||||
"MOVNE R0, #0 \n" // R0 = 0 (Unprivileged)
|
||||
"MRSNE R1, PSP \n" // R1 = PSP
|
||||
#if !(defined(__ARM_ARCH_8M_MAIN__) || defined(__ARM_ARCH_8M_BASE__))
|
||||
"CMP R0, #0 \n"
|
||||
"BEQ 1f \n" // Skip stack ptr checking for PSP
|
||||
"LDR R2, =%[sstack] \n"
|
||||
"CMP R1, R2 \n" // Check if PSP is below the stack
|
||||
"ITT LO \n" // base
|
||||
"LDRLO R2, =%[estack] \n"
|
||||
"MSRLO MSP, R2 \n" // Reset MSP
|
||||
"1: \n"
|
||||
#endif
|
||||
"B systask_exit_fault \n" // Exit task with fault
|
||||
:
|
||||
: [estack] "i"(&_estack), [sstack] "i"((uint32_t)&_sstack + 256)
|
||||
: "memory");
|
||||
}
|
||||
|
||||
__attribute__((naked, no_stack_protector)) void BusFault_Handler(void) {
|
||||
__asm__ volatile(
|
||||
"TST LR, #0x4 \n" // Return stack (1=>PSP, 0=>MSP)
|
||||
"ITTEE EQ \n"
|
||||
"MOVEQ R0, #1 \n" // R0 = 1 (Privileged)
|
||||
"MRSEQ R1, MSP \n" // R1 = MSP
|
||||
"MOVNE R0, #0 \n" // R0 = 0 (Unprivileged)
|
||||
"MRSNE R1, PSP \n" // R1 = PSP
|
||||
"B systask_exit_fault \n" // Exit task with fault
|
||||
);
|
||||
}
|
||||
|
||||
__attribute__((naked, no_stack_protector)) void UsageFault_Handler(void) {
|
||||
__asm__ volatile(
|
||||
"TST LR, #0x4 \n" // Return stack (1=>PSP, 0=>MSP)
|
||||
"ITTEE EQ \n"
|
||||
"MOVEQ R0, #1 \n" // R0 = 1 (Privileged)
|
||||
"MRSEQ R1, MSP \n" // R1 = MSP
|
||||
"MOVNE R0, #0 \n" // R0 = 0 (Unprivileged)
|
||||
"MRSNE R1, PSP \n" // R1 = PSP
|
||||
"B systask_exit_fault \n" // Exit task with fault
|
||||
);
|
||||
}
|
||||
|
||||
#if defined(__ARM_ARCH_8M_MAIN__) || defined(__ARM_ARCH_8M_BASE__)
|
||||
__attribute__((naked, no_stack_protector)) void SecureFault_Handler(void) {
|
||||
__asm__ volatile(
|
||||
"TST LR, #0x4 \n" // Return stack (1=>PSP, 0=>MSP)
|
||||
"ITTEE EQ \n"
|
||||
"MOVEQ R0, #1 \n" // R0 = 1 (Privileged)
|
||||
"MRSEQ R1, MSP \n" // R1 = MSP
|
||||
"MOVNE R0, #0 \n" // R0 = 0 (Unprivileged)
|
||||
"MRSNE R1, PSP \n" // R1 = PSP
|
||||
"B systask_exit_fault \n" // Exit task with fault
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef STM32U5
|
||||
void GTZC_IRQHandler(void) { systask_exit_fault(true, __get_MSP()); }
|
||||
#endif
|
||||
|
||||
void NMI_Handler(void) {
|
||||
mpu_mode_t mpu_mode = mpu_reconfig(MPU_MODE_DEFAULT);
|
||||
#ifdef STM32U5
|
||||
if ((RCC->CIFR & RCC_CIFR_CSSF) != 0) {
|
||||
#else
|
||||
if ((RCC->CIR & RCC_CIR_CSSF) != 0) {
|
||||
#endif
|
||||
// Clock Security System triggered NMI
|
||||
systask_exit_fault(true, __get_MSP());
|
||||
}
|
||||
mpu_restore(mpu_mode);
|
||||
}
|
||||
|
||||
// from util.s
|
||||
extern void shutdown_privileged(void);
|
||||
|
||||
void PVD_PVM_IRQHandler(void) {
|
||||
mpu_reconfig(MPU_MODE_DEFAULT);
|
||||
#ifdef BACKLIGHT_PWM_TIM
|
||||
// Turn off display backlight
|
||||
BACKLIGHT_PWM_TIM->BACKLIGHT_PWM_TIM_CCR = 0;
|
||||
#endif
|
||||
shutdown_privileged();
|
||||
}
|
||||
|
||||
#endif // KERNEL_MODE
|
@ -0,0 +1,315 @@
|
||||
/*
|
||||
* This file is part of the Trezor project, https://trezor.io/
|
||||
*
|
||||
* Copyright (c) SatoshiLabs
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include STM32_HAL_H
|
||||
|
||||
#include "system.h"
|
||||
#include "bootutils.h"
|
||||
#include "mpu.h"
|
||||
#include "systask.h"
|
||||
#include "systick.h"
|
||||
#include "systimer.h"
|
||||
|
||||
#ifdef KERNEL_MODE
|
||||
|
||||
void system_init(systask_error_handler_t error_handler) {
|
||||
mpu_init();
|
||||
mpu_reconfig(MPU_MODE_DEFAULT);
|
||||
systask_scheduler_init(error_handler);
|
||||
systick_init();
|
||||
systimer_init();
|
||||
}
|
||||
|
||||
void system_exit(int exitcode) { systask_exit(NULL, exitcode); }
|
||||
|
||||
void system_exit_error(const char* title, const char* message,
|
||||
const char* footer) {
|
||||
systask_exit_error(NULL, title, message, footer);
|
||||
}
|
||||
|
||||
void system_exit_fatal(const char* message, const char* file, int line) {
|
||||
systask_exit_fatal(NULL, message, file, line);
|
||||
}
|
||||
|
||||
#endif // KERNEL_MODE
|
||||
|
||||
#ifndef HardFault_IRQn
|
||||
#define HardFault_IRQn (-13) // not defined in stm32lib/cmsis/stm32429xx.h
|
||||
#endif
|
||||
|
||||
const char* system_fault_message(const system_fault_t* fault) {
|
||||
switch (fault->irqn) {
|
||||
case HardFault_IRQn:
|
||||
return "(HF)";
|
||||
|
||||
case MemoryManagement_IRQn:
|
||||
#if !(defined(__ARM_ARCH_8M_MAIN__) || defined(__ARM_ARCH_8M_BASE__))
|
||||
if (fault->sp < fault->sp_lim) {
|
||||
return "(SO)";
|
||||
} else {
|
||||
return "(MM)";
|
||||
}
|
||||
#else
|
||||
return "(MM)";
|
||||
#endif
|
||||
|
||||
case BusFault_IRQn:
|
||||
return "(BF)";
|
||||
|
||||
case UsageFault_IRQn:
|
||||
#if defined(__ARM_ARCH_8M_MAIN__) || defined(__ARM_ARCH_8M_BASE__)
|
||||
if (fault->cfsr & SCB_CFSR_STKOF_Msk) {
|
||||
return "(SO)";
|
||||
} else {
|
||||
return "(UF)";
|
||||
}
|
||||
#else
|
||||
return "(UF)";
|
||||
#endif
|
||||
|
||||
#if defined(__ARM_ARCH_8M_MAIN__) || defined(__ARM_ARCH_8M_BASE__)
|
||||
case SecureFault_IRQn:
|
||||
return "(SF)";
|
||||
#endif
|
||||
|
||||
#ifdef STM32U5
|
||||
case GTZC_IRQn:
|
||||
return "(IA)";
|
||||
#endif
|
||||
|
||||
case NonMaskableInt_IRQn:
|
||||
return "(CS)";
|
||||
}
|
||||
return "(FAULT)";
|
||||
}
|
||||
|
||||
__attribute__((used)) static void emergency_reset(void) {
|
||||
// TODO: reset peripherals (at least DMA, DMA2D)
|
||||
|
||||
// Disable all NVIC interrupts and clear pending flags
|
||||
// so later the global interrupt can be re-enabled without
|
||||
// firing any pending interrupt
|
||||
for (int irqn = 0; irqn < 255; irqn++) {
|
||||
NVIC_DisableIRQ(irqn);
|
||||
NVIC_ClearPendingIRQ(irqn);
|
||||
}
|
||||
|
||||
// Disable SysTick
|
||||
SysTick->CTRL = 0;
|
||||
|
||||
// Clear PENDSV flag to prevent the PendSV_Handler call
|
||||
SCB->ICSR &= ~SCB_ICSR_PENDSVSET_Msk;
|
||||
|
||||
// Clear SCB->SHCSR exception flags so we can return back
|
||||
// to thread mode without any exception active
|
||||
|
||||
uint32_t preserved_flag = 0;
|
||||
|
||||
switch ((__get_IPSR() & IPSR_ISR_Msk) - 16) {
|
||||
case HardFault_IRQn:
|
||||
break;
|
||||
case MemoryManagement_IRQn:
|
||||
preserved_flag = SCB_SHCSR_MEMFAULTACT_Msk;
|
||||
break;
|
||||
case BusFault_IRQn:
|
||||
preserved_flag = SCB_SHCSR_BUSFAULTACT_Msk;
|
||||
break;
|
||||
case UsageFault_IRQn:
|
||||
preserved_flag = SCB_SHCSR_USGFAULTACT_Msk;
|
||||
break;
|
||||
case PendSV_IRQn:
|
||||
preserved_flag = SCB_SHCSR_PENDSVACT_Msk;
|
||||
break;
|
||||
case SysTick_IRQn:
|
||||
preserved_flag = SCB_SHCSR_SYSTICKACT_Msk;
|
||||
break;
|
||||
case SVCall_IRQn:
|
||||
preserved_flag = SCB_SHCSR_SVCALLACT_Msk;
|
||||
break;
|
||||
}
|
||||
|
||||
const uint32_t cleared_flags =
|
||||
SCB_SHCSR_MEMFAULTACT_Msk | SCB_SHCSR_BUSFAULTACT_Msk |
|
||||
SCB_SHCSR_USGFAULTACT_Msk | SCB_SHCSR_SVCALLACT_Msk |
|
||||
SCB_SHCSR_MONITORACT_Msk | SCB_SHCSR_PENDSVACT_Msk |
|
||||
SCB_SHCSR_SYSTICKACT_Msk;
|
||||
|
||||
SCB->SHCSR &= ~(cleared_flags & ~preserved_flag);
|
||||
}
|
||||
|
||||
__attribute((naked, no_stack_protector)) void system_emergency_rescue(
|
||||
systask_error_handler_t error_handler, const systask_postmortem_t* pminfo) {
|
||||
extern uint32_t __stack_chk_guard;
|
||||
|
||||
__asm__ volatile(
|
||||
"MOV R5, R1 \n" // R5 = pminfo
|
||||
"MOV R6, R0 \n" // R6 = error_handler
|
||||
|
||||
"CPSID I \n" // Disable interrupts
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// Disable MPU
|
||||
// --------------------------------------------------------------
|
||||
|
||||
"DMB 0xF \n" // Data memory barrier
|
||||
"LDR R0, =0xE000ED94 \n" // MPU->CTRL
|
||||
"MOV R1, #0 \n"
|
||||
"STR R1, [R0] \n" // Disable MPU
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// Setup new stack
|
||||
// --------------------------------------------------------------
|
||||
|
||||
"LDR R0, =_estack \n" // Setup new stack
|
||||
"MSR MSP, R0 \n" // Set MSP
|
||||
#if defined(__ARM_ARCH_8M_MAIN__) || defined(__ARM_ARCH_8M_BASE__)
|
||||
"LDR R0, =_sstack \n"
|
||||
"ADD R0, R0, #256 \n" // Add safety margin
|
||||
"MSR MSPLIM, R0 \n" // Set MSPLIM
|
||||
#endif
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// Copy pminfo to new stack
|
||||
// --------------------------------------------------------------
|
||||
|
||||
"LDR R2, =%[PMINFO_SIZE] \n" // Copy pminfo to new stack
|
||||
"SUB SP, R2 \n" // Allocate space for pminfo
|
||||
"MOV R0, SP \n" // Destination
|
||||
"MOV R1, R5 \n" // Source
|
||||
"MOV R5, R0 \n" // R5 = pminfo on the new stack
|
||||
"BL memcpy \n"
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// Save stack protector guard
|
||||
// --------------------------------------------------------------
|
||||
|
||||
"LDR R0, =%[STK_GUARD] \n" // Save stack protector guard
|
||||
"LDR R7, [R0] \n" // R7 = __stack_chk_guard
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// Clear .bss, initialize .data, ...
|
||||
// --------------------------------------------------------------
|
||||
|
||||
"LDR R0, =bss_start \n" // Clear .bss
|
||||
"MOV R1, #0 \n"
|
||||
"LDR R2, =bss_end \n"
|
||||
"SUB R2, R2, R0 \n"
|
||||
"BL memset \n"
|
||||
|
||||
"LDR R0, =data_vma \n" // Initialize .data
|
||||
"LDR R1, =data_lma \n"
|
||||
"LDR R2, =data_size \n"
|
||||
"BL memcpy \n"
|
||||
|
||||
#ifdef STM32U5
|
||||
"LDR R0, =confidential_vma \n" // Initialize .confidental
|
||||
"LDR R1, =confidential_lma \n"
|
||||
"LDR R2, =confidential_size \n"
|
||||
"BL memcpy \n"
|
||||
#endif
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// Restore stack protector guard
|
||||
// --------------------------------------------------------------
|
||||
|
||||
"LDR R0, =%[STK_GUARD] \n" // Restore stack protector guard
|
||||
"STR R7, [R0] \n"
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// Reset critical hardware so we can safely enable interrupts
|
||||
// --------------------------------------------------------------
|
||||
|
||||
"BL emergency_reset \n"
|
||||
|
||||
"CPSIE I \n" // Re-enable interrupts
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// Clear all VFP registers
|
||||
// --------------------------------------------------------------
|
||||
|
||||
"LDR R1, = 0xE000EF34 \n" // FPU->FPCCR
|
||||
"LDR R0, [R1] \n"
|
||||
"BIC R0, R0, #1 \n" // Clear LSPACT to suppress lazy
|
||||
// stacking
|
||||
"STR R0, [R1] \n"
|
||||
|
||||
// TODO: clear VFP registers (maybe for ARMV7-M only)
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// Clear R7-R11 registers
|
||||
// --------------------------------------------------------------
|
||||
|
||||
"MOV R0, #0 \n"
|
||||
"MOV R7, R0 \n"
|
||||
"MOV R8, R0 \n"
|
||||
"MOV R9, R0 \n"
|
||||
"MOV R10, R0 \n"
|
||||
"MOV R11, R0 \n"
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// Check if we are in thread mode and if yes, jump to error_handler
|
||||
// --------------------------------------------------------------
|
||||
|
||||
"LDR R1, =0x1FF \n" // Get lower 9 bits of IPSR
|
||||
"MRS R0, IPSR \n"
|
||||
"ANDS R0, R0, R1 \n"
|
||||
"CMP R0, #0 \n" // == 0 if in thread mode
|
||||
"ITTT EQ \n"
|
||||
"MOVEQ R0, R5 \n" // R0 = pminfo
|
||||
"LDREQ LR, =reboot \n"
|
||||
"BXEQ R6 \n" // jump to error_handler directly
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// Return from exception to thread mode
|
||||
// --------------------------------------------------------------
|
||||
|
||||
"MOV R0, SP \n" // Align stack pointer to 8 bytes
|
||||
"AND R0, R0, #~7 \n"
|
||||
"MOV SP, R0 \n"
|
||||
"SUB SP, SP, #32 \n" // Allocate space for the stack frame
|
||||
|
||||
"MOV R0, #0 \n"
|
||||
"STR R5, [SP, #0] \n" // future R0 = pminfo
|
||||
"STR R0, [SP, #4] \n" // future R1 = 0
|
||||
"STR R0, [SP, #8] \n" // future R2 = 0
|
||||
"STR R0, [SP, #12] \n" // future R3 = 0
|
||||
"STR R0, [SP, #16] \n" // future R12 = 0
|
||||
"LDR R1, =reboot \n"
|
||||
"STR R0, [SP, #20] \n" // future LR = reboot()
|
||||
"BIC R6, R6, #1 \n"
|
||||
"STR R6, [SP, #24] \n" // return address = error_handler()
|
||||
"LDR R1, = 0x01000000 \n" // THUMB bit set
|
||||
"STR R1, [SP, #28] \n" // future xPSR
|
||||
|
||||
"MOV R4, R0 \n" // Clear registers R4-R6
|
||||
"MOV R5, R0 \n" // (R7-R11 are already cleared)
|
||||
"MOV R6, R0 \n"
|
||||
|
||||
"MRS R0, CONTROL \n" // Clear SPSEL to use MSP for thread
|
||||
"BIC R0, R0, #3 \n" // Clear nPRIV to run in privileged mode
|
||||
"MSR CONTROL, R1 \n"
|
||||
|
||||
"LDR LR, = 0xFFFFFFF9 \n" // Return to Secure Thread mode, use MSP
|
||||
"BX LR \n"
|
||||
: // no output
|
||||
: [PMINFO_SIZE] "i"(sizeof(systask_postmortem_t)),
|
||||
[STK_GUARD] "i"(&__stack_chk_guard)
|
||||
: // no clobber
|
||||
);
|
||||
}
|
@ -0,0 +1 @@
|
||||
../stm32f4/applet.c
|
@ -1,76 +0,0 @@
|
||||
#include "common.h"
|
||||
#include "mpu.h"
|
||||
|
||||
#ifdef KERNEL_MODE
|
||||
|
||||
void fault_handlers_init(void) {
|
||||
// Enable BUS fault and USAGE fault handlers
|
||||
SCB->SHCSR |= (SCB_SHCSR_USGFAULTENA_Msk | SCB_SHCSR_BUSFAULTENA_Msk);
|
||||
}
|
||||
|
||||
void HardFault_Handler(void) {
|
||||
// A HardFault may also be caused by exception escalation.
|
||||
// To ensure we have enough space to handle the exception,
|
||||
// we set the stack pointer to the end of the stack.
|
||||
extern uint8_t _estack; // linker script symbol
|
||||
// Fix stack pointer
|
||||
__set_MSP((uint32_t)&_estack);
|
||||
|
||||
mpu_reconfig(MPU_MODE_DEFAULT);
|
||||
error_shutdown("(HF)");
|
||||
}
|
||||
|
||||
void MemManage_Handler(void) {
|
||||
mpu_reconfig(MPU_MODE_DEFAULT);
|
||||
error_shutdown("(MM)");
|
||||
}
|
||||
|
||||
void BusFault_Handler(void) {
|
||||
mpu_reconfig(MPU_MODE_DEFAULT);
|
||||
error_shutdown("(BF)");
|
||||
}
|
||||
|
||||
void UsageFault_Handler(void) {
|
||||
if (SCB->CFSR & SCB_CFSR_STKOF_Msk) {
|
||||
// Stack overflow
|
||||
extern uint8_t _estack; // linker script symbol
|
||||
// Fix stack pointer
|
||||
__set_MSP((uint32_t)&_estack);
|
||||
mpu_reconfig(MPU_MODE_DEFAULT);
|
||||
error_shutdown("(SO)");
|
||||
} else {
|
||||
// Other error
|
||||
mpu_reconfig(MPU_MODE_DEFAULT);
|
||||
error_shutdown("(UF)");
|
||||
}
|
||||
}
|
||||
|
||||
void SecureFault_Handler(void) {
|
||||
mpu_reconfig(MPU_MODE_DEFAULT);
|
||||
error_shutdown("(SF)");
|
||||
}
|
||||
|
||||
void GTZC_IRQHandler(void) {
|
||||
mpu_reconfig(MPU_MODE_DEFAULT);
|
||||
error_shutdown("(IA)");
|
||||
}
|
||||
|
||||
void NMI_Handler(void) {
|
||||
mpu_mode_t mpu_mode = mpu_reconfig(MPU_MODE_DEFAULT);
|
||||
// Clock Security System triggered NMI
|
||||
if ((RCC->CIFR & RCC_CIFR_CSSF) != 0) {
|
||||
error_shutdown("(CS)");
|
||||
}
|
||||
mpu_restore(mpu_mode);
|
||||
}
|
||||
|
||||
// from util.s
|
||||
extern void shutdown_privileged(void);
|
||||
|
||||
void PVD_PVM_IRQHandler(void) {
|
||||
mpu_reconfig(MPU_MODE_DEFAULT);
|
||||
TIM1->CCR1 = 0; // turn off display backlight
|
||||
shutdown_privileged();
|
||||
}
|
||||
|
||||
#endif // KERNEL_MODE
|
@ -0,0 +1 @@
|
||||
../stm32f4/systask.c
|
@ -0,0 +1 @@
|
||||
../stm32f4/system.c
|
@ -0,0 +1,181 @@
|
||||
/*
|
||||
* This file is part of the Trezor project, https://trezor.io/
|
||||
*
|
||||
* Copyright (c) SatoshiLabs
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TREZORHAL_SYSTASK_H
|
||||
#define TREZORHAL_SYSTASK_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "mpu.h"
|
||||
|
||||
// Termination reason for the task
|
||||
typedef enum {
|
||||
TASK_TERM_REASON_EXIT = 0,
|
||||
TASK_TERM_REASON_ERROR,
|
||||
TASK_TERM_REASON_FATAL,
|
||||
TASK_TERM_REASON_FAULT,
|
||||
|
||||
} systask_term_reason_t;
|
||||
|
||||
typedef struct {
|
||||
// Fault/exception number (-15..-1)
|
||||
int irqn;
|
||||
// Configurable Fault Status Register
|
||||
// (combined UFSR/BFSR/MMFSR)
|
||||
uint32_t cfsr;
|
||||
// Hard Fault Status Register
|
||||
uint32_t hfsr;
|
||||
// Address associated with MemManage fault
|
||||
uint32_t mmfar;
|
||||
// Address associated with the BusFault
|
||||
uint32_t bfar;
|
||||
// Stack pointer at the time of the fault
|
||||
// (MSP or PSP depending on the privilege level)
|
||||
uint32_t sp;
|
||||
#if !(defined(__ARM_ARCH_8M_MAIN__) || defined(__ARM_ARCH_8M_BASE__))
|
||||
// Stack pointer limit (for the stack overflow detection)
|
||||
uint32_t sp_lim;
|
||||
#endif
|
||||
|
||||
} system_fault_t;
|
||||
|
||||
// Task post-mortem information
|
||||
typedef struct {
|
||||
// Reason for the task termination
|
||||
systask_term_reason_t reason;
|
||||
// Whether the error occurred in privileged mode
|
||||
bool privileged;
|
||||
|
||||
union {
|
||||
// Argument passed to `systask_exit()`
|
||||
struct {
|
||||
int code;
|
||||
} exit;
|
||||
|
||||
// Fault information catched in `systask_exit_fault()`
|
||||
system_fault_t fault;
|
||||
|
||||
// Arguments passed to `systask_exit_fatal()`
|
||||
struct {
|
||||
uint32_t line;
|
||||
char file[64];
|
||||
char expr[64];
|
||||
} fatal;
|
||||
|
||||
// Arguments passed to `systask_exit_error()`
|
||||
struct {
|
||||
char title[64];
|
||||
char message[64];
|
||||
char footer[64];
|
||||
} error;
|
||||
};
|
||||
|
||||
} systask_postmortem_t;
|
||||
|
||||
// Error handler callback invoke when kernel task terminates.
|
||||
//
|
||||
// The purpose of this callbacks display RSOD (Red Screen of Death).
|
||||
//
|
||||
// The callback may be called from any context, including interrupt context.
|
||||
typedef void (*systask_error_handler_t)(const systask_postmortem_t* pminfo);
|
||||
|
||||
#ifdef KERNEL_MODE
|
||||
|
||||
// Task context used by the kernel to save the state of each task
|
||||
// when switching between them
|
||||
typedef struct {
|
||||
// `sp`, `sp_lim`, `exc_return` and `killed` should at the beginning
|
||||
// and in this order to be compatible with the PendSV_Handler
|
||||
// Stack pointer value
|
||||
uint32_t sp;
|
||||
// Stack pointer limit (ARMv8-M only)
|
||||
uint32_t sp_lim;
|
||||
// Exception return value
|
||||
uint32_t exc_return;
|
||||
// Set to nonzero, if the task is killed
|
||||
uint32_t killed;
|
||||
|
||||
// MPU mode the task is running in
|
||||
mpu_mode_t mpu_mode;
|
||||
// Task post-mortem information
|
||||
systask_postmortem_t pminfo;
|
||||
|
||||
} systask_t;
|
||||
|
||||
// Initializes the scheduler for tasks
|
||||
//
|
||||
// No other task functions should be called before this function
|
||||
void systask_scheduler_init(systask_error_handler_t error_handler);
|
||||
|
||||
// Returns the currently running task
|
||||
systask_t* systask_active(void);
|
||||
|
||||
// Makes the given task the currently running task
|
||||
void systask_yield_to(systask_t* task);
|
||||
|
||||
// Initializes a task with the given stack pointer, stack size
|
||||
//
|
||||
// The task must be not be running when the function is called
|
||||
void systask_init(systask_t* task, uint32_t stack_ptr, uint32_t stack_size);
|
||||
|
||||
// Pushes data onto the stack of the task
|
||||
//
|
||||
// The task must be not be running when the function is called
|
||||
uint32_t* systask_push_data(systask_t* task, const void* data, size_t size);
|
||||
|
||||
// Pops data from the stack of the task
|
||||
//
|
||||
// The task must be not be running when the function is called
|
||||
void systask_pop_data(systask_t* task, size_t size);
|
||||
|
||||
// Runs the task with the given entrypoint and arguments
|
||||
//
|
||||
// The task must be not be running when the function is called
|
||||
void systask_push_call(systask_t* task, void* fn, uint32_t arg1, uint32_t arg2,
|
||||
uint32_t arg3);
|
||||
|
||||
// Terminates the task with the given exit code
|
||||
//
|
||||
// If the task is not specified (NULL), it's automatically determined:
|
||||
// 1) If the function is called in thread mode, the active task will be
|
||||
// terminated.
|
||||
// 2) If the function is called in handler mode, the kernel task will be
|
||||
// terminated even if it is not the active task.
|
||||
//
|
||||
// If the terminated task is unprivileged, the kernel task will be scheduled
|
||||
// next.
|
||||
void systask_exit(systask_t* task, int exit_code);
|
||||
|
||||
// Terminates the task with an error message
|
||||
//
|
||||
// (see `systask_exit()` for more details)
|
||||
void systask_exit_error(systask_t* task, const char* title, const char* message,
|
||||
const char* footer);
|
||||
|
||||
// Terminates the task with a fatal error message
|
||||
//
|
||||
// (see `systask_exit()` for more details)
|
||||
void systask_exit_fatal(systask_t* task, const char* message, const char* file,
|
||||
int line);
|
||||
|
||||
#endif // KERNEL_MODE
|
||||
|
||||
#endif // TREZORHAL_SYSTASK_H
|
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* This file is part of the Trezor project, https://trezor.io/
|
||||
*
|
||||
* Copyright (c) SatoshiLabs
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TREZORHAL_SYSTEM_H
|
||||
#define TREZORHAL_SYSTEM_H
|
||||
|
||||
#include <systask.h>
|
||||
|
||||
// Initializes the fundamental system services
|
||||
// (MPU, SysTick, systimer and task scheduler).
|
||||
//
|
||||
// `error_handler` is a callback that is called when a kernel task terminates
|
||||
// with an error
|
||||
void system_init(systask_error_handler_t error_handler);
|
||||
|
||||
// Terminates the current task normally with the given exit code.
|
||||
//
|
||||
// If the current task is the kernel task, the error handler is called with the
|
||||
// postmortem information. If the task is not the kernel task, the task is
|
||||
// terminated immediately and the kernel task is scheduled.
|
||||
void system_exit(int exitcode);
|
||||
|
||||
// Terminates the current task with an error message.
|
||||
//
|
||||
// See the notes for `system_exit` regarding the behavior of the error handler
|
||||
void system_exit_error(const char* title, const char* message,
|
||||
const char* footer);
|
||||
|
||||
// Terminates the current task with a fatal error message.
|
||||
//
|
||||
// See the notes for `system_exit` regarding the behavior of the error handler
|
||||
void system_exit_fatal(const char* message, const char* file, int line);
|
||||
|
||||
// Returns string representation of the system fault.
|
||||
const char* system_fault_message(const system_fault_t* fault);
|
||||
|
||||
// Calls the error handler in the emergency mode.
|
||||
//
|
||||
// This function is called when the system encounters a critical error
|
||||
// and needs to perform a useful action (such as displaying an error message)
|
||||
// before it is reset or shut down.
|
||||
//
|
||||
// The function may be called from any context, including interrupt context.
|
||||
// It completely resets stack pointers, clears the .bss segment, reinitializes
|
||||
// the .data segment, and calls the `error_handler` callback.
|
||||
//
|
||||
// The system will be in a state similar to a reset when `main()` is called
|
||||
// (but with some hardware peripherals still initialized and running).
|
||||
__attribute__((noreturn)) void system_emergency_rescue(
|
||||
systask_error_handler_t error_handler, const systask_postmortem_t* pminfo);
|
||||
|
||||
#endif // TREZORHAL_SYSTEM_H
|
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* This file is part of the Trezor project, https://trezor.io/
|
||||
*
|
||||
* Copyright (c) SatoshiLabs
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "bootutils.h"
|
||||
#include "system.h"
|
||||
#include "systick.h"
|
||||
#include "systimer.h"
|
||||
|
||||
systask_error_handler_t g_error_handler = NULL;
|
||||
|
||||
void system_init(systask_error_handler_t error_handler) {
|
||||
g_error_handler = error_handler;
|
||||
systick_init();
|
||||
systimer_init();
|
||||
}
|
||||
|
||||
void system_exit(int exitcode) {
|
||||
if (g_error_handler != NULL) {
|
||||
systask_postmortem_t pminfo = {0};
|
||||
|
||||
pminfo.reason = TASK_TERM_REASON_EXIT;
|
||||
pminfo.exit.code = exitcode;
|
||||
|
||||
if (g_error_handler != NULL) {
|
||||
g_error_handler(&pminfo);
|
||||
}
|
||||
}
|
||||
|
||||
secure_shutdown();
|
||||
}
|
||||
|
||||
void system_exit_error(const char* title, const char* message,
|
||||
const char* footer) {
|
||||
fprintf(stderr, "ERROR: %s\n", message);
|
||||
fflush(stderr);
|
||||
|
||||
if (g_error_handler != NULL) {
|
||||
systask_postmortem_t pminfo = {0};
|
||||
|
||||
pminfo.reason = TASK_TERM_REASON_ERROR;
|
||||
strncpy(pminfo.error.title, title, sizeof(pminfo.error.title) - 1);
|
||||
strncpy(pminfo.error.message, message, sizeof(pminfo.error.message) - 1);
|
||||
strncpy(pminfo.error.footer, footer, sizeof(pminfo.error.footer) - 1);
|
||||
|
||||
if (g_error_handler != NULL) {
|
||||
g_error_handler(&pminfo);
|
||||
}
|
||||
}
|
||||
|
||||
secure_shutdown();
|
||||
}
|
||||
|
||||
void system_exit_fatal(const char* message, const char* file, int line) {
|
||||
fprintf(stderr, "ERROR: %s\n", message);
|
||||
if (file) {
|
||||
fprintf(stderr, "FILE: %s:%d\n", file, line);
|
||||
}
|
||||
fflush(stderr);
|
||||
|
||||
if (g_error_handler != NULL) {
|
||||
systask_postmortem_t pminfo = {0};
|
||||
|
||||
pminfo.reason = TASK_TERM_REASON_FATAL;
|
||||
strncpy(pminfo.fatal.file, file, sizeof(pminfo.fatal.file) - 1);
|
||||
strncpy(pminfo.fatal.expr, message, sizeof(pminfo.fatal.expr) - 1);
|
||||
pminfo.fatal.line = line;
|
||||
|
||||
if (g_error_handler != NULL) {
|
||||
g_error_handler(&pminfo);
|
||||
}
|
||||
}
|
||||
|
||||
secure_shutdown();
|
||||
}
|
||||
|
||||
const char* system_fault_message(const system_fault_t* fault) {
|
||||
// Not used in simulator
|
||||
return "(FAULT)";
|
||||
}
|
||||
|
||||
void system_emergency_rescue(systask_error_handler_t error_handler,
|
||||
const systask_postmortem_t* pminfo) {
|
||||
error_handler(pminfo);
|
||||
|
||||
// We should never reach this point
|
||||
exit(0);
|
||||
}
|
Loading…
Reference in new issue