What changed, and why it matters
This commit adds a software-based emulation of Trezor's multitasking 'systask' system for the desktop emulator. Previously the emulator faked task functions as empty stubs; now it uses real POSIX threads and condition variables to mimic how tasks switch on the real hardware. It also moves some system shutdown helpers into a shared file used by both real devices and the emulator. There is no direct evidence this fixes a security bug; it is primarily an engineering change to make the emulator behave more like the real device.
Treat as a normal engineering commit. Reviewers should verify that the new pthread-based scheduler correctly handles all wait/signal edge cases, that the shared system_exit paths preserve previous device behavior, and that the SDL event-loop kernel-task guard does not introduce deadlocks or event starvation in the emulator. No security patch or incident response is indicated by the supplied materials.
Security signals we found
New threading/concurrency code using pthreads and condition variables in the emulator path
Shared system shutdown path introduced across stm32f4, stm32u5, and unix builds
SDL event polling restricted to kernel task context to avoid thread-safety issues
No changelog entry and no security framing in commit message
Evidence from the diff
The patch reimplements the unix/systask.c emulator layer using pthreads, pthread_cond_t, and a scheduler lock, replacing no-op stubs. It adds TREZOR_EMULATOR conditional fields to systask_t (pthread, cv, pushed_fn_call) and wraps hardware-specific fields (sp, sp_lim, exc_return, mpu_mode, stack_base/stack_end, tls_area) with #ifndef TREZOR_EMULATOR. A shared core/embed/sys/task/system.c is introduced and linked for stm32f4, stm32u5, and unix builds, consolidating system_exit, system_exit_error, and system_exit_fatal* implementations. The SDL event loop is restricted to the kernel task context because SDL is not thread-safe. The unix applet.c is added as a minimal wrapper. No vulnerability, CVE, or security disclosure is mentioned in the commit or supplied references.
Changed components
core/embed/sys/task/unix/systask.ccore/embed/sys/task/unix/system.ccore/embed/sys/task/unix/applet.ccore/embed/sys/task/unix/sdl_event.ccore/embed/sys/task/system.ccore/embed/sys/task/stm32/system.ccore/embed/sys/task/inc/sys/systask.hcore/site_scons/models/stm32f4_common.pycore/site_scons/models/stm32u5_common.pycore/site_scons/models/unix_common.pyInspect captured patch +527 / −154
diff --git a/core/embed/sys/task/inc/sys/systask.h b/core/embed/sys/task/inc/sys/systask.h
index a3925a3c6..dc0a356fb 100644
--- a/core/embed/sys/task/inc/sys/systask.h
+++ b/core/embed/sys/task/inc/sys/systask.h
@@ -23,6 +23,10 @@
#include <sys/mpu.h>
+#ifdef TREZOR_EMULATOR
+#include <pthread.h>
+#endif
+
// Termination reason for the task
typedef enum {
TASK_TERM_REASON_EXIT = 0,
@@ -112,9 +116,18 @@ typedef uint8_t systask_id_t;
#ifdef KERNEL_MODE
+// Function call pushed onto the stack of the task
+typedef struct {
+ uintptr_t (*fn)(uintptr_t, uintptr_t, uintptr_t);
+ uintptr_t arg1;
+ uintptr_t arg2;
+ uintptr_t arg3;
+} systask_fn_call_t;
+
// Task context used by the kernel to save the state of each task
// when switching between them
typedef struct {
+#ifndef TREZOR_EMULATOR
// `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
@@ -123,18 +136,20 @@ typedef struct {
uint32_t sp_lim;
// Exception return value
uint32_t exc_return;
+#endif
// Set to nonzero, if the task is killed
volatile uint32_t killed;
// Task id
systask_id_t id;
- // MPU mode the task is running in
- mpu_mode_t mpu_mode;
// Task post-mortem information
systask_postmortem_t pminfo;
// Applet bound to the task
void* applet;
+#ifndef TREZOR_EMULATOR
+ // MPU mode the task is running in
+ mpu_mode_t mpu_mode;
// Original stack base
uint32_t stack_base;
// Original stack end
@@ -153,6 +168,16 @@ typedef struct {
// Set if the task is processing the kernel callback
bool in_callback;
+#else
+ // System thread handle
+ pthread_t pthread;
+ // Condition variable used to signal the task
+ // is ready to run
+ pthread_cond_t cv;
+
+ // Emulation of the call pushed onto the stack
+ systask_fn_call_t pushed_fn_call;
+#endif
} systask_t;
@@ -167,11 +192,13 @@ systask_t* systask_active(void);
// Returns the kernel task
systask_t* systask_kernel(void);
+#ifndef TREZOR_EMULATOR
// Enables automatics restoring of TLS area
//
// When task is deactivated, the tls area is automatically stored in the
// `task->tls_copy` array and restored when the task is activated again.
void systask_enable_tls(systask_t* task, mpu_area_t tls);
+#endif
// Makes the given task the currently running task.
void systask_yield_to(systask_t* task);
@@ -199,19 +226,22 @@ void systask_pop_data(systask_t* task, size_t size);
//
// The task must be not be running when the function is called
// Return `true` in case of success, `false` otherwise
-bool systask_push_call(systask_t* task, void* fn, uint32_t arg1, uint32_t arg2,
- uint32_t arg3);
+bool systask_push_call(systask_t* task, void* fn, uintptr_t arg1,
+ uintptr_t arg2, uintptr_t arg3);
// Invokes the callback function in the context of the given task
// uint32_t callback(uint32_t arg1, uint32_t arg2, uint32_t arg3);
-uint32_t systask_invoke_callback(systask_t* task, uint32_t arg1, uint32_t arg2,
- uint32_t arg3, void* callback);
+uint32_t systask_invoke_callback(systask_t* task, uintptr_t arg1,
+ uintptr_t arg2, uintptr_t arg3,
+ void* callback);
+#ifndef TREZOR_EMULATOR
// Sets R0 and R1 registers of the suspended task
void systask_set_r0r1(systask_t* task, uint32_t r0, uint32_t r1);
// Gets R0 register value of the suspended task
uint32_t systask_get_r0(systask_t* task);
+#endif
// Gets the ID (zero-based index up SYSTASK_MAX_TASKS - 1) of the given task.
systask_id_t systask_id(const systask_t* task);
@@ -242,4 +272,7 @@ void systask_exit_fatal(systask_t* task, const char* message,
size_t message_len, const char* file, size_t file_len,
int line);
+// Prints the post-mortem information about the task to the debug output
+void systask_print_pminfo(systask_t* task);
+
#endif // KERNEL_MODE
diff --git a/core/embed/sys/task/stm32/systask.c b/core/embed/sys/task/stm32/systask.c
index d57b3dddc..4da4b2831 100644
--- a/core/embed/sys/task/stm32/systask.c
+++ b/core/embed/sys/task/stm32/systask.c
@@ -46,7 +46,7 @@
#define STK_FRAME_RET_ADDR 6
#define STK_FRAME_XPSR 7
-// Task manager state
+// Task scheduler state
typedef struct {
// Error handler called when a kernel task terminates
systask_error_handler_t error_handler;
@@ -61,7 +61,6 @@ typedef struct {
} systask_scheduler_t;
-// Global task manager state
static systask_scheduler_t g_systask_scheduler = {
// This static initialization is required for exception handling
// to function correctly before the scheduler is initialized.
@@ -215,8 +214,8 @@ uint32_t* systask_push_data(systask_t* task, const void* data, size_t size) {
void systask_pop_data(systask_t* task, size_t size) { task->sp += size; }
-bool systask_push_call(systask_t* task, void* entrypoint, uint32_t arg1,
- uint32_t arg2, uint32_t arg3) {
+bool systask_push_call(systask_t* task, void* entrypoint, uintptr_t arg1,
+ uintptr_t arg2, uintptr_t arg3) {
#ifdef KERNEL
if (task->applet != NULL) {
applet_t* applet = (applet_t*)task->applet;
@@ -272,8 +271,9 @@ cleanup:
return false;
}
-uint32_t systask_invoke_callback(systask_t* task, uint32_t arg1, uint32_t arg2,
- uint32_t arg3, void* callback) {
+uint32_t systask_invoke_callback(systask_t* task, uintptr_t arg1,
+ uintptr_t arg2, uintptr_t arg3,
+ void* callback) {
uint32_t original_sp = task->sp;
if (!systask_push_call(task, callback, arg1, arg2, arg3)) {
// There is not enough space on the unprivileged stack
diff --git a/core/embed/sys/task/stm32/system.c b/core/embed/sys/task/stm32/system.c
index 2772c740a..081952db4 100644
--- a/core/embed/sys/task/stm32/system.c
+++ b/core/embed/sys/task/stm32/system.c
@@ -90,20 +90,6 @@ void system_deinit(void) {
mpu_reconfig(MPU_MODE_DISABLED);
}
-void system_exit(int exitcode) { systask_exit(NULL, exitcode); }
-
-void system_exit_error_ex(const char* title, size_t title_len,
- const char* message, size_t message_len,
- const char* footer, size_t footer_len) {
- systask_exit_error(NULL, title, title_len, message, message_len, footer,
- footer_len);
-}
-
-void system_exit_fatal_ex(const char* message, size_t message_len,
- const char* file, size_t file_len, int line) {
- systask_exit_fatal(NULL, message, message_len, file, file_len, line);
-}
-
__attribute((noreturn, no_stack_protector)) static void
system_emergency_rescue_phase_2(uint32_t arg1, uint32_t arg2) {
systask_error_handler_t error_handler = (systask_error_handler_t)arg1;
@@ -245,18 +231,3 @@ const char* system_fault_message(const system_fault_t* fault) {
return message;
}
#endif // STM32U5
-
-void system_exit_error(const char* title, const char* message,
- const char* footer) {
- size_t title_len = title != NULL ? strlen(title) : 0;
- size_t message_len = message != NULL ? strlen(message) : 0;
- size_t footer_len = footer != NULL ? strlen(footer) : 0;
- system_exit_error_ex(title, title_len, message, message_len, footer,
- footer_len);
-}
-
-void system_exit_fatal(const char* message, const char* file, int line) {
- size_t message_len = message != NULL ? strlen(message) : 0;
- size_t file_len = file != NULL ? strlen(file) : 0;
- system_exit_fatal_ex(message, message_len, file, file_len, line);
-}
diff --git a/core/embed/sys/task/system.c b/core/embed/sys/task/system.c
new file mode 100644
index 000000000..bd846f617
--- /dev/null
+++ b/core/embed/sys/task/system.c
@@ -0,0 +1,57 @@
+/*
+ * 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 <trezor_rtl.h>
+
+#include <sys/systask.h>
+#include <sys/system.h>
+
+#ifdef KERNEL_MODE
+
+void system_exit(int exitcode) { systask_exit(NULL, exitcode); }
+
+void system_exit_error_ex(const char* title, size_t title_len,
+ const char* message, size_t message_len,
+ const char* footer, size_t footer_len) {
+ systask_exit_error(NULL, title, title_len, message, message_len, footer,
+ footer_len);
+}
+
+void system_exit_fatal_ex(const char* message, size_t message_len,
+ const char* file, size_t file_len, int line) {
+ systask_exit_fatal(NULL, message, message_len, file, file_len, line);
+}
+
+#endif // KERNEL_MODE
+
+void system_exit_error(const char* title, const char* message,
+ const char* footer) {
+ size_t title_len = title != NULL ? strlen(title) : 0;
+ size_t message_len = message != NULL ? strlen(message) : 0;
+ size_t footer_len = footer != NULL ? strlen(footer) : 0;
+
+ system_exit_error_ex(title, title_len, message, message_len, footer,
+ footer_len);
+}
+
+void system_exit_fatal(const char* message, const char* file, int line) {
+ size_t message_len = message != NULL ? strlen(message) : 0;
+ size_t file_len = file != NULL ? strlen(file) : 0;
+ system_exit_fatal_ex(message, message_len, file, file_len, line);
+}
diff --git a/core/embed/sys/task/unix/applet.c b/core/embed/sys/task/unix/applet.c
new file mode 100644
index 000000000..8d1877603
--- /dev/null
+++ b/core/embed/sys/task/unix/applet.c
@@ -0,0 +1,48 @@
+/*
+ * 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 <trezor_rtl.h>
+
+#include <sys/applet.h>
+
+void applet_init(applet_t* applet, const applet_layout_t* layout,
+ const applet_privileges_t* privileges) {
+ memset(applet, 0, sizeof(applet_t));
+
+ applet->layout = *layout;
+ applet->privileges = *privileges;
+}
+
+void applet_run(applet_t* applet) { systask_yield_to(&applet->task); }
+
+void applet_stop(applet_t* applet) {}
+
+bool applet_is_alive(applet_t* applet) {
+ return systask_is_alive(&applet->task);
+}
+
+applet_t* applet_active(void) {
+ systask_t* task = systask_active();
+
+ if (task == NULL) {
+ return NULL;
+ }
+
+ return (applet_t*)task->applet;
+}
diff --git a/core/embed/sys/task/unix/sdl_event.c b/core/embed/sys/task/unix/sdl_event.c
index e373b957a..b60683496 100644
--- a/core/embed/sys/task/unix/sdl_event.c
+++ b/core/embed/sys/task/unix/sdl_event.c
@@ -19,6 +19,10 @@
#include <trezor_rtl.h>
+#include <sys/sysevent.h>
+#include <sys/systask.h>
+#include <sys/systick.h>
+
#include <sys/unix/sdl_event.h>
typedef struct {
@@ -61,16 +65,25 @@ void sdl_events_unregister(sdl_event_filter_cb_t callback, void* context) {
}
void sdl_events_poll(void) {
- sdl_event_dispatcher_t* dispatcher = &g_sdl_event_dispatcher;
- SDL_Event sdl_event;
+ // SDL functions are not thread-safe, so we process events
+ // only in the kernel task context. In other tasks, we just yield
+ // use the sysevents_poll() that will poll events in the kernel task.
+ if (systask_active() == systask_kernel()) {
+ sdl_event_dispatcher_t* dispatcher = &g_sdl_event_dispatcher;
+ SDL_Event sdl_event;
- // Process all pending events
- while (SDL_PollEvent(&sdl_event) > 0) {
- for (int index = 0; index < ARRAY_LENGTH(dispatcher->filter); index++) {
- sdl_event_filter_t* filter = &dispatcher->filter[index];
- if (filter->callback != NULL) {
- filter->callback(filter->context, &sdl_event);
+ // Process all pending events
+ while (SDL_PollEvent(&sdl_event) > 0) {
+ for (int index = 0; index < ARRAY_LENGTH(dispatcher->filter); index++) {
+ sdl_event_filter_t* filter = &dispatcher->filter[index];
+ if (filter->callback != NULL) {
+ filter->callback(filter->context, &sdl_event);
+ }
}
}
+ } else {
+ sysevents_t awaited = {0};
+ sysevents_t signalled = {0};
+ sysevents_poll(&awaited, &signalled, ticks_timeout(0));
}
}
diff --git a/core/embed/sys/task/unix/systask.c b/core/embed/sys/task/unix/systask.c
index 6c122ef24..903db9c73 100644
--- a/core/embed/sys/task/unix/systask.c
+++ b/core/embed/sys/task/unix/systask.c
@@ -17,16 +17,355 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <trezor_rtl.h>
+
+#include <sys/bootutils.h>
+#include <sys/sysevent_source.h>
#include <sys/systask.h>
-// Currently, the emulator runs in single-task mode, so all
-// task-related functions are stubs that allow compiling and
-// running the emulator without multitasking support.
+#include <pthread.h>
+
+#ifdef USE_DBG_CONSOLE
+#include <sys/dbg_console.h>
+#endif
+
+// Task scheduler 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;
+ // Bitmap of used task IDs
+ uint32_t task_id_map;
+ // Mutex used for synchronizing access to the scheduler state
+ pthread_mutex_t lock;
+
+} systask_scheduler_t;
+
+static systask_scheduler_t g_systask_scheduler = {
+ // This static initialization is required for exception handling
+ // to function correctly before the scheduler is initialized.
+ .active_task = &g_systask_scheduler.kernel_task,
+ .waiting_task = &g_systask_scheduler.kernel_task,
+ .task_id_map = 0x00000001, // Kernel task is always present
+ .kernel_task = {
+ .id = 0, // Kernel task ID == 0
+ .cv = PTHREAD_COND_INITIALIZER,
+ }};
+
+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->task_id_map = 0x00000001; // Kernel task is always present
+ scheduler->lock = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
+ scheduler->kernel_task.cv = (pthread_cond_t)PTHREAD_COND_INITIALIZER;
+}
+
+systask_t* systask_active(void) {
+ systask_scheduler_t* scheduler = &g_systask_scheduler;
+
+ return scheduler->active_task;
+}
+
+systask_t* systask_kernel(void) {
+ systask_scheduler_t* scheduler = &g_systask_scheduler;
+
+ return &scheduler->kernel_task;
+}
+
+systask_id_t systask_id(const systask_t* task) { return task->id; }
+
+static uint32_t invoke_pushed_fn_call(systask_t* task) {
+ systask_fn_call_t call = task->pushed_fn_call;
+
+ // Clear the pushed call before invoking it to allow re-entrancy
+ // (before the call returns, the kernel may push another call)
+ task->pushed_fn_call = (systask_fn_call_t){0};
+
+ return call.fn(call.arg1, call.arg2, call.arg3);
+}
+
+static void systask_yield(void) {
+ systask_scheduler_t* scheduler = &g_systask_scheduler;
+
+ pthread_mutex_lock(&scheduler->lock);
+
+ systask_t* current_task = scheduler->active_task;
+
+ if (scheduler->waiting_task->killed) {
+ pthread_mutex_unlock(&scheduler->lock);
+ return;
+ }
+
+ // Set the predicate *before* signaling to avoid lost wakeups
+ scheduler->active_task = scheduler->waiting_task;
+ pthread_cond_signal(&scheduler->waiting_task->cv);
+
+ // Park until someone makes us active again (or we’re exiting)
+ while (scheduler->active_task != current_task && !current_task->killed) {
+ pthread_cond_wait(¤t_task->cv, &scheduler->lock);
+ }
+
+ pthread_mutex_unlock(&scheduler->lock);
+
+ // Now the task called systask_yield() is active again
+
+ // Do not return to a killed task
+ if (current_task->killed) {
+ pthread_exit(0);
+ }
+
+ // Process the pushed call first, if any
+ // (used to throw exceptions into the task)
+ if (current_task->pushed_fn_call.fn != NULL) {
+ invoke_pushed_fn_call(current_task);
+ }
+}
+
+void systask_yield_to(systask_t* task) {
+ systask_scheduler_t* scheduler = &g_systask_scheduler;
+
+ pthread_mutex_lock(&scheduler->lock);
+ scheduler->waiting_task = task;
+ pthread_mutex_unlock(&scheduler->lock);
+
+ systask_yield();
+}
+
+static systask_id_t systask_get_unused_id(void) {
+ systask_scheduler_t* scheduler = &g_systask_scheduler;
+
+ pthread_mutex_lock(&scheduler->lock);
+
+ systask_id_t id = 0;
+ while (++id < SYSTASK_MAX_TASKS) {
+ if ((scheduler->task_id_map & (1 << id)) == 0) {
+ scheduler->task_id_map |= (1 << id);
+ break;
+ }
+ }
+
+ pthread_mutex_unlock(&scheduler->lock);
+
+ return id;
+}
+
+static void* thread_trampoline(void* arg) {
+ systask_t* task = (systask_t*)arg;
+ systask_scheduler_t* scheduler = &g_systask_scheduler;
+
+ pthread_mutex_lock(&scheduler->lock);
+ while (scheduler->active_task != task) {
+ pthread_cond_wait(&task->cv, &scheduler->lock);
+ }
+ pthread_mutex_unlock(&scheduler->lock);
+
+ invoke_pushed_fn_call(task);
+
+ // Cooperative exit: pick someone else if possible
+ pthread_mutex_lock(&scheduler->lock);
+ task->killed = true;
+
+ // If we're still active, hand off to the kernel thread
+ if (scheduler->active_task == task) {
+ scheduler->active_task = &scheduler->kernel_task;
+ pthread_cond_signal(&scheduler->kernel_task.cv);
+ }
+
+ pthread_mutex_unlock(&scheduler->lock);
+ return 0;
+}
+
+bool systask_init(systask_t* task, uint32_t stack_base, uint32_t stack_size,
+ uint32_t sb_addr, void* applet) {
+ UNUSED(stack_base);
+ UNUSED(stack_size);
+ UNUSED(sb_addr);
+
+ systask_id_t id = systask_get_unused_id();
+ if (id >= SYSTASK_MAX_TASKS) {
+ return false;
+ }
+
+ memset(task, 0, sizeof(systask_t));
+ task->id = id;
+ task->applet = applet;
+ task->cv = (pthread_cond_t)PTHREAD_COND_INITIALIZER;
+
+ if (pthread_create(&task->pthread, NULL, thread_trampoline, task) != 0) {
+ return false;
+ }
+
+ // Notify all event sources about the task creation
+ sysevents_notify_task_created(task);
+
+ return true;
+}
+
+bool systask_push_call(systask_t* task, void* fn, uintptr_t arg1,
+ uintptr_t arg2, uintptr_t arg3) {
+ if (task->pushed_fn_call.fn != NULL) {
+ return false;
+ }
+
+ task->pushed_fn_call.fn = fn;
+ task->pushed_fn_call.arg1 = arg1;
+ task->pushed_fn_call.arg2 = arg2;
+ task->pushed_fn_call.arg3 = arg3;
+
+ return true;
+}
+
+static void systask_kill(systask_t* task) {
+ systask_scheduler_t* scheduler = &g_systask_scheduler;
+
+ systask_print_pminfo(task);
+
+ task->killed = 1;
+
+ if (task == &scheduler->kernel_task) {
+ // Call panic handler
+ if (scheduler->error_handler != NULL) {
+ scheduler->error_handler(&task->pminfo);
+ }
+
+ // We reach this point only if error_handler is NULL or
+ // if it returns. Neither is expected to happen.
+ reboot_device();
+ } else {
+ // Free task ID
+ scheduler->task_id_map &= ~(1 << task->id);
+ // Notify all event sources about the task termination
+ sysevents_notify_task_killed(task);
+ // Switch to the kernel task
+ systask_yield_to(&scheduler->kernel_task);
+ }
+}
+
+bool systask_is_alive(const systask_t* task) { return !task->killed; }
+
+void systask_exit(systask_t* task, int exit_code) {
+ systask_scheduler_t* scheduler = &g_systask_scheduler;
+
+ if (task == NULL) {
+ task = systask_active();
+ }
+
+ 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, size_t title_len,
+ const char* message, size_t message_len,
+ const char* footer, size_t footer_len) {
+ systask_scheduler_t* scheduler = &g_systask_scheduler;
+
+ if (task == NULL) {
+ task = systask_active();
+ }
+
+ 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) {
+ size_t len = MIN(title_len, sizeof(pminfo->error.title) - 1);
+ strncpy(pminfo->error.title, title, len);
+ }
+
+ if (message != NULL) {
+ size_t len = MIN(message_len, sizeof(pminfo->error.message) - 1);
+ strncpy(pminfo->error.message, message, len);
+ }
+
+ if (footer != NULL) {
+ size_t len = MIN(footer_len, sizeof(pminfo->error.footer) - 1);
+ strncpy(pminfo->error.footer, footer, len);
+ }
+
+ systask_kill(task);
+}
+
+void systask_exit_fatal(systask_t* task, const char* message,
+ size_t message_len, const char* file, size_t file_len,
+ int line) {
+ systask_scheduler_t* scheduler = &g_systask_scheduler;
+
+ if (task == NULL) {
+ task = systask_active();
+ }
+
+ 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) {
+ size_t len = MIN(message_len, sizeof(pminfo->fatal.expr) - 1);
+ strncpy(pminfo->fatal.expr, message, len);
+ }
+
+ if (file != NULL) {
+ size_t len = MIN(file_len, sizeof(pminfo->fatal.file) - 1);
+ strncpy(pminfo->fatal.file, file, len);
+ }
+
+ pminfo->fatal.line = line;
+
+ systask_kill(task);
+}
+
+void systask_print_pminfo(systask_t* task) {
+#ifdef USE_DBG_CONSOLE
+ dbg_printf("Task #%u terminated.\n", task->id);
+ dbg_printf(" Post-mortem info:\n");
+
+ const systask_postmortem_t* pminfo = &task->pminfo;
-systask_t* systask_active(void) { return NULL; }
+ switch (pminfo->reason) {
+ case TASK_TERM_REASON_EXIT:
+ dbg_printf(" EXIT: %d\n", pminfo->exit.code);
+ break;
-systask_t* systask_kernel(void) { return NULL; }
+ case TASK_TERM_REASON_ERROR:
+ dbg_printf(" ERROR: %s\n", pminfo->error.message);
+ if (pminfo->error.title[0] != '\0') {
+ dbg_printf(" Title: %s\n", pminfo->error.title);
+ }
+ if (pminfo->error.footer[0] != '\0') {
+ dbg_printf(" Footer: %s\n", pminfo->error.footer);
+ }
+ break;
-systask_id_t systask_id(const systask_t* task) { return 0; }
+ case TASK_TERM_REASON_FATAL:
+ dbg_printf(" FATAL: %s\n", pminfo->fatal.expr);
+ if (pminfo->fatal.file[0] != '\0') {
+ dbg_printf(" at %s:%u\n", pminfo->fatal.file,
+ (unsigned int)pminfo->fatal.line);
+ }
+ break;
-void systask_yield_to(systask_t* task) {}
+ case TASK_TERM_REASON_FAULT:
+ dbg_printf(" FAULT\n");
+ break;
+ }
+#endif // USE_DBG_CONSOLE
+}
diff --git a/core/embed/sys/task/unix/system.c b/core/embed/sys/task/unix/system.c
index 271f66cc0..8fcb91d50 100644
--- a/core/embed/sys/task/unix/system.c
+++ b/core/embed/sys/task/unix/system.c
@@ -30,12 +30,17 @@
#include <sys/dbg_console.h>
#endif
-systask_error_handler_t g_error_handler = NULL;
+#ifdef USE_IPC
+#include <sys/ipc.h>
+#endif
void system_init(systask_error_handler_t error_handler) {
- g_error_handler = error_handler;
systick_init();
systimer_init();
+ systask_scheduler_init(error_handler);
+#ifdef USE_IPC
+ ipc_init();
+#endif
#ifdef USE_DBG_CONSOLE
dbg_console_init();
#endif
@@ -43,102 +48,6 @@ void system_init(systask_error_handler_t error_handler) {
void system_deinit(void) { systick_deinit(); }
-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);
- }
- }
-
- // We reach this point only if g_error_handler is NULL or
- // if it returns. Neither is expected to happen.
- reboot_device();
-}
-
-void system_exit_error_ex(const char* title, size_t title_len,
- const char* message, size_t message_len,
- const char* footer, size_t footer_len) {
- fprintf(stderr, "ERROR: %.*s\n", (int)message_len, message);
- fflush(stderr);
-
- if (g_error_handler != NULL) {
- systask_postmortem_t pminfo = {0};
- size_t len;
-
- pminfo.reason = TASK_TERM_REASON_ERROR;
-
- len = MIN(title_len, sizeof(pminfo.error.title) - 1);
- strncpy(pminfo.error.title, title, len);
-
- len = MIN(message_len, sizeof(pminfo.error.message) - 1);
- strncpy(pminfo.error.message, message, len);
-
- len = MIN(footer_len, sizeof(pminfo.error.footer) - 1);
- strncpy(pminfo.error.footer, footer, len);
-
- if (g_error_handler != NULL) {
- g_error_handler(&pminfo);
- }
- }
-
- // We reach this point only if g_error_handler is NULL or
- // if it returns. Neither is expected to happen.
- reboot_device();
-}
-
-void system_exit_error(const char* title, const char* message,
- const char* footer) {
- size_t title_len = title != NULL ? strlen(title) : 0;
- size_t message_len = message != NULL ? strlen(message) : 0;
- size_t footer_len = footer != NULL ? strlen(footer) : 0;
-
- system_exit_error_ex(title, title_len, message, message_len, footer,
- footer_len);
-}
-
-void system_exit_fatal_ex(const char* message, size_t message_len,
- const char* file, size_t file_len, 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};
- size_t len;
-
- pminfo.reason = TASK_TERM_REASON_FATAL;
-
- len = MIN(file_len, sizeof(pminfo.fatal.file) - 1);
- strncpy(pminfo.fatal.file, file, len);
-
- len = MIN(message_len, sizeof(pminfo.fatal.expr) - 1);
- strncpy(pminfo.fatal.expr, message, len);
-
- pminfo.fatal.line = line;
-
- if (g_error_handler != NULL) {
- g_error_handler(&pminfo);
- }
- }
-
- // We reach this point only if g_error_handler is NULL or
- // if it returns. Neither is expected to happen.
- reboot_device();
-}
-
-void system_exit_fatal(const char* message, const char* file, int line) {
- size_t message_len = message != NULL ? strlen(message) : 0;
- size_t file_len = file != NULL ? strlen(file) : 0;
- system_exit_fatal_ex(message, message_len, file, file_len, line);
-}
-
const char* system_fault_message(const system_fault_t* fault) {
// Not used in simulator
return "(FAULT)";
diff --git a/core/site_scons/models/stm32f4_common.py b/core/site_scons/models/stm32f4_common.py
index 46af4e9d3..0afaebba0 100644
--- a/core/site_scons/models/stm32f4_common.py
+++ b/core/site_scons/models/stm32f4_common.py
@@ -108,6 +108,7 @@ def stm32f4_common_files(env, features_wanted, defines, sources, paths):
"embed/sys/time/stm32/systick.c",
"embed/sys/time/stm32/systimer.c",
"embed/sys/task/sysevent.c",
+ "embed/sys/task/system.c",
]
if "dbg_console" in features_wanted:
diff --git a/core/site_scons/models/stm32u5_common.py b/core/site_scons/models/stm32u5_common.py
index 4bee6b096..62c021015 100644
--- a/core/site_scons/models/stm32u5_common.py
+++ b/core/site_scons/models/stm32u5_common.py
@@ -136,6 +136,7 @@ def stm32u5_common_files(env, features_wanted, defines, sources, paths):
"embed/sys/time/stm32/systick.c",
"embed/sys/time/stm32/systimer.c",
"embed/sys/task/sysevent.c",
+ "embed/sys/task/system.c",
"embed/sys/trustzone/stm32u5/trustzone.c",
]
diff --git a/core/site_scons/models/unix_common.py b/core/site_scons/models/unix_common.py
index a161f466a..b02be0198 100644
--- a/core/site_scons/models/unix_common.py
+++ b/core/site_scons/models/unix_common.py
@@ -54,6 +54,7 @@ def unix_common_files(env, features_wanted, defines, sources, paths):
"embed/sys/rng/unix/rng.c",
"embed/sys/startup/unix/bootutils.c",
"embed/sys/task/sysevent.c",
+ "embed/sys/task/system.c",
"embed/sys/task/unix/sdl_event.c",
"embed/sys/task/unix/system.c",
"embed/sys/task/unix/systask.c",
Why this scored 22/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.