mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-04-28 04:59:01 +00:00
feat(core): introduce system-level event polling
[no changelog]
This commit is contained in:
parent
6b045dd43d
commit
b9d15cb343
@ -127,7 +127,10 @@ SOURCE_TREZORHAL = [
|
|||||||
'embed/sec/secret/unix/secret.c',
|
'embed/sec/secret/unix/secret.c',
|
||||||
'embed/sys/mpu/unix/mpu.c',
|
'embed/sys/mpu/unix/mpu.c',
|
||||||
'embed/sys/startup/unix/bootutils.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/system.c',
|
||||||
|
'embed/sys/task/unix/systask.c',
|
||||||
'embed/sys/time/unix/systick.c',
|
'embed/sys/time/unix/systick.c',
|
||||||
'embed/sys/time/unix/systimer.c',
|
'embed/sys/time/unix/systimer.c',
|
||||||
'embed/util/flash/unix/flash.c',
|
'embed/util/flash/unix/flash.c',
|
||||||
|
@ -392,7 +392,10 @@ SOURCE_UNIX = [
|
|||||||
'embed/sec/time_estimate/unix/time_estimate.c',
|
'embed/sec/time_estimate/unix/time_estimate.c',
|
||||||
'embed/sys/mpu/unix/mpu.c',
|
'embed/sys/mpu/unix/mpu.c',
|
||||||
'embed/sys/startup/unix/bootutils.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/system.c',
|
||||||
|
'embed/sys/task/unix/systask.c',
|
||||||
'embed/sys/time/unix/systick.c',
|
'embed/sys/time/unix/systick.c',
|
||||||
'embed/sys/time/unix/systimer.c',
|
'embed/sys/time/unix/systimer.c',
|
||||||
'embed/util/flash/unix/flash.c',
|
'embed/util/flash/unix/flash.c',
|
||||||
|
@ -62,4 +62,8 @@
|
|||||||
|
|
||||||
#define ARRAY_LENGTH(x) (sizeof(x) / sizeof((x)[0]))
|
#define ARRAY_LENGTH(x) (sizeof(x) / sizeof((x)[0]))
|
||||||
|
|
||||||
|
#ifndef UNUSED
|
||||||
|
#define UNUSED(x) (void)(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif // TREZOR_RTL_H
|
#endif // TREZOR_RTL_H
|
||||||
|
@ -45,6 +45,8 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#undef UNUSED
|
||||||
|
|
||||||
/* Exported types ------------------------------------------------------------*/
|
/* Exported types ------------------------------------------------------------*/
|
||||||
/* Exported constants --------------------------------------------------------*/
|
/* Exported constants --------------------------------------------------------*/
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
#include <sys/bootutils.h>
|
#include <sys/bootutils.h>
|
||||||
#include <sys/irq.h>
|
#include <sys/irq.h>
|
||||||
#include <sys/mpu.h>
|
#include <sys/mpu.h>
|
||||||
|
#include <sys/sysevent.h>
|
||||||
#include <sys/systask.h>
|
#include <sys/systask.h>
|
||||||
#include <sys/system.h>
|
#include <sys/system.h>
|
||||||
#include <sys/systick.h>
|
#include <sys/systick.h>
|
||||||
@ -146,6 +147,13 @@ __attribute((no_stack_protector)) void syscall_handler(uint32_t *args,
|
|||||||
args[1] = cycles >> 32;
|
args[1] = cycles >> 32;
|
||||||
} break;
|
} 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: {
|
case SYSCALL_REBOOT_DEVICE: {
|
||||||
reboot_device();
|
reboot_device();
|
||||||
} break;
|
} break;
|
||||||
|
@ -31,6 +31,8 @@ typedef enum {
|
|||||||
SYSCALL_SYSTICK_US,
|
SYSCALL_SYSTICK_US,
|
||||||
SYSCALL_SYSTICK_US_TO_CYCLES,
|
SYSCALL_SYSTICK_US_TO_CYCLES,
|
||||||
|
|
||||||
|
SYSCALL_SYSEVENTS_POLL,
|
||||||
|
|
||||||
SYSCALL_REBOOT_DEVICE,
|
SYSCALL_REBOOT_DEVICE,
|
||||||
SYSCALL_REBOOT_TO_BOOTLOADER,
|
SYSCALL_REBOOT_TO_BOOTLOADER,
|
||||||
SYSCALL_REBOOT_AND_UPGRADE,
|
SYSCALL_REBOOT_AND_UPGRADE,
|
||||||
|
@ -70,6 +70,18 @@ uint64_t systick_us_to_cycles(uint64_t us) {
|
|||||||
return syscall_invoke2_ret64(arg0, arg1, SYSCALL_SYSTICK_US_TO_CYCLES);
|
return syscall_invoke2_ret64(arg0, arg1, SYSCALL_SYSTICK_US_TO_CYCLES);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// sysevent.h
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
#include <sys/sysevent.h>
|
||||||
|
|
||||||
|
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
|
// bootutils.h
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
@ -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) {
|
void system_exit__verified(int exit_code) {
|
||||||
systask_t *task = systask_active();
|
systask_t *task = systask_active();
|
||||||
|
|
||||||
|
@ -21,6 +21,12 @@
|
|||||||
|
|
||||||
#ifdef SYSCALL_DISPATCH
|
#ifdef SYSCALL_DISPATCH
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
#include <sys/sysevent.h>
|
||||||
|
|
||||||
|
void sysevents_poll__verified(const sysevents_t *awaited,
|
||||||
|
sysevents_t *signalled, uint32_t timeout);
|
||||||
|
|
||||||
// ---------------------------------------------------------------------
|
// ---------------------------------------------------------------------
|
||||||
#include <sys/systask.h>
|
#include <sys/systask.h>
|
||||||
|
|
||||||
|
56
core/embed/sys/task/inc/sys/sysevent.h
Normal file
56
core/embed/sys/task/inc/sys/sysevent.h
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <trezor_types.h>
|
||||||
|
|
||||||
|
// 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);
|
113
core/embed/sys/task/inc/sys/sysevent_source.h
Normal file
113
core/embed/sys/task/inc/sys/sysevent_source.h
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef KERNEL_MODE
|
||||||
|
|
||||||
|
#include <sys/sysevent.h>
|
||||||
|
#include <sys/systask.h>
|
||||||
|
|
||||||
|
// 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
|
@ -135,7 +135,10 @@ void systask_scheduler_init(systask_error_handler_t error_handler);
|
|||||||
// Returns the currently running task
|
// Returns the currently running task
|
||||||
systask_t* systask_active(void);
|
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);
|
void systask_yield_to(systask_t* task);
|
||||||
|
|
||||||
// Initializes a task with the given stack pointer, stack size
|
// 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,
|
bool systask_push_call(systask_t* task, void* fn, uint32_t arg1, uint32_t arg2,
|
||||||
uint32_t arg3);
|
uint32_t arg3);
|
||||||
|
|
||||||
// Gets the Id (zero-based index up SYSTASK_MAX_TASKS - 1) of the given task
|
// Gets the ID (zero-based index up SYSTASK_MAX_TASKS - 1) of the given task.
|
||||||
systask_id_t systask_id(systask_t* task);
|
systask_id_t systask_id(const systask_t* task);
|
||||||
|
|
||||||
// Terminates the task with the given exit code
|
// Terminates the task with the given exit code
|
||||||
//
|
//
|
||||||
|
48
core/embed/sys/task/inc/sys/unix/sdl_event.h
Normal file
48
core/embed/sys/task/inc/sys/unix/sdl_event.h
Normal file
@ -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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <trezor_types.h>
|
||||||
|
|
||||||
|
#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);
|
@ -17,6 +17,8 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifdef KERNEL_MODE
|
||||||
|
|
||||||
#include <trezor_bsp.h>
|
#include <trezor_bsp.h>
|
||||||
#include <trezor_rtl.h>
|
#include <trezor_rtl.h>
|
||||||
|
|
||||||
@ -25,11 +27,10 @@
|
|||||||
#include <sys/linker_utils.h>
|
#include <sys/linker_utils.h>
|
||||||
#include <sys/mpu.h>
|
#include <sys/mpu.h>
|
||||||
#include <sys/syscall.h>
|
#include <sys/syscall.h>
|
||||||
|
#include <sys/sysevent_source.h>
|
||||||
#include <sys/systask.h>
|
#include <sys/systask.h>
|
||||||
#include <sys/system.h>
|
#include <sys/system.h>
|
||||||
|
|
||||||
#ifdef KERNEL_MODE
|
|
||||||
|
|
||||||
// Disable stack protector for this file since it may interfere
|
// Disable stack protector for this file since it may interfere
|
||||||
// with the stack manipulation and fault handling
|
// with the stack manipulation and fault handling
|
||||||
#pragma GCC optimize("no-stack-protector")
|
#pragma GCC optimize("no-stack-protector")
|
||||||
@ -96,6 +97,12 @@ systask_t* systask_active(void) {
|
|||||||
return scheduler->active_task;
|
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) {
|
static void systask_yield(void) {
|
||||||
bool handler_mode = (__get_IPSR() & IPSR_ISR_Msk) != 0;
|
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->mpu_mode = MPU_MODE_APP;
|
||||||
task->applet = applet;
|
task->applet = applet;
|
||||||
|
|
||||||
|
// Notify all event sources about the task creation
|
||||||
|
sysevents_notify_task_created(task);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,7 +248,8 @@ static void systask_kill(systask_t* task) {
|
|||||||
} else if (task == scheduler->active_task) {
|
} else if (task == scheduler->active_task) {
|
||||||
// Free task ID
|
// Free task ID
|
||||||
scheduler->task_id_map &= ~(1 << 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
|
// Switch to the kernel task
|
||||||
systask_yield_to(&scheduler->kernel_task);
|
systask_yield_to(&scheduler->kernel_task);
|
||||||
}
|
}
|
||||||
|
279
core/embed/sys/task/sysevent.c
Normal file
279
core/embed/sys/task/sysevent.c
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef KERNEL_MODE
|
||||||
|
|
||||||
|
#include <trezor_bsp.h>
|
||||||
|
#include <trezor_rtl.h>
|
||||||
|
|
||||||
|
#include <sys/sysevent_source.h>
|
||||||
|
#include <sys/systask.h>
|
||||||
|
#include <sys/systick.h>
|
||||||
|
|
||||||
|
#ifdef TREZOR_EMULATOR
|
||||||
|
#include <sys/unix/sdl_event.h>
|
||||||
|
#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
|
76
core/embed/sys/task/unix/sdl_event.c
Normal file
76
core/embed/sys/task/unix/sdl_event.c
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <trezor_rtl.h>
|
||||||
|
|
||||||
|
#include <sys/unix/sdl_event.h>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
32
core/embed/sys/task/unix/systask.c
Normal file
32
core/embed/sys/task/unix/systask.c
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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.
|
||||||
|
|
||||||
|
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) {}
|
@ -89,6 +89,7 @@ def stm32f4_common_files(env, defines, sources, paths):
|
|||||||
"embed/sys/task/stm32/system.c",
|
"embed/sys/task/stm32/system.c",
|
||||||
"embed/sys/time/stm32/systick.c",
|
"embed/sys/time/stm32/systick.c",
|
||||||
"embed/sys/time/stm32/systimer.c",
|
"embed/sys/time/stm32/systimer.c",
|
||||||
|
"embed/sys/task/sysevent.c",
|
||||||
"embed/util/board_capabilities/stm32/board_capabilities.c",
|
"embed/util/board_capabilities/stm32/board_capabilities.c",
|
||||||
"embed/util/flash/stm32f4/flash.c",
|
"embed/util/flash/stm32f4/flash.c",
|
||||||
"embed/util/flash/stm32f4/flash_layout.c",
|
"embed/util/flash/stm32f4/flash_layout.c",
|
||||||
|
@ -108,6 +108,7 @@ def stm32u5_common_files(env, defines, sources, paths):
|
|||||||
"embed/sys/task/stm32/system.c",
|
"embed/sys/task/stm32/system.c",
|
||||||
"embed/sys/time/stm32/systick.c",
|
"embed/sys/time/stm32/systick.c",
|
||||||
"embed/sys/time/stm32/systimer.c",
|
"embed/sys/time/stm32/systimer.c",
|
||||||
|
"embed/sys/task/sysevent.c",
|
||||||
"embed/sys/trustzone/stm32u5/trustzone.c",
|
"embed/sys/trustzone/stm32u5/trustzone.c",
|
||||||
"embed/util/board_capabilities/stm32/board_capabilities.c",
|
"embed/util/board_capabilities/stm32/board_capabilities.c",
|
||||||
"embed/util/flash/stm32u5/flash.c",
|
"embed/util/flash/stm32u5/flash.c",
|
||||||
|
Loading…
Reference in New Issue
Block a user