feat(core): introduce centralized logging
What changed, and why it matters
This commit adds a new centralized logging system to the Trezor firmware. It is a feature/refactoring change: debug output is routed through a new 'syslog' layer with log levels and filters, and the debug console is now gated behind a compile-time flag. There is no direct evidence in the commit that this fixes a security vulnerability; it appears to be a developer-facing infrastructure improvement.
Treat as a normal feature commit. Review the new syslog filter parser for robustness and ensure the debug console is disabled in production/release builds. Verify that unprivileged tasks cannot use the syslog syscalls to leak sensitive data or cause denial of service via excessive logging.
Security signals we found
New debug/logging subsystem added
Debug console now gated by compile-time feature flag (USE_DBG_CONSOLE)
Syscall verifiers added for syslog_start_record, syslog_write_chunk, syslog_set_filter
Default maximum log level set to OFF in syslog_config.h
Filter parser added with bounded 128-byte filter string
Evidence from the diff
The patch introduces core/embed/sys/dbg/syslog.c, syslog.h, syslog_config.h, and core/embed/rtl/inc/rtl/logging.h. It replaces the previous ad-hoc MicroPython logging implementation in core/embed/rust/src/micropython/logging.rs with calls to syslog_start_record() / syslog_write_chunk(). Debug console support is made conditional on USE_DBG_CONSOLE/dbg_console feature flag across SCons build scripts, Rust bindings, and C code. Syscall numbers and verifiers are added for unprivileged-to-kernel logging calls on STM32. The default log level is LOG_LEVEL_OFF unless overridden. No buffer overflow, use-after-free, information leak, or authentication bypass is visible in the diff.
Changed components
core/embed/sys/dbg/syslog.ccore/embed/sys/dbg/inc/sys/syslog.hcore/embed/sys/dbg/inc/sys/syslog_config.hcore/embed/rtl/inc/rtl/logging.hcore/embed/rust/src/micropython/logging.rscore/embed/rust/src/trezorhal/syslog.rscore/embed/sys/syscall/stm32/syscall_verifiers.ccore/embed/sys/syscall/stm32/syscall_dispatch.ccore/embed/sys/syscall/stm32/syscall_stubs.ccore/site_scons/models/stm32f4_common.pycore/site_scons/models/stm32u5_common.pycore/site_scons/models/unix_common.pyInspect captured patch +885 / −40
diff --git a/core/SConscript.bootloader_emu b/core/SConscript.bootloader_emu
index dd0e7380..3097429a 100644
--- a/core/SConscript.bootloader_emu
+++ b/core/SConscript.bootloader_emu
@@ -7,6 +7,7 @@ import tools, models, ui
TREZOR_MODEL = ARGUMENTS.get('TREZOR_MODEL', 'T2T1')
CMAKELISTS = int(ARGUMENTS.get('CMAKELISTS', 0))
HW_REVISION = 'emulator'
+DBG_CONSOLE = ARGUMENTS.get('DBG_CONSOLE', '')
if not models.has_emulator(TREZOR_MODEL):
# skip bootloader build
@@ -33,6 +34,9 @@ FEATURES_WANTED = [
"usb_iface_wire",
]
+if DBG_CONSOLE != "":
+ FEATURES_WANTED += ["dbg_console"]
+
CCFLAGS_MOD = ''
CPPPATH_MOD = []
CPPDEFINES_HAL = []
diff --git a/core/SConscript.prodtest_emu b/core/SConscript.prodtest_emu
index ba454a2f..98c7981f 100644
--- a/core/SConscript.prodtest_emu
+++ b/core/SConscript.prodtest_emu
@@ -7,6 +7,7 @@ import tools, models, ui
TREZOR_MODEL = ARGUMENTS.get('TREZOR_MODEL', 'T2T1')
CMAKELISTS = int(ARGUMENTS.get('CMAKELISTS', 0))
HW_REVISION = 'emulator'
+DBG_CONSOLE = ARGUMENTS.get('DBG_CONSOLE', '')
FEATURE_FLAGS = {
"AES_GCM": True,
@@ -36,6 +37,9 @@ FEATURES_WANTED = [
"usb_iface_vcp",
]
+if DBG_CONSOLE != "":
+ FEATURES_WANTED += ["dbg_console"]
+
CCFLAGS_MOD = ''
CPPPATH_MOD = []
CPPDEFINES_MOD = [
diff --git a/core/SConscript.unix b/core/SConscript.unix
index 59f45523..90f130c4 100644
--- a/core/SConscript.unix
+++ b/core/SConscript.unix
@@ -18,6 +18,7 @@ PYOPT = ARGUMENTS.get('PYOPT', '1')
FROZEN = ARGUMENTS.get('TREZOR_EMULATOR_FROZEN', 0)
RASPI = os.getenv('TREZOR_EMULATOR_RASPI') == '1'
MICROPY_ENABLE_SOURCE_LINE = ARGUMENTS.get('MICROPY_ENABLE_SOURCE_LINE', '1')
+DBG_CONSOLE = ARGUMENTS.get('DBG_CONSOLE', '')
if BENCHMARK and PYOPT != '0':
print("BENCHMARK=1 works only with PYOPT=0.")
@@ -41,6 +42,12 @@ FEATURES_WANTED = [
"usb_iface_debug",
]
+if PYOPT == '0':
+ DBG_CONSOLE = DBG_CONSOLE or "VCP"
+
+if DBG_CONSOLE != "":
+ FEATURES_WANTED += ["dbg_console"]
+
if BITCOIN_ONLY == '0':
FEATURES_WANTED += [
"usb_iface_webauthn",
diff --git a/core/embed/projects/unix/main.c b/core/embed/projects/unix/main.c
index 6e1ff081..dfb8199e 100644
--- a/core/embed/projects/unix/main.c
+++ b/core/embed/projects/unix/main.c
@@ -39,7 +39,6 @@
#include <io/display.h>
#include <io/usb_config.h>
#include <sec/secret.h>
-#include <sys/dbg_console.h>
#include <sys/system.h>
#include <sys/systimer.h>
#include <util/flash.h>
@@ -67,6 +66,10 @@
#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"
@@ -89,7 +92,9 @@ long heap_size = 1024 * 1024 * (sizeof(mp_uint_t) / 4);
STATIC void stderr_print_strn(void *env, const char *str, size_t len) {
(void)env;
+#ifdef USE_DBG_CONSOLE
dbg_console_write(str, len);
+#endif
mp_uos_dupterm_tx_strn(str, len);
}
diff --git a/core/embed/rtl/inc/rtl/logging.h b/core/embed/rtl/inc/rtl/logging.h
new file mode 100644
index 00000000..d048e35d
--- /dev/null
+++ b/core/embed/rtl/inc/rtl/logging.h
@@ -0,0 +1,67 @@
+/*
+ * 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
+
+typedef enum {
+ LOG_LEVEL_OFF = 0,
+ LOG_LEVEL_ERR = 1,
+ LOG_LEVEL_WARN = 2,
+ LOG_LEVEL_INF = 3,
+ LOG_LEVEL_DBG = 4,
+} log_level_t;
+
+/** Information about a source module */
+typedef struct {
+ /** Source module name shown in the logs */
+ const char* name;
+ /** Length of the module name in characters */
+ size_t name_len;
+} log_source_t;
+
+#ifdef USE_DBG_CONSOLE
+
+#include <sys/syslog.h>
+
+#define LOG_DECLARE(source_name) SYSLOG_LOG_DECLARE(source_name)
+
+#define LOG_MODULE_MAX_LEVEL (SYSLOG_MODULE_MAX_LEVEL)
+
+#define LOG_ERR(fmt, ...) SYSLOG_LOG_ERR(fmt, ##__VA_ARGS__)
+#define LOG_WARN(fmt, ...) SYSLOG_LOG_WARN(fmt, ##__VA_ARGS__)
+#define LOG_INF(fmt, ...) SYSLOG_LOG_INF(fmt, ##__VA_ARGS__)
+#define LOG_DBG(fmt, ...) SYSLOG_LOG_DBG(fmt, ##__VA_ARGS__)
+
+#define LOG_HEXDUMP_DBG(prefix, data, data_size) \
+ SYSLOG_LOG_HEXDUMP_DBG(prefix, data, data_size)
+
+#else
+
+#define LOG_DECLARE(source_name)
+
+#define LOG_MODULE_MAX_LEVEL (LOG_LEVEL_OFF)
+
+#define LOG_ERR(fmt, ...)
+#define LOG_WARN(fmt, ...)
+#define LOG_INF(fmt, ...)
+#define LOG_DBG(fmt, ...)
+
+#define LOG_HEXDUMP_DBG(prefix, data, data_size)
+
+#endif // USE_DBG_CONSOLE
diff --git a/core/embed/rtl/inc/trezor_types.h b/core/embed/rtl/inc/trezor_types.h
index 044bf743..e4b1147e 100644
--- a/core/embed/rtl/inc/trezor_types.h
+++ b/core/embed/rtl/inc/trezor_types.h
@@ -25,6 +25,7 @@
// Avoid adding additional includes here unless absolutely necessary,
// as it may pollute the global namespace across the project.
+#include <inttypes.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
diff --git a/core/embed/rust/Cargo.toml b/core/embed/rust/Cargo.toml
index 71971ac3..db33f4ea 100644
--- a/core/embed/rust/Cargo.toml
+++ b/core/embed/rust/Cargo.toml
@@ -56,6 +56,7 @@ serial_number = []
storage = []
translations = ["crypto"]
secmon_layout = []
+dbg_console = []
test = [
"backlight",
"button",
diff --git a/core/embed/rust/build.rs b/core/embed/rust/build.rs
index 45ab8435..44bf3621 100644
--- a/core/embed/rust/build.rs
+++ b/core/embed/rust/build.rs
@@ -45,6 +45,7 @@ const DEFAULT_BINDGEN_MACROS_COMMON: &[&str] = &[
"-I../io/rgb_led/inc",
"-I../io/usb/inc",
"-I../sec/storage/inc",
+ "-I../sys/dbg/inc",
"-I../sys/time/inc",
"-I../sys/task/inc",
"-I../sys/power_manager/inc",
@@ -63,6 +64,7 @@ const DEFAULT_BINDGEN_MACROS_COMMON: &[&str] = &[
"-DUSE_NRF",
"-DUSE_HW_JPEG_DECODER",
"-DUSE_STORAGE",
+ "-DUSE_DBG_CONSOLE",
];
fn add_bindgen_macros<'a>(
@@ -483,6 +485,15 @@ fn generate_trezorhal_bindings() {
.allowlist_function("irq_unlock_fn")
// nrf
.allowlist_function("nrf_send_uart_data")
+ // syslog
+ .allowlist_function("syslog_start_record")
+ .allowlist_function("syslog_write_chunk")
+ .allowlist_type("log_source_t")
+ .allowlist_type("log_level_t")
+ .allowlist_var("LOG_LEVEL_DBG")
+ .allowlist_var("LOG_LEVEL_INF")
+ .allowlist_var("LOG_LEVEL_WARN")
+ .allowlist_var("LOG_LEVEL_ERR")
// c_layout
.allowlist_type("c_layout_t");
diff --git a/core/embed/rust/src/micropython/logging.rs b/core/embed/rust/src/micropython/logging.rs
index f17c673b..8d767632 100644
--- a/core/embed/rust/src/micropython/logging.rs
+++ b/core/embed/rust/src/micropython/logging.rs
@@ -1,62 +1,78 @@
-use core::str::from_utf8;
+use crate::micropython::{map::Map, module::Module, obj::Obj, qstr::Qstr};
+#[cfg(feature = "dbg_console")]
use crate::{
error::Error,
- micropython::{
- buffer::StrBuffer, map::Map, module::Module, obj::Obj, print::print, qstr::Qstr, util,
- },
- strutil,
- trezorhal::time::ticks_ms,
+ micropython::{buffer::StrBuffer, util},
+ trezorhal::syslog::{syslog_start_record, syslog_write_chunk, LogLevel},
};
-fn _log(level: &str, args: &[Obj], kwargs: &Map) -> Result<Obj, Error> {
+#[cfg(feature = "dbg_console")]
+fn _log(level: LogLevel, args: &[Obj], kwargs: &Map) -> Result<Obj, Error> {
let [module, fmt, fmt_args @ ..] = args else {
return Err(Error::TypeError);
};
- {
- let millis = ticks_ms();
- let seconds = millis / 1000;
- let mut millis_str = [b'0'; 3];
- let len = unwrap!(strutil::format_i64((millis % 1000).into(), &mut millis_str)).len();
- millis_str.rotate_left(len);
- let log_prefix = uformat!(len: 128, "{}.{} \x1b[35m{}\x1b[0m \x1b[{}\x1b[0m ",
- seconds, unwrap!(from_utf8(&millis_str)), StrBuffer::try_from(*module)?.as_ref(), level,
- );
- print(&log_prefix);
- }
- if let Ok(iface_obj) = kwargs.get(Qstr::MP_QSTR_iface) {
- if iface_obj != Obj::const_none() {
- let iface_type = iface_obj.type_().ok_or(Error::TypeError)?;
- let iface_prefix = uformat!(len: 128, "\x1b[93m[{}]\x1b[0m ", iface_type.name());
- print(&iface_prefix);
+ let module_name = StrBuffer::try_from(*module)?;
+
+ if syslog_start_record(module_name.as_ref(), level) {
+ if let Ok(iface_obj) = kwargs.get(Qstr::MP_QSTR_iface) {
+ if iface_obj != Obj::const_none() {
+ let iface_type = iface_obj.type_().ok_or(Error::TypeError)?;
+ let iface_prefix = uformat!(len: 128, "\x1b[93m[{}]\x1b[0m ", iface_type.name());
+ syslog_write_chunk(iface_prefix.as_ref(), false);
+ }
}
+
+ let msg: StrBuffer = util::modulo_format(*fmt, fmt_args)?.try_into()?;
+ syslog_write_chunk(msg.as_ref(), true);
}
- let msg: StrBuffer = util::modulo_format(*fmt, fmt_args)?.try_into()?;
- print(msg.as_ref());
- print("\n");
Ok(Obj::const_none())
}
extern "C" fn py_debug(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
- let block = |args: &[Obj], kwargs: &Map| _log("32mDEBUG", args, kwargs);
- unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
+ #[cfg(feature = "dbg_console")]
+ {
+ let block = |args: &[Obj], kwargs: &Map| _log(LogLevel::Debug, args, kwargs);
+ unsafe {
+ util::try_with_args_and_kwargs(n_args, args, kwargs, block);
+ }
+ }
+ Obj::const_none()
}
extern "C" fn py_info(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
- let block = |args: &[Obj], kwargs: &Map| _log("36mINFO", args, kwargs);
- unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
+ #[cfg(feature = "dbg_console")]
+ {
+ let block = |args: &[Obj], kwargs: &Map| _log(LogLevel::Info, args, kwargs);
+ unsafe {
+ util::try_with_args_and_kwargs(n_args, args, kwargs, block);
+ }
+ }
+ Obj::const_none()
}
extern "C" fn py_warning(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
- let block = |args: &[Obj], kwargs: &Map| _log("33mWARNING", args, kwargs);
- unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
+ #[cfg(feature = "dbg_console")]
+ {
+ let block = |args: &[Obj], kwargs: &Map| _log(LogLevel::Warn, args, kwargs);
+ unsafe {
+ util::try_with_args_and_kwargs(n_args, args, kwargs, block);
+ }
+ }
+ Obj::const_none()
}
extern "C" fn py_error(n_args: usize, args: *const Obj, kwargs: *mut Map) -> Obj {
- let block = |args: &[Obj], kwargs: &Map| _log("31mERROR", args, kwargs);
- unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
+ #[cfg(feature = "dbg_console")]
+ {
+ let block = |args: &[Obj], kwargs: &Map| _log(LogLevel::Error, args, kwargs);
+ unsafe {
+ util::try_with_args_and_kwargs(n_args, args, kwargs, block);
+ }
+ }
+ Obj::const_none()
}
#[no_mangle]
diff --git a/core/embed/rust/src/trezorhal/mod.rs b/core/embed/rust/src/trezorhal/mod.rs
index d17a8d4b..06ac54b8 100644
--- a/core/embed/rust/src/trezorhal/mod.rs
+++ b/core/embed/rust/src/trezorhal/mod.rs
@@ -51,3 +51,6 @@ pub mod irq;
#[cfg(feature = "nrf")]
pub mod nrf;
+
+#[cfg(feature = "dbg_console")]
+pub mod syslog;
diff --git a/core/embed/rust/src/trezorhal/syslog.rs b/core/embed/rust/src/trezorhal/syslog.rs
new file mode 100644
index 00000000..2b71a6d8
--- /dev/null
+++ b/core/embed/rust/src/trezorhal/syslog.rs
@@ -0,0 +1,32 @@
+use super::ffi;
+
+#[derive(PartialEq, Debug, Eq, FromPrimitive, Clone, Copy)]
+pub enum LogLevel {
+ Debug = ffi::log_level_t_LOG_LEVEL_DBG as _,
+ Info = ffi::log_level_t_LOG_LEVEL_INF as _,
+ Warn = ffi::log_level_t_LOG_LEVEL_WARN as _,
+ Error = ffi::log_level_t_LOG_LEVEL_ERR as _,
+}
+
+impl ffi::log_source_t {
+ fn new(module: &str) -> Self {
+ Self {
+ name: module.as_ptr() as *const cty::c_char,
+ name_len: module.len(),
+ }
+ }
+}
+
+pub fn syslog_start_record(module: &str, level: LogLevel) -> bool {
+ let syslog_info = ffi::log_source_t::new(module);
+ unsafe {
+ ffi::syslog_start_record(
+ &syslog_info as *const ffi::log_source_t,
+ level as ffi::log_level_t,
+ )
+ }
+}
+
+pub fn syslog_write_chunk(text: &str, end_record: bool) -> isize {
+ unsafe { ffi::syslog_write_chunk(text.as_ptr() as *const cty::c_char, text.len(), end_record) }
+}
diff --git a/core/embed/rust/trezorhal.h b/core/embed/rust/trezorhal.h
index ef13e3c5..6527e5c7 100644
--- a/core/embed/rust/trezorhal.h
+++ b/core/embed/rust/trezorhal.h
@@ -6,6 +6,7 @@
#include <io/display.h>
#include <io/display_utils.h>
#include <io/usb.h>
+#include <rtl/logging.h>
#include <rtl/secbool.h>
#include <sys/irq.h>
#include <sys/sysevent.h>
diff --git a/core/embed/sys/dbg/inc/sys/dbg_console.h b/core/embed/sys/dbg/inc/sys/dbg_console.h
index 649c862b..2fa33bb4 100644
--- a/core/embed/sys/dbg/inc/sys/dbg_console.h
+++ b/core/embed/sys/dbg/inc/sys/dbg_console.h
@@ -65,7 +65,8 @@ ssize_t dbg_console_write(const void* data, size_t data_size);
* @param fmt Format string.
* @param args Variable argument list.
*/
-void dbg_console_vprintf(const char* fmt, va_list args);
+void dbg_console_vprintf(const char* fmt, va_list args)
+ __attribute__((format(printf, 1, 0)));
/**
* @brief printf-like function for debugging.
@@ -76,7 +77,8 @@ void dbg_console_vprintf(const char* fmt, va_list args);
* @param fmt Format string.
* @param ... Variable arguments.
*/
-void dbg_console_printf(const char* fmt, ...);
+void dbg_console_printf(const char* fmt, ...)
+ __attribute__((format(printf, 1, 2)));
/**
* @brief Short alias for `dbg_console_printf()`.
diff --git a/core/embed/sys/dbg/inc/sys/syslog.h b/core/embed/sys/dbg/inc/sys/syslog.h
new file mode 100644
index 00000000..024ed6ee
--- /dev/null
+++ b/core/embed/sys/dbg/inc/sys/syslog.h
@@ -0,0 +1,235 @@
+/*
+ * 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 <stdarg.h>
+
+#include "syslog_config.h"
+
+/**
+ * Starts a new log record and verifies whether it should be logged
+ *
+ * If the record should be logged, it prepares internal context for
+ * subsequent `syslog_write_chunk()` calls.
+ *
+ * The function is safe to call from interrupt context.
+ *
+ * @param source Source module information
+ * @param level Log level of the message
+ * @return true if the record should be logged, false otherwise
+ *
+ */
+bool syslog_start_record(const log_source_t* source, log_level_t level);
+
+/**
+ * Writes a message (or a part of it) to the log
+ *
+ * Should be called only after a successful `syslog_start_record()` call.
+ * Multiple calls to `syslog_write_chunk()` may be used to write
+ * a single log record in smaller parts. The `end_record` parameter
+ * indicates whether this is the last chunk of the message
+ *
+ * The function is safe to call from interrupt context.
+ *
+ * @param text Text chunk to write
+ * @param text_len Length of the text chunk
+ * @param end_record true if this is the last chunk of the record
+ * @return Number of bytes written, or negative value on error
+ */
+
+ssize_t syslog_write_chunk(const char* text, size_t text_len, bool end_record);
+
+/**
+ * Sets the logging filter string
+ *
+ * Filter string is processed left to right, each part modifies the logging
+ * configuration. Each part starts with '+' (enable) or '-' (disable), followed
+ * by optional log level digit (1-4), followed by optional module name pattern
+ * (with '*' wildcard support at the end). Examples:
+ *
+ * `+*` Enable all modules up to DBG level
+ * `+1*` Enable logging for all modules up to ERR level
+ * `-*` Disable all logging for all modules
+ * `+4power*` Enable DBG level for power modules (starting with 'power')
+ * `-3*` Disable DBG for all modules, keep WRN level and below
+ * `+py.* Enable all python modules (py.*) up to DBG level
+ * `+3* -py.core*` Enable all modules up to INF level, except 'py.core*'
+ *
+ * Note: space before or after parts is ignored.
+ *
+ * Despite other functions in this file being safe to call from interrupt
+ * context, `syslog_set_filter()` must not be called from interrupt context.
+ *
+ * @param filter Filter string
+ * @param filter_len Length of the filter string
+ * @return true if the filter was successfully set, false otherwise
+ */
+bool syslog_set_filter(const char* filter, size_t filter_len);
+
+/**
+ * Logs a messagge (printf-style with va_list)
+ *
+ * Message is logged if it passes the current logging filter (
+ * see `syslog_set_filter()`).
+ *
+ * @param source Source module information
+ * @param level Log level of the message
+ * @param fmt Format string (printf-style)
+ * @param args Variable arguments list
+ */
+void syslog_vprintf(const log_source_t* source, log_level_t level,
+ const char* fmt, va_list args)
+ __attribute__((format(printf, 3, 0)));
+
+/**
+ * Logs a message (printf-style)
+ *
+ * Message is logged if it passes the current logging filter (
+ * see `syslog_set_filter()`).
+ *
+ * @param source Source module information
+ * @param level Log level of the message
+ * @param fmt Format string (printf-style)
+ * @param ... Variable arguments
+ */
+void syslog_printf(const log_source_t* source, log_level_t level,
+ const char* fmt, ...) __attribute__((format(printf, 3, 4)));
+
+/**
+ * Logs a hex dump of binary data and an optional prefix string
+ *
+ * Message is logged if it passes the current logging filter (
+ * see `syslog_set_filter()`).
+ *
+ * @param source Source module information
+ * @param level Log level of the message
+ * @param prefix Optional prefix string to log before the hex data
+ * @param data Binary data to log
+ * @param data_size Size of the binary data
+ */
+
+void syslog_print_hex(const log_source_t* source, log_level_t level,
+ const char* prefix, const uint8_t* data,
+ size_t data_size);
+
+/**
+ * Enables logging in the current compilation unit.
+ *
+ * All subsequent SYSLOG_LOG_*() calls will use this module information.
+ *
+ * It's expected that SYSLOG_<module_name>_LOG_LEVEL is defined
+ * to one of LOG_LEVEL_* values.
+ */
+#define SYSLOG_LOG_DECLARE(module_name) \
+ static const log_source_t g_syslog_source __attribute__((used)) = { \
+ .name = #module_name, \
+ .name_len = sizeof(#module_name) - 1, \
+ }; \
+ static const log_level_t g_syslog_max_level __attribute__((used)) = \
+ SYSLOG_##module_name##_MAX_LOG_LEVEL;
+
+/**
+ * Gets the maximum log level of the current module
+ */
+#define SYSLOG_MODULE_MAX_LEVEL g_syslog_max_level
+
+/**
+ * Logs an error message
+ *
+ * Message is logged if it passes the current logging filter (
+ * see `syslog_set_filter()`).
+ *
+ * @param fmt Format string (printf-style)
+ * @param ... Variable arguments
+ */
+#define SYSLOG_LOG_ERR(fmt, ...) \
+ do { \
+ if (g_syslog_max_level >= LOG_LEVEL_ERR) { \
+ syslog_printf(&g_syslog_source, LOG_LEVEL_ERR, fmt, ##__VA_ARGS__); \
+ } \
+ } while (0)
+
+/**
+ * Logs a warning message
+ *
+ * Message is logged if it passes the current logging filter (
+ * see `syslog_set_filter()`).
+ *
+ * @param fmt Format string (printf-style)
+ * @param ... Variable arguments
+ */
+#define SYSLOG_LOG_WARN(fmt, ...) \
+ do { \
+ if (g_syslog_max_level >= LOG_LEVEL_WARN) { \
+ syslog_printf(&g_syslog_source, LOG_LEVEL_WARN, fmt, ##__VA_ARGS__); \
+ } \
+ } while (0)
+
+/**
+ * Logs an informational message
+ *
+ * Message is logged if it passes the current logging filter (
+ * see `syslog_set_filter()`).
+ *
+ * @param fmt Format string (printf-style)
+ * @param ... Variable arguments
+ */
+#define SYSLOG_LOG_INF(fmt, ...) \
+ do { \
+ if (g_syslog_max_level >= LOG_LEVEL_INF) { \
+ syslog_printf(&g_syslog_source, LOG_LEVEL_INF, fmt, ##__VA_ARGS__); \
+ } \
+ } while (0)
+
+/**
+ * Logs a debug message
+ *
+ * Message is logged if it passes the current logging filter (
+ * see `syslog_set_filter()`).
+ *
+ * @param fmt Format string (printf-style)
+ * @param ... Variable arguments
+ */
+#define SYSLOG_LOG_DBG(fmt, ...) \
+ do { \
+ if (g_syslog_max_level >= LOG_LEVEL_DBG) { \
+ syslog_printf(&g_syslog_source, LOG_LEVEL_DBG, fmt, ##__VA_ARGS__); \
+ } \
+ } while (0)
+
+/**
+ * Logs a hex dump of binary data with an optional prefix string
+ *
+ * Message is logged if it passes the current logging filter (
+ * see `syslog_set_filter()`).
+ *
+ * @param prefix Optional prefix string to log before the hex data
+ * @param data Binary data to log
+ * @param data_size Size of the binary data
+ */
+#define SYSLOG_LOG_HEXDUMP_DBG(prefix, data, data_size) \
+ do { \
+ if (g_syslog_max_level >= LOG_LEVEL_DBG) { \
+ syslog_print_hex(&g_syslog_source, LOG_LEVEL_DBG, prefix, data, \
+ data_size); \
+ } \
+ } while (0)
diff --git a/core/embed/sys/dbg/inc/sys/syslog_config.h b/core/embed/sys/dbg/inc/sys/syslog_config.h
new file mode 100644
index 00000000..7ba47eea
--- /dev/null
+++ b/core/embed/sys/dbg/inc/sys/syslog_config.h
@@ -0,0 +1,45 @@
+
+/*
+ * 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
+
+// Maximum default log level for all modules if not overriden in
+// by defining SYSLOG_<module_name>_MAX_LOG_LEVEL during compilation
+#ifndef SYSLOG_DEFAULT_LOG_LEVEL
+#define SYSLOG_DEFAULT_LOG_LEVEL LOG_LEVEL_OFF
+#endif
+
+// Maximum default log level for specific modules
+// (can be overriden by defining SYSLOG_<module_name>_MAX_LOG_LEVEL)
+
+#ifndef SYSLOG_touch_driver_MAX_LOG_LEVEL
+#define SYSLOG_touch_driver_MAX_LOG_LEVEL SYSLOG_DEFAULT_LOG_LEVEL
+#endif
+
+// Optiga command log is relatively quiet
+#ifndef SYSLOG_optiga_MAX_LOG_LEVEL
+#define SYSLOG_optiga_MAX_LOG_LEVEL SYSLOG_DEFAULT_LOG_LEVEL
+#endif
+
+// Optiga transport log can be spammy
+#ifndef SYSLOG_optiga_transport_MAX_LOG_LEVEL
+#define SYSLOG_optiga_transport_MAX_LOG_LEVEL SYSLOG_DEFAULT_LOG_LEVEL
+#endif
+
+// Add more module-specific max log level definitions here...
diff --git a/core/embed/sys/dbg/syslog.c b/core/embed/sys/dbg/syslog.c
new file mode 100644
index 00000000..6302df00
--- /dev/null
+++ b/core/embed/sys/dbg/syslog.c
@@ -0,0 +1,287 @@
+/*
+ * 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 <rtl/logging.h>
+#include <rtl/mini_printf.h>
+#include <rtl/strutils.h>
+#include <sys/dbg_console.h>
+#include <sys/systick.h>
+
+#ifndef TREZOR_EMULATOR
+#include <sys/irq.h>
+#endif
+
+#include <stdarg.h>
+
+#ifdef KERNEL_MODE
+
+#define EOL_STRING "\r\n"
+#define SYSLOG_MAX_FILTER_LEN 128
+
+#define ESC_COLOR_NORMAL "\e[0m"
+#define ESC_COLOR_SOURCE "\e[35m"
+#define ESC_COLOR_ERR "\e[31m"
+#define ESC_COLOR_WARN "\e[33m"
+#define ESC_COLOR_INF "\e[36m"
+#define ESC_COLOR_DBG "\e[32m"
+
+typedef struct {
+ // Current filter string
+ char filter[SYSLOG_MAX_FILTER_LEN];
+ // Not ended record
+ bool eol_needed;
+} syslog_t;
+
+static syslog_t g_syslog;
+
+static bool syslog_filter_match(const log_source_t* source, log_level_t level) {
+ syslog_t* syslog = &g_syslog;
+
+ const char* p = syslog->filter;
+
+ // Start with inclusion of everything if the filter is empty
+ // or starts with '-';
+ bool included = (*p == '-' || *p == '\0');
+
+ while (*p != '\0') {
+ // Parse operation
+ char op = *p++;
+ if (op != '-' && op != '+') {
+ // Error in filter format
+ break;
+ }
+
+ // Parse log level threshold
+ log_level_t threshold = op == '-' ? LOG_LEVEL_ERR : LOG_LEVEL_DBG;
+ if (*p >= '1' && *p <= '4') {
+ threshold = LOG_LEVEL_OFF + (uint8_t)(*p - '0');
+ p++;
+ }
+
+ const char* s = source->name;
+ const char* s_end = s + source->name_len;
+
+ // Parse module name
+ while (*p != '\0' && s < s_end && *p == *s) {
+ p++;
+ s++;
+ }
+
+ // Wildcard at the end?
+ if (*p == '*') {
+ p++;
+ s = s_end;
+ } else {
+ // Skip remaining characters in module name
+ while (*p != '\0' && *p != '-' && *p != '+') {
+ p++;
+ }
+ }
+
+ // Skip spaces
+ while (*p == ' ') {
+ p++;
+ }
+
+ // Module name matched?
+ if (s == s_end && (*p == '+' || *p == '-' || *p == '\0')) {
+ if (op == '-' && level >= threshold) {
+ included = false;
+ } else if (level <= threshold) {
+ included = true;
+ }
+ }
+ }
+
+ return included;
+}
+
+static const char* log_level_str(log_level_t level) {
+ switch (level) {
+ case LOG_LEVEL_ERR:
+ return ESC_COLOR_ERR "ERR" ESC_COLOR_NORMAL;
+ case LOG_LEVEL_WARN:
+ return ESC_COLOR_WARN "WRN" ESC_COLOR_NORMAL;
+ case LOG_LEVEL_INF:
+ return ESC_COLOR_INF "INF" ESC_COLOR_NORMAL;
+ case LOG_LEVEL_DBG:
+ return ESC_COLOR_DBG "DBG" ESC_COLOR_NORMAL;
+ default:
+ return "UNK";
+ }
+}
+
+bool syslog_start_record(const log_source_t* source, log_level_t level) {
+ syslog_t* syslog = &g_syslog;
+
+ if (syslog_filter_match(source, level)) {
+ // Prepare a record header
+ uint32_t ticks = systick_ms();
+ uint32_t seconds = ticks / 1000;
+ uint32_t msec = ticks % 1000;
+ const char* level_str = log_level_str(level);
+
+#ifndef TREZOR_EMULATOR
+ irq_key_t irq_key = irq_lock();
+#endif
+
+ const char* eol = syslog->eol_needed ? EOL_STRING : "";
+ syslog->eol_needed = true;
+
+#ifndef TREZOR_EMULATOR
+ irq_unlock(irq_key);
+#endif
+
+ int name_len = (int)MIN(source->name_len, INT32_MAX);
+
+ dbg_console_printf("%s%" PRIu32 ".%03" PRIu32 " " ESC_COLOR_SOURCE
+ "%*s" ESC_COLOR_NORMAL " %s ",
+ eol, seconds, msec, name_len, source->name, level_str);
+
+ return true;
+ } else {
+ return false;
+ }
+}
+
+ssize_t syslog_write_chunk(const char* text, size_t text_len, bool end_record) {
+ syslog_t* syslog = &g_syslog;
+
+ if (text_len > 0) {
+#ifndef TREZOR_EMULATOR
+ irq_key_t irq_key = irq_lock();
+#endif
+ syslog->eol_needed = true;
+#ifndef TREZOR_EMULATOR
+ irq_unlock(irq_key);
+#endif
+ }
+
+ // Write text chunk
+ ssize_t bytes_written = dbg_console_write(text, text_len);
+
+ if (end_record && bytes_written == (ssize_t)text_len) {
+ // Finish the record with a newline
+ dbg_console_write(EOL_STRING, strlen(EOL_STRING));
+
+#ifndef TREZOR_EMULATOR
+ irq_key_t irq_key = irq_lock();
+#endif
+ syslog->eol_needed = false;
+#ifndef TREZOR_EMULATOR
+ irq_unlock(irq_key);
+#endif
+ }
+
+ return bytes_written;
+}
+
+bool syslog_set_filter(const char* filter, size_t filter_len) {
+ syslog_t* syslog = &g_syslog;
+
+ // Filter string too long?
+ if (filter_len > sizeof(syslog->filter) - 1) {
+ return false;
+ }
+
+ // Locking interrutps here ensures that `syslog_start_record()`
+ // potentially running in interrupt context does not read partial
+ // filter string.
+
+#ifndef TREZOR_EMULATOR
+ irq_key_t irq_key = irq_lock();
+#endif
+
+ strncpy(syslog->filter, filter, filter_len);
+ syslog->filter[filter_len] = '\0';
+
+#ifndef TREZOR_EMULATOR
+ irq_unlock(irq_key);
+#endif
+
+ return true;
+}
+
+#endif // KERNEL_MODE
+
+void syslog_vprintf(const log_source_t* source, log_level_t level,
+ const char* fmt, va_list args) {
+ if (syslog_start_record(source, level)) {
+ char msg[160];
+ size_t msg_len = mini_vsnprintf(msg, sizeof(msg), fmt, args);
+ syslog_write_chunk(msg, msg_len, true);
+ }
+}
+
+void syslog_printf(const log_source_t* source, log_level_t level,
+ const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ syslog_vprintf(source, level, fmt, args);
+ va_end(args);
+}
+
+void syslog_print_hex(const log_source_t* source, log_level_t level,
+ const char* prefix, const uint8_t* data,
+ size_t data_size) {
+ if (syslog_start_record(source, level)) {
+ syslog_write_chunk(prefix, strlen(prefix), data_size == 0);
+ if (data_size > 0) {
+ syslog_write_chunk(" ", 1, false);
+ }
+ for (size_t i = 0; i < data_size; i++) {
+ char byte_str[3];
+ cstr_encode_hex(byte_str, sizeof(byte_str), &data[i], sizeof(uint8_t));
+ bool last_chunk = (i == data_size - 1);
+ syslog_write_chunk(byte_str, strlen(byte_str), last_chunk);
+ }
+ }
+}
+
+#ifdef TREZOR_PRODTEST
+
+#include <rtl/cli.h>
+
+static void prodtest_set_log_filter(cli_t* cli) {
+ const char* filter = cli_arg(cli, "filter");
+ size_t filter_len = strlen(filter);
+
+ if (filter_len == 0) {
+ cli_error_arg(cli, "Expecting filter string.");
+ return;
+ }
+
+ if (cli_arg_count(cli) > 1) {
+ cli_error_arg_count(cli);
+ return;
+ }
+
+ if (!syslog_set_filter(filter, filter_len)) {
+ cli_error(cli, CLI_ERROR, "Failed to set log filter.");
+ }
+
+ cli_ok(cli, "");
+}
+
+PRODTEST_CLI_CMD(.name = "log-filter", .func = prodtest_set_log_filter,
+ .info = "Set logging filter", .args = "<filter>");
+
+#endif // TREZOR_PRODTEST
diff --git a/core/embed/sys/syscall/inc/sys/syscall_numbers.h b/core/embed/sys/syscall/inc/sys/syscall_numbers.h
index f6b326d0..a8859d10 100644
--- a/core/embed/sys/syscall/inc/sys/syscall_numbers.h
+++ b/core/embed/sys/syscall/inc/sys/syscall_numbers.h
@@ -47,6 +47,10 @@ typedef enum {
SYSCALL_DBG_CONSOLE_READ,
SYSCALL_DBG_CONSOLE_WRITE,
+ SYSCALL_SYSLOG_START_RECORD,
+ SYSCALL_SYSLOG_WRITE_CHUNK,
+ SYSCALL_SYSLOG_SET_FILTER,
+
SYSCALL_BOOT_IMAGE_CHECK,
SYSCALL_BOOT_IMAGE_REPLACE,
diff --git a/core/embed/sys/syscall/stm32/syscall_dispatch.c b/core/embed/sys/syscall/stm32/syscall_dispatch.c
index a2fbaea5..acdc382f 100644
--- a/core/embed/sys/syscall/stm32/syscall_dispatch.c
+++ b/core/embed/sys/syscall/stm32/syscall_dispatch.c
@@ -183,6 +183,26 @@ __attribute((no_stack_protector)) void syscall_handler(uint32_t *args,
size_t data_size = (size_t)args[1];
args[0] = dbg_console_write__verified(data, data_size);
} break;
+
+ case SYSCALL_SYSLOG_START_RECORD: {
+ const log_source_t *source = (const log_source_t *)args[0];
+ uint8_t level = (uint8_t)args[1];
+ args[0] = syslog_start_record__verified(source, level);
+ } break;
+
+ case SYSCALL_SYSLOG_WRITE_CHUNK: {
+ const char *text = (const char *)args[0];
+ size_t text_len = (size_t)args[1];
+ bool end_record = (bool)args[2];
+ args[0] = syslog_write_chunk__verified(text, text_len, end_record);
+ } break;
+
+ case SYSCALL_SYSLOG_SET_FILTER: {
+ const char *filter = (const char *)args[0];
+ size_t filter_len = (size_t)args[1];
+ args[0] = syslog_set_filter__verified(filter, filter_len);
+ } break;
+
#endif
case SYSCALL_BOOT_IMAGE_CHECK: {
diff --git a/core/embed/sys/syscall/stm32/syscall_stubs.c b/core/embed/sys/syscall/stm32/syscall_stubs.c
index 4cb79fd1..cd21585f 100644
--- a/core/embed/sys/syscall/stm32/syscall_stubs.c
+++ b/core/embed/sys/syscall/stm32/syscall_stubs.c
@@ -114,6 +114,31 @@ ssize_t dbg_console_write(const void *data, size_t data_size) {
#endif // USE_DBG_CONSOLE
+// =============================================================================
+// logging.h
+// =============================================================================
+
+#ifdef USE_DBG_CONSOLE
+
+#include <rtl/logging.h>
+
+bool syslog_start_record(const log_source_t *source, log_level_t level) {
+ return (bool)syscall_invoke2((uint32_t)source, level,
+ SYSCALL_SYSLOG_START_RECORD);
+}
+
+ssize_t syslog_write_chunk(const char *text, size_t text_len, bool end_record) {
+ return (ssize_t)syscall_invoke3((uint32_t)text, text_len, end_record,
+ SYSCALL_SYSLOG_WRITE_CHUNK);
+}
+
+bool syslog_set_filter(const char *filter, size_t filter_len) {
+ return (bool)syscall_invoke2((uint32_t)filter, filter_len,
+ SYSCALL_SYSLOG_SET_FILTER);
+}
+
+#endif
+
// =============================================================================
// boot_image.h
// =============================================================================
diff --git a/core/embed/sys/syscall/stm32/syscall_verifiers.c b/core/embed/sys/syscall/stm32/syscall_verifiers.c
index e5f13b10..d99971d4 100644
--- a/core/embed/sys/syscall/stm32/syscall_verifiers.c
+++ b/core/embed/sys/syscall/stm32/syscall_verifiers.c
@@ -120,6 +120,49 @@ access_violation:
// ---------------------------------------------------------------------
+#ifdef USE_DBG_CONSOLE
+
+bool syslog_start_record__verified(const log_source_t *source,
+ log_level_t level) {
+ if (!probe_read_access(source, sizeof(*source))) {
+ goto access_violation;
+ }
+
+ return syslog_start_record(source, level);
+access_violation:
+ apptask_access_violation();
+ return false;
+}
+
+ssize_t syslog_write_chunk__verified(const char *text, size_t text_len,
+ bool end_record) {
+ if (!probe_read_access(text, text_len)) {
+ goto access_violation;
+ }
+
+ return syslog_write_chunk(text, text_len, end_record);
+
+access_violation:
+ apptask_access_violation();
+ return -1;
+}
+
+bool syslog_set_filter__verified(const char *module_name, log_level_t level) {
+ if (!probe_read_access(module_name, strlen(module_name))) {
+ goto access_violation;
+ }
+
+ return syslog_set_filter(module_name, level);
+
+access_violation:
+ apptask_access_violation();
+ return false;
+}
+
+#endif // USE_DBG_CONSOLE
+
+// ---------------------------------------------------------------------
+
bool boot_image_check__verified(const boot_image_t *image) {
if (!probe_read_access(image, sizeof(*image))) {
goto access_violation;
diff --git a/core/embed/sys/syscall/stm32/syscall_verifiers.h b/core/embed/sys/syscall/stm32/syscall_verifiers.h
index 43ea17a8..b4e5528f 100644
--- a/core/embed/sys/syscall/stm32/syscall_verifiers.h
+++ b/core/embed/sys/syscall/stm32/syscall_verifiers.h
@@ -56,6 +56,21 @@ ssize_t dbg_console_write__verified(const void *data, size_t data_size);
#endif
+// ---------------------------------------------------------------------
+#ifdef USE_DBG_CONSOLE
+
+#include <rtl/logging.h>
+
+bool syslog_start_record__verified(const log_source_t *source,
+ log_level_t level);
+
+ssize_t syslog_write_chunk__verified(const char *text, size_t text_len,
+ bool end_record);
+
+bool syslog_set_filter__verified(const char *module_name, log_level_t level);
+
+#endif
+
// ---------------------------------------------------------------------
#include <sys/bootutils.h>
diff --git a/core/embed/sys/task/unix/system.c b/core/embed/sys/task/unix/system.c
index 68643ad0..271f66cc 100644
--- a/core/embed/sys/task/unix/system.c
+++ b/core/embed/sys/task/unix/system.c
@@ -22,18 +22,23 @@
#include <stdlib.h>
#include <sys/bootutils.h>
-#include <sys/dbg_console.h>
#include <sys/system.h>
#include <sys/systick.h>
#include <sys/systimer.h>
+#ifdef USE_DBG_CONSOLE
+#include <sys/dbg_console.h>
+#endif
+
systask_error_handler_t g_error_handler = NULL;
void system_init(systask_error_handler_t error_handler) {
g_error_handler = error_handler;
systick_init();
systimer_init();
+#ifdef USE_DBG_CONSOLE
dbg_console_init();
+#endif
}
void system_deinit(void) { systick_deinit(); }
diff --git a/core/site_scons/models/stm32f4_common.py b/core/site_scons/models/stm32f4_common.py
index 3ea8485e..5520a3e4 100644
--- a/core/site_scons/models/stm32f4_common.py
+++ b/core/site_scons/models/stm32f4_common.py
@@ -111,10 +111,12 @@ def stm32f4_common_files(env, features_wanted, defines, sources, paths):
if "dbg_console" in features_wanted:
sources += [
"embed/sys/dbg/dbg_console.c",
+ "embed/sys/dbg/syslog.c",
"embed/sys/dbg/stm32/dbg_console_backend.c",
]
paths += ["embed/sys/dbg/inc"]
defines += [("USE_DBG_CONSOLE", "1")]
+ features_available.append("dbg_console")
if env.get("DBG_CONSOLE") == "VCP" and "usb" in features_wanted:
features_wanted += ["usb_iface_vcp"]
diff --git a/core/site_scons/models/stm32u5_common.py b/core/site_scons/models/stm32u5_common.py
index d54a3965..6f5ca33e 100644
--- a/core/site_scons/models/stm32u5_common.py
+++ b/core/site_scons/models/stm32u5_common.py
@@ -140,10 +140,12 @@ def stm32u5_common_files(env, features_wanted, defines, sources, paths):
if "dbg_console" in features_wanted:
sources += [
"embed/sys/dbg/dbg_console.c",
+ "embed/sys/dbg/syslog.c",
"embed/sys/dbg/stm32/dbg_console_backend.c",
]
paths += ["embed/sys/dbg/inc"]
defines += [("USE_DBG_CONSOLE", "1")]
+ features_available.append("dbg_console")
if env.get("DBG_CONSOLE") == "VCP" and "usb" in features_wanted:
features_wanted += ["usb_iface_vcp"]
diff --git a/core/site_scons/models/unix_common.py b/core/site_scons/models/unix_common.py
index fa70171e..bdb14f53 100644
--- a/core/site_scons/models/unix_common.py
+++ b/core/site_scons/models/unix_common.py
@@ -43,8 +43,6 @@ def unix_common_files(env, features_wanted, defines, sources, paths):
"embed/sec/rng/unix/rng.c",
"embed/sec/rng/rng_common.c",
"embed/sec/time_estimate/unix/time_estimate.c",
- "embed/sys/dbg/dbg_console.c",
- "embed/sys/dbg/unix/dbg_console_backend.c",
"embed/sys/mpu/unix/mpu.c",
"embed/sys/notify/notify.c",
"embed/sys/startup/unix/bootutils.c",
@@ -80,4 +78,14 @@ def unix_common_files(env, features_wanted, defines, sources, paths):
if "usb_iface_vcp" in features_wanted:
defines += [("USE_USB_IFACE_VCP", "1")]
+ if "dbg_console" in features_wanted:
+ sources += [
+ "embed/sys/dbg/dbg_console.c",
+ "embed/sys/dbg/syslog.c",
+ "embed/sys/dbg/unix/dbg_console_backend.c",
+ ]
+ paths += ["embed/sys/dbg/inc"]
+ defines += [("USE_DBG_CONSOLE", "1")]
+ features_available.append("dbg_console")
+
return features_available
Why this scored 18/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.