feat(core): introduce DBG_CONSOLE build argument
What changed, and why it matters
This commit adds a new build-time option called DBG_CONSOLE that lets developers choose how debug messages are output during firmware builds. It is a build-system change only—no actual device code is modified. By default it is empty/off, and in some non-production debug builds it automatically falls back to using the existing USB virtual COM port (VCP) for debug output. There is no direct security vulnerability in this change, but it makes debug-console support a configurable feature across bootloader, firmware, kernel, and other components.
No immediate action required. Treat this as a build-system hygiene change. If the new 'dbg_console' feature is implemented in board configuration or runtime code, review that implementation separately to ensure debug interfaces cannot be enabled in production builds and do not expose secrets or unauthorized command channels.
Security signals we found
Debug console feature now centrally configurable at build time
Non-production debug builds (PYOPT=0) automatically enable VCP debug console
No runtime code changes or new attack surface visible in the diff
Build-only change; security relevance depends on downstream feature implementation
Evidence from the diff
The patch introduces a DBG_CONSOLE build argument propagated through core/Makefile into multiple SCons build scripts (boardloader, bootloader, firmware, kernel, prodtest, secmon). When DBG_CONSOLE is non-empty, the feature flag ‘dbg_console’ is added to FEATURES_WANTED and passed into models.configure_board via the SCons Environment. In SConscript.firmware and SConscript.kernel, PYOPT=0 builds now default DBG_CONSOLE to ‘VCP’ instead of explicitly requesting ‘usb_iface_debug’ and ‘usb_iface_vcp’. This is a refactoring of how debug-console features are requested; it does not change runtime behavior unless a developer explicitly sets DBG_CONSOLE.
Changed components
core/Makefilecore/SConscript.boardloadercore/SConscript.bootloadercore/SConscript.firmwarecore/SConscript.kernelcore/SConscript.prodtestcore/SConscript.secmonInspect captured patch +47 / −14
diff --git a/core/Makefile b/core/Makefile
index ea2695cd..7e5022cd 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -49,6 +49,7 @@ QUIET_MODE ?= 0
TREZOR_DISABLE_ANIMATION ?= $(if $(filter 0,$(PYOPT)),1,0)
STORAGE_INSECURE_TESTING_MODE ?= 0
UI_PERFORMANCE_OVERLAY ?= 0
+DBG_CONSOLE ?=
# If set, VCP writes will be blocking, in order to allow reliable debug data transmission over VCP.
# Disabled by default, to prevent debug firmware from getting stuck while writing log messages (if the host is not reading them).
@@ -145,7 +146,8 @@ SCONS_VARS = \
TREZOR_MEMPERF="$(TREZOR_MEMPERF)" \
TREZOR_MODEL="$(TREZOR_MODEL)" \
UI_PERFORMANCE_OVERLAY="$(UI_PERFORMANCE_OVERLAY)" \
- BLOCK_ON_VCP="$(BLOCK_ON_VCP)"
+ BLOCK_ON_VCP="$(BLOCK_ON_VCP)" \
+ DBG_CONSOLE="$(DBG_CONSOLE)"
SCONS_OPTS = -Q -j $(JOBS)
ifeq ($(QUIET_MODE),1)
diff --git a/core/SConscript.boardloader b/core/SConscript.boardloader
index a9c4768c..ba56bc28 100644
--- a/core/SConscript.boardloader
+++ b/core/SConscript.boardloader
@@ -6,6 +6,7 @@ import tools, models
TREZOR_MODEL = ARGUMENTS.get('TREZOR_MODEL', 'T2T1')
CMAKELISTS = int(ARGUMENTS.get('CMAKELISTS', 0))
HW_REVISION = ARGUMENTS.get('HW_REVISION', None)
+DBG_CONSOLE = ARGUMENTS.get('DBG_CONSOLE', '')
FEATURES_WANTED = [
"boot_ucb",
@@ -26,11 +27,15 @@ CPPDEFINES_HAL = []
SOURCE_HAL = []
PATH_HAL = []
+if DBG_CONSOLE != "":
+ FEATURES_WANTED += ["dbg_console"]
+
env = Environment(ENV=os.environ,
CFLAGS='%s -DPRODUCTION=%s' % (ARGUMENTS.get('CFLAGS', ''), ARGUMENTS.get('PRODUCTION', '0')),
CPPDEFINES_IMPLICIT=[],
CPPDEFPREFIX="-D'",
CPPDEFSUFFIX="'",
+ DBG_CONSOLE=DBG_CONSOLE,
)
FEATURES_AVAILABLE = models.configure_board(TREZOR_MODEL, HW_REVISION, FEATURES_WANTED, env, CPPDEFINES_HAL, SOURCE_HAL, PATH_HAL)
diff --git a/core/SConscript.bootloader b/core/SConscript.bootloader
index 2c5a1794..effcf23e 100644
--- a/core/SConscript.bootloader
+++ b/core/SConscript.bootloader
@@ -10,6 +10,7 @@ BOOTLOADER_QA = ARGUMENTS.get('BOOTLOADER_QA', '0') == '1'
PRODUCTION = 0 if BOOTLOADER_QA else ARGUMENTS.get('PRODUCTION', '0') == '1'
HW_REVISION = ARGUMENTS.get('HW_REVISION', None)
UI_PERFORMANCE_OVERLAY = ARGUMENTS.get('UI_PERFORMANCE_OVERLAY', '0') == '1'
+DBG_CONSOLE = ARGUMENTS.get('DBG_CONSOLE', '')
FEATURES_WANTED = [
"ble",
@@ -43,12 +44,16 @@ SOURCE_HAL = []
PATH_HAL = []
RUST_UI_FEATURES = []
+if DBG_CONSOLE != "":
+ FEATURES_WANTED += ["dbg_console"]
+
env = Environment(
ENV=os.environ,
CFLAGS=f"{ARGUMENTS.get('CFLAGS', '')} -DPRODUCTION={int(PRODUCTION)} -DBOOTLOADER_QA={int(BOOTLOADER_QA)}",
CPPDEFINES_IMPLICIT=[],
CPPDEFPREFIX="-D'",
CPPDEFSUFFIX="'",
+ DBG_CONSOLE=DBG_CONSOLE,
)
FEATURES_AVAILABLE = models.configure_board(TREZOR_MODEL, HW_REVISION, FEATURES_WANTED, env, CPPDEFINES_HAL, SOURCE_HAL, PATH_HAL)
diff --git a/core/SConscript.firmware b/core/SConscript.firmware
index 595e44df..534d2064 100644
--- a/core/SConscript.firmware
+++ b/core/SConscript.firmware
@@ -25,6 +25,7 @@ MICROPY_ENABLE_SOURCE_LINE = ARGUMENTS.get('MICROPY_ENABLE_SOURCE_LINE', '0')
DISABLE_ANIMATION = ARGUMENTS.get('TREZOR_DISABLE_ANIMATION', '0') == '1'
BLOCK_ON_VCP = ARGUMENTS.get('BLOCK_ON_VCP', '0') == '1'
UI_PERFORMANCE_OVERLAY = ARGUMENTS.get('UI_PERFORMANCE_OVERLAY', '0') == '1'
+DBG_CONSOLE = ARGUMENTS.get('DBG_CONSOLE', '')
STORAGE_INSECURE_TESTING_MODE = ARGUMENTS.get('STORAGE_INSECURE_TESTING_MODE', '0') == '1'
if STORAGE_INSECURE_TESTING_MODE and PRODUCTION:
@@ -72,6 +73,12 @@ if DISABLE_OPTIGA:
raise RuntimeError("DISABLE_OPTIGA requires PYOPT=0")
FEATURES_WANTED.remove("optiga")
+if PYOPT == '0':
+ DBG_CONSOLE = DBG_CONSOLE or "VCP"
+
+if DBG_CONSOLE != "":
+ FEATURES_WANTED += ["dbg_console"]
+
CCFLAGS_MOD = ''
CPPPATH_MOD = []
CPPDEFINES_MOD = []
@@ -429,6 +436,7 @@ env = Environment(
CPPDEFINES_IMPLICIT=[],
CPPDEFPREFIX="-D'",
CPPDEFSUFFIX="'",
+ DBG_CONSOLE=DBG_CONSOLE,
)
FEATURES_AVAILABLE = models.configure_board(TREZOR_MODEL, HW_REVISION, FEATURES_WANTED, env, CPPDEFINES_HAL, SOURCE_HAL, PATH_HAL)
diff --git a/core/SConscript.kernel b/core/SConscript.kernel
index 50b62e87..d89cf829 100644
--- a/core/SConscript.kernel
+++ b/core/SConscript.kernel
@@ -16,6 +16,7 @@ PYOPT = ARGUMENTS.get('PYOPT', '1')
DISABLE_OPTIGA = ARGUMENTS.get('DISABLE_OPTIGA', '0') == '1'
HW_REVISION = ARGUMENTS.get('HW_REVISION', None)
THP = ARGUMENTS.get('THP', '0') == '1' # Trezor-Host Protocol
+DBG_CONSOLE = ARGUMENTS.get('DBG_CONSOLE', '')
STORAGE_INSECURE_TESTING_MODE = ARGUMENTS.get('STORAGE_INSECURE_TESTING_MODE', '0') == '1'
if STORAGE_INSECURE_TESTING_MODE and PRODUCTION:
@@ -24,6 +25,15 @@ if STORAGE_INSECURE_TESTING_MODE:
DISABLE_OPTIGA = True
PYOPT = "0"
+CCFLAGS_MOD = ''
+CPPPATH_MOD = []
+CPPDEFINES_MOD = []
+SOURCE_MOD = []
+SOURCE_MOD_CRYPTO = []
+CPPDEFINES_HAL = []
+SOURCE_HAL = []
+PATH_HAL = []
+
FEATURE_FLAGS = {
"RDI": True,
"SECP256K1_ZKP": True, # required for trezor.crypto.curve.bip340 (BIP340/Taproot)
@@ -59,10 +69,10 @@ if BITCOIN_ONLY == '0':
]
if PYOPT == '0':
- FEATURES_WANTED += [
- "usb_iface_debug",
- "usb_iface_vcp",
- ]
+ DBG_CONSOLE = DBG_CONSOLE or "VCP"
+
+if DBG_CONSOLE != "":
+ FEATURES_WANTED += ["dbg_console"]
if not TREZOR_MODEL in ['T3W1', 'D002']:
FEATURES_WANTED += ["secure_mode"]
@@ -73,15 +83,6 @@ if DISABLE_OPTIGA:
raise RuntimeError("DISABLE_OPTIGA requires non-production build")
FEATURES_WANTED.remove("optiga")
-CCFLAGS_MOD = ''
-CPPPATH_MOD = []
-CPPDEFINES_MOD = []
-SOURCE_MOD = []
-SOURCE_MOD_CRYPTO = []
-CPPDEFINES_HAL = []
-SOURCE_HAL = []
-PATH_HAL = []
-
FROZEN = True
# modtrezorcrypto
@@ -251,6 +252,7 @@ env = Environment(
CPPDEFINES_IMPLICIT=[],
CPPDEFPREFIX="-D'",
CPPDEFSUFFIX="'",
+ DBG_CONSOLE=DBG_CONSOLE,
)
FEATURES_AVAILABLE = models.configure_board(TREZOR_MODEL, HW_REVISION, FEATURES_WANTED, env, CPPDEFINES_HAL, SOURCE_HAL, PATH_HAL)
diff --git a/core/SConscript.prodtest b/core/SConscript.prodtest
index f7c4a8ee..6f6f59a8 100644
--- a/core/SConscript.prodtest
+++ b/core/SConscript.prodtest
@@ -9,6 +9,7 @@ PRODUCTION = ARGUMENTS.get('PRODUCTION', '0') == '1'
BOOTLOADER_DEVEL = ARGUMENTS.get('BOOTLOADER_DEVEL', '0') == '1'
HW_REVISION = ARGUMENTS.get('HW_REVISION', None)
UI_PERFORMANCE_OVERLAY = ARGUMENTS.get('UI_PERFORMANCE_OVERLAY', '0') == '1'
+DBG_CONSOLE = ARGUMENTS.get('DBG_CONSOLE', '')
FEATURE_FLAGS = {
"AES_GCM": True,
@@ -58,12 +59,16 @@ SOURCE_HAL = []
PATH_HAL = []
RUST_UI_FEATURES = []
+if DBG_CONSOLE != "":
+ FEATURES_WANTED += ["dbg_console"]
+
env = Environment(
ENV=os.environ,
CFLAGS='%s -DPRODUCTION=%s' % (ARGUMENTS.get('CFLAGS', ''), ARGUMENTS.get('PRODUCTION', '0')),
CPPDEFINES_IMPLICIT=[],
CPPDEFPREFIX="-D'",
CPPDEFSUFFIX="'",
+ DBG_CONSOLE=DBG_CONSOLE,
)
FEATURES_AVAILABLE = models.configure_board(TREZOR_MODEL, HW_REVISION, FEATURES_WANTED, env, CPPDEFINES_HAL, SOURCE_HAL, PATH_HAL)
diff --git a/core/SConscript.secmon b/core/SConscript.secmon
index 49d06835..5f642ff4 100644
--- a/core/SConscript.secmon
+++ b/core/SConscript.secmon
@@ -16,6 +16,7 @@ PYOPT = ARGUMENTS.get('PYOPT', '1')
DISABLE_OPTIGA = ARGUMENTS.get('DISABLE_OPTIGA', '0') == '1'
HW_REVISION = ARGUMENTS.get('HW_REVISION', None)
THP = ARGUMENTS.get('THP', '0') == '1' # Trezor-Host Protocol
+DBG_CONSOLE = ARGUMENTS.get('DBG_CONSOLE', '')
STORAGE_INSECURE_TESTING_MODE = ARGUMENTS.get('STORAGE_INSECURE_TESTING_MODE', '0') == '1'
if STORAGE_INSECURE_TESTING_MODE and PRODUCTION:
@@ -58,6 +59,10 @@ CPPDEFINES_HAL = []
SOURCE_HAL = []
PATH_HAL = []
+
+if DBG_CONSOLE != "":
+ FEATURES_WANTED += ["dbg_console"]
+
FROZEN = True
env = Environment(
@@ -66,6 +71,7 @@ env = Environment(
CPPDEFINES_IMPLICIT=[],
CPPDEFPREFIX="-D'",
CPPDEFSUFFIX="'",
+ DBG_CONSOLE=DBG_CONSOLE,
)
FEATURES_AVAILABLE = models.configure_board(TREZOR_MODEL, HW_REVISION, FEATURES_WANTED, env, CPPDEFINES_HAL, SOURCE_HAL, PATH_HAL)
Why this scored 20/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.