diff --git a/core/SConscript.bootloader_emu b/core/SConscript.bootloader_emu index dc385ff0b2..b65cdbac23 100644 --- a/core/SConscript.bootloader_emu +++ b/core/SConscript.bootloader_emu @@ -127,7 +127,10 @@ SOURCE_TREZORHAL = [ 'embed/sec/secret/unix/secret.c', 'embed/sys/mpu/unix/mpu.c', 'embed/sys/startup/unix/bootutils.c', + 'embed/sys/task/sysevent.c', + 'embed/sys/task/unix/sdl_event.c', 'embed/sys/task/unix/system.c', + 'embed/sys/task/unix/systask.c', 'embed/sys/time/unix/systick.c', 'embed/sys/time/unix/systimer.c', 'embed/util/flash/unix/flash.c', diff --git a/core/SConscript.unix b/core/SConscript.unix index 5cceeb51b3..5425b7237b 100644 --- a/core/SConscript.unix +++ b/core/SConscript.unix @@ -392,7 +392,10 @@ SOURCE_UNIX = [ 'embed/sec/time_estimate/unix/time_estimate.c', 'embed/sys/mpu/unix/mpu.c', 'embed/sys/startup/unix/bootutils.c', + 'embed/sys/task/sysevent.c', + 'embed/sys/task/unix/sdl_event.c', 'embed/sys/task/unix/system.c', + 'embed/sys/task/unix/systask.c', 'embed/sys/time/unix/systick.c', 'embed/sys/time/unix/systimer.c', 'embed/util/flash/unix/flash.c', diff --git a/core/embed/rtl/inc/trezor_rtl.h b/core/embed/rtl/inc/trezor_rtl.h index 75a5844a2e..60b6fc2024 100644 --- a/core/embed/rtl/inc/trezor_rtl.h +++ b/core/embed/rtl/inc/trezor_rtl.h @@ -62,4 +62,8 @@ #define ARRAY_LENGTH(x) (sizeof(x) / sizeof((x)[0])) +#ifndef UNUSED +#define UNUSED(x) (void)(x) +#endif + #endif // TREZOR_RTL_H diff --git a/core/embed/sys/bsp/stm32f4/stm32f4xx_hal_conf.h b/core/embed/sys/bsp/stm32f4/stm32f4xx_hal_conf.h index ebd2e3ea69..5ebea5e1e6 100644 --- a/core/embed/sys/bsp/stm32f4/stm32f4xx_hal_conf.h +++ b/core/embed/sys/bsp/stm32f4/stm32f4xx_hal_conf.h @@ -45,6 +45,8 @@ extern "C" { #endif +#undef UNUSED + /* Exported types ------------------------------------------------------------*/ /* Exported constants --------------------------------------------------------*/ diff --git a/core/embed/sys/syscall/stm32/syscall_dispatch.c b/core/embed/sys/syscall/stm32/syscall_dispatch.c index 61e9fb583b..42062c36e3 100644 --- a/core/embed/sys/syscall/stm32/syscall_dispatch.c +++ b/core/embed/sys/syscall/stm32/syscall_dispatch.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -146,6 +147,13 @@ __attribute((no_stack_protector)) void syscall_handler(uint32_t *args, args[1] = cycles >> 32; } break; + case SYSCALL_SYSEVENTS_POLL: { + const sysevents_t *awaited = (sysevents_t *)args[0]; + sysevents_t *signalled = (sysevents_t *)args[1]; + uint32_t timeout = args[2]; + sysevents_poll__verified(awaited, signalled, timeout); + } break; + case SYSCALL_REBOOT_DEVICE: { reboot_device(); } break; diff --git a/core/embed/sys/syscall/stm32/syscall_numbers.h b/core/embed/sys/syscall/stm32/syscall_numbers.h index fe1f8886f9..c7d736ea9b 100644 --- a/core/embed/sys/syscall/stm32/syscall_numbers.h +++ b/core/embed/sys/syscall/stm32/syscall_numbers.h @@ -31,6 +31,8 @@ typedef enum { SYSCALL_SYSTICK_US, SYSCALL_SYSTICK_US_TO_CYCLES, + SYSCALL_SYSEVENTS_POLL, + SYSCALL_REBOOT_DEVICE, SYSCALL_REBOOT_TO_BOOTLOADER, SYSCALL_REBOOT_AND_UPGRADE, diff --git a/core/embed/sys/syscall/stm32/syscall_stubs.c b/core/embed/sys/syscall/stm32/syscall_stubs.c index 228e55c19c..68984fab06 100644 --- a/core/embed/sys/syscall/stm32/syscall_stubs.c +++ b/core/embed/sys/syscall/stm32/syscall_stubs.c @@ -70,6 +70,18 @@ uint64_t systick_us_to_cycles(uint64_t us) { return syscall_invoke2_ret64(arg0, arg1, SYSCALL_SYSTICK_US_TO_CYCLES); } +// ============================================================================= +// sysevent.h +// ============================================================================= + +#include + +void sysevents_poll(const sysevents_t *awaited, sysevents_t *signalled, + uint32_t timeout) { + syscall_invoke3((uint32_t)awaited, (uint32_t)signalled, timeout, + SYSCALL_SYSEVENTS_POLL); +} + // ============================================================================= // bootutils.h // ============================================================================= diff --git a/core/embed/sys/syscall/stm32/syscall_verifiers.c b/core/embed/sys/syscall/stm32/syscall_verifiers.c index 84e9b60bbf..a688718074 100644 --- a/core/embed/sys/syscall/stm32/syscall_verifiers.c +++ b/core/embed/sys/syscall/stm32/syscall_verifiers.c @@ -45,6 +45,25 @@ // --------------------------------------------------------------------- +void sysevents_poll__verified(const sysevents_t *awaited, + sysevents_t *signalled, uint32_t timeout) { + if (!probe_read_access(awaited, sizeof(*awaited))) { + goto access_violation; + } + + if (!probe_write_access(signalled, sizeof(*signalled))) { + goto access_violation; + } + + sysevents_poll(awaited, signalled, timeout); + return; + +access_violation: + apptask_access_violation(); +} + +// --------------------------------------------------------------------- + void system_exit__verified(int exit_code) { systask_t *task = systask_active(); diff --git a/core/embed/sys/syscall/stm32/syscall_verifiers.h b/core/embed/sys/syscall/stm32/syscall_verifiers.h index 1b27116b33..840ca9db7d 100644 --- a/core/embed/sys/syscall/stm32/syscall_verifiers.h +++ b/core/embed/sys/syscall/stm32/syscall_verifiers.h @@ -21,6 +21,12 @@ #ifdef SYSCALL_DISPATCH +// --------------------------------------------------------------------- +#include + +void sysevents_poll__verified(const sysevents_t *awaited, + sysevents_t *signalled, uint32_t timeout); + // --------------------------------------------------------------------- #include diff --git a/core/embed/sys/task/inc/sys/sysevent.h b/core/embed/sys/task/inc/sys/sysevent.h new file mode 100644 index 0000000000..3632d4db6e --- /dev/null +++ b/core/embed/sys/task/inc/sys/sysevent.h @@ -0,0 +1,56 @@ +/* + * 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 . + */ + +#pragma once + +#include + +// Event sources that can be signaled by the system or device drivers +typedef enum { + SYSHANDLE_USB_IFACE_0, + SYSHANDLE_USB_IFACE_7 = SYSHANDLE_USB_IFACE_0 + 7, + SYSHANDLE_BLE_IFACE_0, + // SYSHANDLE_BLE_IFACE_N = SYSHANDLE_BLE_IFACE_0 + N - 1, + SYSHANDLE_POWERCTL, + SYSHANDLE_BUTTON, + SYSHANDLE_TOUCH, + SYSHANDLE_USB, + SYSHANDLE_BLE, + SYSHANDLE_COUNT, +} syshandle_t; + +// Bitmask of event sources +typedef uint32_t syshandle_mask_t; + +typedef struct { + // Bitmask of handles ready for reading + syshandle_mask_t read_ready; + // Bitmask of handles ready for writing + syshandle_mask_t write_ready; +} sysevents_t; // sys_events_t + +// Polls for the specified events. The function blocks until at least +// one event is signaled or the timeout (in milliseconds) expires. +// +// Multiple events may be signaled simultaneously. +// +// Returns the events that were signaled. If the timeout expires, both +// fields in the result are set to 0. +void sysevents_poll(const sysevents_t* awaited, sysevents_t* signalled, + uint32_t timeout); diff --git a/core/embed/sys/task/inc/sys/sysevent_source.h b/core/embed/sys/task/inc/sys/sysevent_source.h new file mode 100644 index 0000000000..9dbb9012cd --- /dev/null +++ b/core/embed/sys/task/inc/sys/sysevent_source.h @@ -0,0 +1,113 @@ +/* + * 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 . + */ + +#pragma once + +#ifdef KERNEL_MODE + +#include +#include + +// Callback invoked when a new task is created. +// Driver may use this callback to initialize the its own task local storage. +typedef void (*syshandle_task_created_cb_t)(void *context, + systask_id_t task_id); + +// Callback invoked when a task is killed +// Driver may use this callback to deinitialize the its own task local storage. +// +// The callback may be called from the fault handler. But in this case +// it's guaranteed that the task is not running anymore. +typedef void (*syshandle_task_killed_cb_t)(void *context, systask_id_t task_id); + +// Callback invoked when the system is polling for events. +// +// 'read_awaited' is set if there's at least one task waiting for read events. +// 'write_awaited' is set if there's at least one task waiting for write events. +typedef void (*syshandle_poll_cb_t)(void *context, bool read_awaited, + bool write_awaited); + +// Callback invoked when the driver's polling callback calls +// `syshandle_signal_read_ready()` or `syshandle_signal_write_ready()`. +// +// The callback is executed for each task waiting for the event. +// The `param` parameter is passed unchanged from the +// `syshandle_signal_read_ready()` or `syshandle_signal_write_ready()` function. +// +// The callback returns `true` if the event should be signaled to the task. +typedef bool (*syshandle_check_cb_t)(void *context, systask_id_t task_id, + void *param); + +// System handle virtual method table +typedef struct { + syshandle_task_created_cb_t task_created; + syshandle_task_killed_cb_t task_killed; + syshandle_poll_cb_t poll; + syshandle_check_cb_t check_read_ready; + syshandle_check_cb_t check_write_ready; +} syshandle_vmt_t; + +// ---------------------------------------------------------------------- + +// Registers a new event source +// +// This function is called by the device driver's +// initialization code. Sources that are not registered will never be signaled. +// +// Returns `true` if the source was registered successfully. +bool syshandle_register(syshandle_t handle, const syshandle_vmt_t *vmt, + void *context); + +// Unregisters an event source +// +// This function is called by the device driver's deinitialization code. +void syshandle_unregister(syshandle_t handle); + +// Distributes read ready event to waiting tasks +// +// This function is called by the device driver to distribute events +// to waiting tasks. +// +// The function may only be called from a poll callback. +void syshandle_signal_read_ready(syshandle_t handle, void *param); + +// Distributes write ready event to waiting tasks +// +// This function is called by the device driver to distribute events +// to waiting tasks. +// +// The function may only be called from a poll callback. +void syshandle_signal_write_ready(syshandle_t handle, void *param); + +// ---------------------------------------------------------------------- +// Internal functions called by the system + +// Notifies all registered event sources / drivers about a new task creation +// +// The function invoke the `task_created` callback of each registered +// event source. +void sysevents_notify_task_created(systask_t *task); + +// Notifies all registered event sources / drivers about a task termination +// +// The function invoke the `task_killed` callback of each registered +// event source. +void sysevents_notify_task_killed(systask_t *task); + +#endif // KERNEL_MODE diff --git a/core/embed/sys/task/inc/sys/systask.h b/core/embed/sys/task/inc/sys/systask.h index 72f72e6501..9b918a1214 100644 --- a/core/embed/sys/task/inc/sys/systask.h +++ b/core/embed/sys/task/inc/sys/systask.h @@ -135,7 +135,10 @@ 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 +// Returns the kernel task +systask_t* systask_kernel(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 @@ -161,8 +164,8 @@ void systask_pop_data(systask_t* task, size_t size); bool systask_push_call(systask_t* task, void* fn, uint32_t arg1, uint32_t arg2, uint32_t arg3); -// Gets the Id (zero-based index up SYSTASK_MAX_TASKS - 1) of the given task -systask_id_t systask_id(systask_t* task); +// Gets the ID (zero-based index up SYSTASK_MAX_TASKS - 1) of the given task. +systask_id_t systask_id(const systask_t* task); // Terminates the task with the given exit code // diff --git a/core/embed/sys/task/inc/sys/unix/sdl_event.h b/core/embed/sys/task/inc/sys/unix/sdl_event.h new file mode 100644 index 0000000000..17efa4395b --- /dev/null +++ b/core/embed/sys/task/inc/sys/unix/sdl_event.h @@ -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 . + */ + +#pragma once + +#include + +#include "SDL.h" + +// This module provides a modular approach to processing SDL events. +// +// SDL events are collected from a single source using the `sdl_event_poll()` +// function, which is called from the main event loop. `sdl_event_poll()` then +// dispatches these events to all registered event filters. + +// SDL event filter callback +// +// The callback is invoked for each SDL event. +typedef void (*sdl_event_filter_cb_t)(void* context, SDL_Event* sdl_event); + +// Register an SDL event filter +// +// Returns `true` if the filter was successfully registered +bool sdl_events_register(sdl_event_filter_cb_t filter, void* context); + +// Unregister an SDL event filter +void sdl_events_unregister(sdl_event_filter_cb_t filter, void* context); + +// Process all pending SDL events +// +// Invokes all registered event filters for each event +void sdl_events_poll(void); diff --git a/core/embed/sys/task/stm32/systask.c b/core/embed/sys/task/stm32/systask.c index d6ddddae45..7a2562e703 100644 --- a/core/embed/sys/task/stm32/systask.c +++ b/core/embed/sys/task/stm32/systask.c @@ -17,6 +17,8 @@ * along with this program. If not, see . */ +#ifdef KERNEL_MODE + #include #include @@ -25,11 +27,10 @@ #include #include #include +#include #include #include -#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") @@ -96,6 +97,12 @@ systask_t* systask_active(void) { return scheduler->active_task; } +systask_t* systask_kernel(void) { + systask_scheduler_t* scheduler = &g_systask_scheduler; + + return &scheduler->kernel_task; +} + static void systask_yield(void) { bool handler_mode = (__get_IPSR() & IPSR_ISR_Msk) != 0; @@ -147,6 +154,9 @@ bool systask_init(systask_t* task, uint32_t stack_ptr, uint32_t stack_size, task->mpu_mode = MPU_MODE_APP; task->applet = applet; + // Notify all event sources about the task creation + sysevents_notify_task_created(task); + return true; } @@ -238,7 +248,8 @@ static void systask_kill(systask_t* task) { } else if (task == scheduler->active_task) { // 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); } diff --git a/core/embed/sys/task/sysevent.c b/core/embed/sys/task/sysevent.c new file mode 100644 index 0000000000..49e2501169 --- /dev/null +++ b/core/embed/sys/task/sysevent.c @@ -0,0 +1,279 @@ +/* + * 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 . + */ + +#ifdef KERNEL_MODE + +#include +#include + +#include +#include +#include + +#ifdef TREZOR_EMULATOR +#include +#endif + +typedef struct { + // Waiting task + systask_t *task; + // Deadline for the task to be woken up + uint32_t deadline; + // Bitmask of events the task is waiting for + const sysevents_t *awaited; + // Bitmask of events that were signaled + sysevents_t *signalled; +} sysevent_poller_t; + +typedef struct { + const syshandle_vmt_t *vmt; + void *context; +} sysevent_source_t; + +typedef struct { + // Registered event sources + sysevent_source_t sources[SYSHANDLE_COUNT]; + // Priority queue of tasks waiting for events + // (zero index is reserved for the kernel task) + sysevent_poller_t pollers[SYSTASK_MAX_TASKS]; + // Number of pollers in the list + size_t pollers_count; + +} sysevent_dispatcher_t; + +sysevent_dispatcher_t g_sysevent_dispatcher = {0}; + +bool syshandle_register(syshandle_t handle, const syshandle_vmt_t *vmt, + void *context) { + sysevent_dispatcher_t *dispatcher = &g_sysevent_dispatcher; + + if (handle >= SYSHANDLE_COUNT || dispatcher->sources[handle].vmt != NULL) { + return false; + } + + dispatcher->sources[handle].vmt = vmt; + dispatcher->sources[handle].context = context; + return true; +} + +void syshandle_unregister(syshandle_t handle) { + sysevent_dispatcher_t *dispatcher = &g_sysevent_dispatcher; + + if (handle < SYSHANDLE_COUNT) { + dispatcher->sources[handle].vmt = NULL; + dispatcher->sources[handle].context = NULL; + } +} + +void syshandle_signal_read_ready(syshandle_t handle, void *param) { + if (handle >= SYSHANDLE_COUNT) { + return; + } + + sysevent_dispatcher_t *dispatcher = &g_sysevent_dispatcher; + const sysevent_source_t *source = &dispatcher->sources[handle]; + + // For each polling task, call `check_read_ready` callback + for (size_t i = 0; i < dispatcher->pollers_count; i++) { + sysevent_poller_t *poller = &dispatcher->pollers[i]; + syshandle_mask_t handle_mask = 1 << handle; + if ((poller->awaited->read_ready & handle_mask) != 0) { + if (source->vmt->check_read_ready != NULL) { + if (source->vmt->check_read_ready(source->context, + systask_id(poller->task), param)) { + poller->signalled->read_ready |= handle_mask; + } else { + poller->signalled->read_ready &= ~handle_mask; + } + } + } + } +} + +void syshandle_signal_write_ready(syshandle_t handle, void *param) { + if (handle >= SYSHANDLE_COUNT) { + return; + } + + sysevent_dispatcher_t *dispatcher = &g_sysevent_dispatcher; + const sysevent_source_t *source = &dispatcher->sources[handle]; + + // For each polling task, call `check_write_ready` callback + for (size_t i = 0; i < dispatcher->pollers_count; i++) { + sysevent_poller_t *poller = &dispatcher->pollers[i]; + syshandle_mask_t handle_mask = 1 << handle; + if ((poller->awaited->write_ready & handle_mask) != 0) { + if (source->vmt->check_write_ready != NULL) { + if (source->vmt->check_write_ready(source->context, + systask_id(poller->task), param)) { + poller->signalled->write_ready |= handle_mask; + } else { + poller->signalled->write_ready &= ~handle_mask; + } + } + } + } +} + +static inline void remove_poller(sysevent_dispatcher_t *dispatcher, + size_t idx) { + for (size_t j = idx; j < dispatcher->pollers_count - 1; j++) { + dispatcher->pollers[j] = dispatcher->pollers[j + 1]; + } + --dispatcher->pollers_count; +} + +static inline void insert_poller(sysevent_dispatcher_t *dispatcher, + size_t idx) { + if (dispatcher->pollers_count >= SYSTASK_MAX_TASKS) { + // This should never happen since the number of pollers + // is limited by the number of tasks. But just in case... + error_shutdown("Too many pollers"); + } + + ++dispatcher->pollers_count; + if (idx < dispatcher->pollers_count - 1) { + // Move all pollers with lower priority to the right + for (size_t j = dispatcher->pollers_count - 1; j > idx; j--) { + dispatcher->pollers[j] = dispatcher->pollers[j - 1]; + } + } +} + +void sysevents_poll(const sysevents_t *awaited, sysevents_t *signalled, + uint32_t timeout) { + sysevent_dispatcher_t *dispatcher = &g_sysevent_dispatcher; + + memset(signalled, 0, sizeof(*signalled)); + + systask_t *kernel_task = systask_kernel(); + systask_t *active_task = systask_active(); + + // Determine task priority + // - Kernel task has the highest priority so it is always first in the list + // - Unprivileged task use round-robin scheduling + uint32_t prio = (active_task == kernel_task) ? 0 : dispatcher->pollers_count; + + insert_poller(dispatcher, prio); + + // Add task to the polling list + // Kernel task has the highest priority so it is always first in the list + dispatcher->pollers[prio].task = systask_active(); + dispatcher->pollers[prio].awaited = awaited; + dispatcher->pollers[prio].signalled = signalled; + dispatcher->pollers[prio].deadline = ticks_timeout(timeout); + + if (active_task != kernel_task) { + systask_yield_to(kernel_task); + return; + } + + for (;;) { +#ifdef TREZOR_EMULATOR + // Poll SDL events and dispatch them + sdl_events_poll(); +#endif + + syshandle_mask_t handles_to_read = 0; + syshandle_mask_t handles_to_write = 0; + + // Gather sources to poll + for (size_t i = 0; i < dispatcher->pollers_count; i++) { + sysevent_poller_t *poller = &dispatcher->pollers[i]; + handles_to_read |= poller->awaited->read_ready; + handles_to_write |= poller->awaited->write_ready; + } + + // Poll sources we are waiting for + for (size_t handle = 0; handle < SYSHANDLE_COUNT; handle++) { + const sysevent_source_t *source = &dispatcher->sources[handle]; + if (source->vmt != NULL) { + bool read_awaited = (handles_to_read & (1 << handle)) != 0; + bool write_awaited = (handles_to_write & (1 << handle)) != 0; + if (read_awaited || write_awaited) { + source->vmt->poll(source->context, read_awaited, write_awaited); + } + } + } + + uint32_t now = systick_ms(); + + // Choose the next task to run + for (size_t prio = 0; prio < dispatcher->pollers_count; prio++) { + sysevent_poller_t *poller = &dispatcher->pollers[prio]; + bool timed_out = ((int32_t)(poller->deadline - now)) <= 0; + bool ready = (poller->signalled->read_ready != 0) || + (poller->signalled->write_ready != 0); + if (ready || timed_out) { + systask_t *task = poller->task; + remove_poller(dispatcher, prio); + if (task == kernel_task) { + return; + } else { + systask_yield_to(task); + break; + } + } + } + +#ifdef TREZOR_EMULATOR + // Wait a bit to not consume 100% CPU + systick_delay_ms(1); +#else + // Wait for the next event + __WFI(); +#endif + } +} + +void sysevents_notify_task_created(systask_t *task) { + sysevent_dispatcher_t *dispatcher = &g_sysevent_dispatcher; + // Notify sources about the task being created + systask_id_t task_id = systask_id(task); + for (size_t i = 0; i < SYSHANDLE_COUNT; i++) { + const sysevent_source_t *source = &dispatcher->sources[i]; + if (source->vmt != NULL && source->vmt->task_created != NULL) { + source->vmt->task_created(source->context, task_id); + } + } +} + +// This routine may be called from the fault handler!!! +void sysevents_notify_task_killed(systask_t *task) { + sysevent_dispatcher_t *dispatcher = &g_sysevent_dispatcher; + // Remove task from poller list + // (kernel task is not included) + for (size_t i = 0; i < dispatcher->pollers_count; i++) { + if (dispatcher->pollers[i].task == task) { + remove_poller(dispatcher, i); + break; + } + } + + // Notify sources about the task being killed + systask_id_t task_id = systask_id(task); + for (size_t i = 0; i < SYSHANDLE_COUNT; i++) { + const sysevent_source_t *source = &dispatcher->sources[i]; + if (source->vmt != NULL && source->vmt->task_killed != NULL) { + source->vmt->task_killed(source->context, task_id); + } + } +} + +#endif // KERNEL_MODE diff --git a/core/embed/sys/task/unix/sdl_event.c b/core/embed/sys/task/unix/sdl_event.c new file mode 100644 index 0000000000..e373b957a5 --- /dev/null +++ b/core/embed/sys/task/unix/sdl_event.c @@ -0,0 +1,76 @@ +/* + * 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 . + */ + +#include + +#include + +typedef struct { + sdl_event_filter_cb_t callback; + void* context; +} sdl_event_filter_t; + +typedef struct { + // Registered event filters + sdl_event_filter_t filter[4]; +} sdl_event_dispatcher_t; + +static sdl_event_dispatcher_t g_sdl_event_dispatcher = {0}; + +bool sdl_events_register(sdl_event_filter_cb_t callback, void* context) { + sdl_event_dispatcher_t* dispatcher = &g_sdl_event_dispatcher; + + for (int index = 0; index < ARRAY_LENGTH(dispatcher->filter); index++) { + sdl_event_filter_t* filter = &dispatcher->filter[index]; + if (filter->callback == NULL) { + filter->callback = callback; + filter->context = context; + return true; + } + } + + return false; +} + +void sdl_events_unregister(sdl_event_filter_cb_t callback, void* context) { + sdl_event_dispatcher_t* dispatcher = &g_sdl_event_dispatcher; + + for (int index = 0; index < ARRAY_LENGTH(dispatcher->filter); index++) { + sdl_event_filter_t* filter = &dispatcher->filter[index]; + if (filter->callback == callback && filter->context == context) { + filter->callback = NULL; + filter->context = NULL; + } + } +} + +void sdl_events_poll(void) { + 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); + } + } + } +} diff --git a/core/embed/sys/task/unix/systask.c b/core/embed/sys/task/unix/systask.c new file mode 100644 index 0000000000..6c122ef24d --- /dev/null +++ b/core/embed/sys/task/unix/systask.c @@ -0,0 +1,32 @@ +/* + * 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 . + */ + +#include + +// 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. + +systask_t* systask_active(void) { return NULL; } + +systask_t* systask_kernel(void) { return NULL; } + +systask_id_t systask_id(const systask_t* task) { return 0; } + +void systask_yield_to(systask_t* task) {} diff --git a/core/site_scons/models/stm32f4_common.py b/core/site_scons/models/stm32f4_common.py index 28d1469956..483b5c283c 100644 --- a/core/site_scons/models/stm32f4_common.py +++ b/core/site_scons/models/stm32f4_common.py @@ -89,6 +89,7 @@ def stm32f4_common_files(env, defines, sources, paths): "embed/sys/task/stm32/system.c", "embed/sys/time/stm32/systick.c", "embed/sys/time/stm32/systimer.c", + "embed/sys/task/sysevent.c", "embed/util/board_capabilities/stm32/board_capabilities.c", "embed/util/flash/stm32f4/flash.c", "embed/util/flash/stm32f4/flash_layout.c", diff --git a/core/site_scons/models/stm32u5_common.py b/core/site_scons/models/stm32u5_common.py index a12b293201..020576c3f4 100644 --- a/core/site_scons/models/stm32u5_common.py +++ b/core/site_scons/models/stm32u5_common.py @@ -108,6 +108,7 @@ def stm32u5_common_files(env, defines, sources, paths): "embed/sys/task/stm32/system.c", "embed/sys/time/stm32/systick.c", "embed/sys/time/stm32/systimer.c", + "embed/sys/task/sysevent.c", "embed/sys/trustzone/stm32u5/trustzone.c", "embed/util/board_capabilities/stm32/board_capabilities.c", "embed/util/flash/stm32u5/flash.c",