refactor(core): refactor coreapp emu startup, run coreapp as a task
What changed, and why it matters
This commit is a code cleanup and architectural refactor for the Trezor firmware's Unix emulator. It moves the emulator startup logic into a new 'coreapp' task structure and splits hardware/driver initialization into a separate file. There is no indication in the commit that this fixes a security vulnerability or changes security-sensitive behavior; it appears to be internal restructuring only.
No security action required. Treat as normal engineering refactor. If reviewing for regressions, verify that SDL quit/escape handling still cleanly terminates the emulator and that Rust test setup remains functionally equivalent.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch refactors the Unix emulator entry point. main.c loses its direct main() and driver initialization, becoming coreapp_emu() called from a task. A new main_main.c hosts the real main(), sets up the kernel/applet environment, initializes drivers, runs the coreapp task, and handles SDL quit/escape events by throwing a SystemExit into the task context. A new rust_c_setup.c consolidates Rust test setup. The SConscript adds the ‘applet’ feature, KERNEL define, and the new rust_c_setup.c source. No security-relevant logic changes are visible.
Changed components
core/embed/projects/unix/main.ccore/embed/projects/unix/main_main.ccore/embed/projects/unix/rust_c_setup.ccore/SConscript.unixInspect captured patch +271 / −138
diff --git a/core/SConscript.unix b/core/SConscript.unix
index e17d8cad6..6bcb389f5 100644
--- a/core/SConscript.unix
+++ b/core/SConscript.unix
@@ -24,6 +24,7 @@ if BENCHMARK and PYOPT != '0':
exit(1)
FEATURES_WANTED = [
+ "applet",
"ble",
"display",
"dma2d",
@@ -263,6 +264,7 @@ if not PRODUCTION:
SOURCE_MOD += ['embed/rtl/printf.c']
CPPDEFINES_MOD += [
+ 'KERNEL',
'TRANSLATIONS',
'FANCY_FATAL_ERROR',
]
@@ -417,6 +419,7 @@ SOURCE_UNIX = [
'embed/projects/unix/main_main.c',
'embed/projects/unix/main.c',
'embed/projects/unix/profile.c',
+ 'embed/projects/unix/rust_c_setup.c',
'vendor/micropython/ports/unix/alloc.c',
'vendor/micropython/ports/unix/gccollect.c',
'vendor/micropython/ports/unix/input.c',
diff --git a/core/embed/projects/unix/main.c b/core/embed/projects/unix/main.c
index 9f33d13b3..1cc870225 100644
--- a/core/embed/projects/unix/main.c
+++ b/core/embed/projects/unix/main.c
@@ -26,50 +26,26 @@
#include <trezor_rtl.h>
-#include <SDL.h>
+#include <sys/system.h>
+
+#ifdef USE_DBG_CONSOLE
+#include <sys/dbg_console.h>
+#endif
#include <ctype.h>
#include <errno.h>
#include <signal.h>
#include <stdarg.h>
+#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
-#include <io/display.h>
-#include <io/rsod.h>
-#include <io/usb_config.h>
-#include <sec/secret.h>
-#include <sec/unit_properties.h>
-#include <sys/flash.h>
-#include <sys/flash_otp.h>
-#include <sys/system.h>
-#include <sys/systimer.h>
#include "extmod/misc.h"
#include "extmod/vfs_posix.h"
#include "genhdr/mpversion.h"
#include "input.h"
-#ifdef USE_BUTTON
-#include <io/button.h>
-#endif
-
-#ifdef USE_TOUCH
-#include <io/touch.h>
-#endif
-
-#ifdef USE_BLE
-#include <io/ble.h>
-#endif
-
-#ifdef USE_TROPIC
-#include <sec/tropic.h>
-#endif
-
-#ifdef USE_DBG_CONSOLE
-#include <sys/dbg_console.h>
-#endif
-
#include "py/builtin.h"
#include "py/compile.h"
#include "py/gc.h"
@@ -432,19 +408,6 @@ STATIC void set_sys_argv(char *argv[], int argc, int start_arg) {
}
}
-// Inject SystemExit exception. This is primarily needed by `prof/__main__.py`
-// to run the flush the coverage data.
-static void __attribute__((noreturn)) main_clean_exit() {
- const int status = 3;
- fflush(stdout);
- fflush(stderr);
- // sys.exit is disabled, so raise a SystemExit exception directly
- nlr_raise(mp_obj_new_exception_arg1(&mp_type_SystemExit,
- MP_OBJ_NEW_SMALL_INT(status)));
- // the above shouldn't return, but make sure we exit just in case
- exit(status);
-}
-
#ifdef _WIN32
#define PATHLIST_SEP_CHAR ';'
#else
@@ -491,61 +454,14 @@ reimport:
return 0;
}
-static int sdl_event_filter(void *userdata, SDL_Event *event) {
- switch (event->type) {
- case SDL_QUIT:
- main_clean_exit();
- return 0;
- case SDL_KEYUP:
- if (event->key.repeat) {
- return 0;
- }
- switch (event->key.keysym.sym) {
- case SDLK_ESCAPE:
- main_clean_exit();
- return 0;
- case SDLK_s:
- display_save("emu");
- return 0;
- }
- break;
- }
- return 1;
-}
-
-void drivers_init(uint16_t tropic_model_port) {
- flash_init();
- flash_otp_init();
-
- unit_properties_init();
-
- display_init(DISPLAY_RESET_CONTENT);
-
-#if USE_TOUCH
- touch_init();
-#endif
-
-#ifdef USE_BUTTON
- button_init();
-#endif
-
-#ifdef USE_TROPIC
- ensure(tropic_init(tropic_model_port) ? sectrue : secfalse,
- "tropic initialization failed");
-#endif
-
- usb_configure(NULL);
-
-#ifdef USE_BLE
- ble_init();
-#endif
-}
-
-// Initialize the system and drivers for running tests in the Rust code.
-// The function is called from the Rust before the test main function is run.
-void rust_tests_c_setup(void) {
- system_init(NULL);
- drivers_init(28992);
+// Inject SystemExit exception. This is primarily needed by `prof/__main__.py`
+// to run the flush the coverage data.
+void __attribute__((noreturn)) coreapp_throw_exit_exception(int code) {
+ // sys.exit is disabled, so raise a SystemExit exception directly
+ nlr_raise(mp_obj_new_exception_arg1(&mp_type_SystemExit,
+ MP_OBJ_NEW_SMALL_INT(code)));
+ // the above shouldn't return, but make sure we exit just in case
+ exit(code);
}
MP_NOINLINE int main_(int argc, char **argv) {
@@ -567,32 +483,6 @@ MP_NOINLINE int main_(int argc, char **argv) {
pre_process_options(argc, argv);
-#ifdef LOCKABLE_BOOTLOADER
- secret_lock_bootloader();
-#endif
-
- system_init(&rsod_panic_handler);
-
- char *tropic_model_port_str = getenv("TROPIC_MODEL_PORT");
- uint16_t tropic_model_port;
- if (tropic_model_port_str == NULL) {
- tropic_model_port = 28992;
- } else {
- char *endptr;
- long port_long = strtol(tropic_model_port_str, &endptr, 10);
-
- if (*endptr != '\0' || port_long < 0 || port_long > 65535) {
- printf("FATAL: invalid TROPIC_MODEL_PORT\n");
- exit(1);
- }
-
- tropic_model_port = (uint16_t)port_long;
- }
-
- drivers_init(tropic_model_port);
-
- SDL_SetEventFilter(sdl_event_filter, NULL);
-
#if MICROPY_ENABLE_GC
char *heap = malloc(heap_size);
gc_init(heap, heap + heap_size);
@@ -789,6 +679,19 @@ MP_NOINLINE int main_(int argc, char **argv) {
return ret & 0xff;
}
+int coreapp_emu(int argc, char **argv) {
+#if MICROPY_PY_THREAD
+ mp_thread_init();
+#endif
+ // We should capture stack top ASAP after start, and it should be
+ // captured guaranteedly before any other stack variables are allocated.
+ // For this, actual main (renamed main_) should not be inlined into
+ // this function. main_() itself may have other functions inlined (with
+ // their own stack variables), that's why we need this main/main_ split.
+ mp_stack_ctrl_init();
+ return main_(argc, argv);
+}
+
#if !MICROPY_VFS
#ifdef TREZOR_EMULATOR_FROZEN
diff --git a/core/embed/projects/unix/main_main.c b/core/embed/projects/unix/main_main.c
index 6a59764c1..85e7b9f98 100644
--- a/core/embed/projects/unix/main_main.c
+++ b/core/embed/projects/unix/main_main.c
@@ -1,27 +1,188 @@
+/*
+ * 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 "py/mpthread.h"
-#include "py/runtime.h"
-#include "py/stackctrl.h"
+#include <io/display.h>
+#include <io/rsod.h>
+#include <io/usb_config.h>
+#include <sec/secret.h>
+#include <sec/unit_properties.h>
+#include <sys/applet.h>
+#include <sys/bootutils.h>
+#include <sys/coreapp.h>
+#include <sys/flash.h>
+#include <sys/flash_otp.h>
+#include <sys/system.h>
+#include <sys/systick.h>
+#include <sys/systimer.h>
+
+#ifdef USE_BUTTON
+#include <io/button.h>
+#endif
+
+#ifdef USE_BLE
+#include <io/ble.h>
+#endif
+
+#ifdef USE_TOUCH
+#include <io/touch.h>
+#endif
+
+#ifdef USE_TROPIC
+#include <sec/tropic.h>
+#endif
+
#ifdef USE_SECP256K1_ZKP
#include "zkp_context.h"
#endif
-MP_NOINLINE int main_(int argc, char **argv);
+#include <SDL.h>
+
+#ifdef USE_TROPIC
+static uint16_t get_tropic_model_port(void) {
+ char *port_str = getenv("TROPIC_MODEL_PORT");
+ if (port_str != NULL) {
+ char *endptr;
+ long port_long = strtol(port_str, &endptr, 10);
+ if (*endptr != '\0' || port_long < 0 || port_long > 65535) {
+ printf("FATAL: invalid TROPIC_MODEL_PORT\n");
+ exit(1);
+ }
+ return (uint16_t)port_long;
+ }
+ return 28992;
+}
+#endif
+
+static void drivers_deinit(void) { flash_deinit(); }
+
+static void drivers_init(void) {
+ flash_init();
+ flash_otp_init();
+
+ unit_properties_init();
+
+ display_init(DISPLAY_RESET_CONTENT);
+
+#if USE_TOUCH
+ touch_init();
+#endif
+
+#ifdef USE_BUTTON
+ button_init();
+#endif
+
+#ifdef USE_TROPIC
+ tropic_init(get_tropic_model_port());
+#endif
+
+ usb_configure(NULL);
+
+#ifdef USE_BLE
+ ble_init();
+#endif
+}
+
+// Throws MicroPython SystemExit exception in the context of the given task
+static void throw_exit_exception(systask_t *task, int code) {
+ extern void coreapp_throw_exit_exception(int code);
+ // Push call to the task
+ systask_push_call(task, (void *)coreapp_throw_exit_exception, code, 0, 0);
+ // Yield to the task and throw the exception
+ systask_yield_to(task);
+ // We are back and the task should be terminated by now
+}
+
+static int sdl_event_filter(void *userdata, SDL_Event *event) {
+ applet_t *coreapp = (applet_t *)userdata;
+
+ switch (event->type) {
+ case SDL_QUIT:
+ throw_exit_exception(&coreapp->task, 0);
+ return 0;
+ case SDL_KEYUP:
+ if (event->key.repeat) {
+ return 0;
+ }
+ switch (event->key.keysym.sym) {
+ case SDLK_ESCAPE:
+ throw_exit_exception(&coreapp->task, 0);
+ return 0;
+ case SDLK_s:
+ display_save("emu");
+ return 0;
+ }
+ break;
+ }
+ return 1;
+}
+
+// Kernel task main loop
+//
+// Returns when the coreapp task is terminated
+static void kernel_loop(applet_t *coreapp) {
+ do {
+ sysevents_t awaited = {0};
+ sysevents_t signalled = {0};
+
+ sysevents_poll(&awaited, &signalled, ticks_timeout(100));
+
+ } while (applet_is_alive(coreapp));
+}
int main(int argc, char **argv) {
+ system_init(&rsod_panic_handler);
+
+#ifdef LOCKABLE_BOOTLOADER
+ secret_lock_bootloader();
+#endif
+
#ifdef USE_SECP256K1_ZKP
ensure(sectrue * (zkp_context_init() == 0), NULL);
#endif
-#if MICROPY_PY_THREAD
- mp_thread_init();
-#endif
- // We should capture stack top ASAP after start, and it should be
- // captured guaranteedly before any other stack variables are allocated.
- // For this, actual main (renamed main_) should not be inlined into
- // this function. main_() itself may have other functions inlined (with
- // their own stack variables), that's why we need this main/main_ split.
- mp_stack_ctrl_init();
- return main_(argc, argv);
+ drivers_init();
+
+ applet_t coreapp;
+
+ // Initialize coreapp task
+ if (!coreapp_init(&coreapp, argc, argv)) {
+ error_shutdown("Cannot start coreapp");
+ }
+
+ // Set SDL event filter to catch quit events
+ SDL_SetEventFilter(sdl_event_filter, &coreapp);
+
+ // Run the coreapp task
+ applet_run(&coreapp);
+
+ // Loop until the coreapp task is terminated
+ kernel_loop(&coreapp);
+
+ // Show RSOD if the coreapp task did not exit cleanly
+ if (coreapp.task.pminfo.reason != TASK_TERM_REASON_EXIT) {
+ rsod_gui(&coreapp.task.pminfo);
+ reboot_or_halt_after_rsod();
+ }
+
+ drivers_deinit();
+
+ return coreapp.task.pminfo.exit.code;
}
diff --git a/core/embed/projects/unix/rust_c_setup.c b/core/embed/projects/unix/rust_c_setup.c
new file mode 100644
index 000000000..92a180e58
--- /dev/null
+++ b/core/embed/projects/unix/rust_c_setup.c
@@ -0,0 +1,66 @@
+/*
+ * 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 <io/display.h>
+#include <io/usb_config.h>
+#include <sec/unit_properties.h>
+#include <sys/flash.h>
+#include <sys/flash_otp.h>
+#include <sys/system.h>
+
+#ifdef USE_BUTTON
+#include <io/button.h>
+#endif
+
+#ifdef USE_TOUCH
+#include <io/touch.h>
+#endif
+
+#ifdef USE_TROPIC
+#include <sec/tropic.h>
+#endif
+
+// Initialize the system and drivers for running tests in the Rust code.
+// The function is called from the Rust before the test main function is run.
+void rust_tests_c_setup(void) {
+ system_init(NULL);
+
+ flash_init();
+ flash_otp_init();
+
+ unit_properties_init();
+
+ display_init(DISPLAY_RESET_CONTENT);
+
+#if USE_TOUCH
+ touch_init();
+#endif
+
+#ifdef USE_BUTTON
+ button_init();
+#endif
+
+#ifdef USE_TROPIC
+ tropic_init(28992);
+#endif
+
+ usb_configure(NULL);
+}
Why this scored 12/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.