What changed, and why it matters
This commit adds a new optional 'headless' display driver for Trezor hardware that has no screen. It does not change any existing behavior for real devices; it simply lets the firmware compile and run on display-less development boards by discarding any drawing commands. There is no security issue here.
No security action required. Treat as normal feature code review.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch introduces a display_none Cargo feature and a matching display/none/display_driver.c stub. The stub implements the io/display.h API with no-op or default-return behavior: initialization succeeds, backlight/orientation state is tracked in memory, framebuffer requests return false, and all bitblt/drawing operations are empty. It is gated on KERNEL_MODE to avoid duplicate symbols in firmware builds. This is purely additive infrastructure for boards without a display panel.
Changed components
core/embed/io/Cargo.tomlcore/embed/io/display/build.rscore/embed/io/display/none/display_driver.cInspect captured patch +152 / −0
diff --git a/core/embed/io/Cargo.toml b/core/embed/io/Cargo.toml
index da425ed3..bb29f041 100644
--- a/core/embed/io/Cargo.toml
+++ b/core/embed/io/Cargo.toml
@@ -111,6 +111,7 @@ framebuffer = []
# to also advertise `framebuffer` in its project.toml. display_st7789 supports
# both modes — the model.toml decides.
# The emulator's unix driver is selected by display/build.rs (not a feature).
+display_none = []
display_ltdc_dsi = ["framebuffer"]
display_st7789 = []
display_vg2864 = ["framebuffer"]
diff --git a/core/embed/io/display/build.rs b/core/embed/io/display/build.rs
index 1a1911e1..4794085d 100644
--- a/core/embed/io/display/build.rs
+++ b/core/embed/io/display/build.rs
@@ -23,6 +23,8 @@ pub fn def_module(lib: &mut CLibrary) -> Result<()> {
// The emulator reuses the emulated board's display configuration (panel,
// framebuffer) but always builds the unix driver instead of the HW one.
add_driver_unix(lib)?;
+ } else if cfg!(feature = "display_none") {
+ add_driver_none(lib)?;
} else if cfg!(feature = "display_ltdc_dsi") {
add_driver_ltdc_dsi(lib)?;
} else if cfg!(feature = "display_st7789") {
@@ -110,6 +112,18 @@ fn add_driver_unix(lib: &mut CLibrary) -> Result<()> {
Ok(())
}
+// Headless driver for boards without a display. Sets nominal resolution
+// defines so dependent code compiles; performs no drawing at runtime.
+fn add_driver_none(lib: &mut CLibrary) -> Result<()> {
+ lib.add_defines([
+ ("USE_RGB_COLORS", Some("1")),
+ ("DISPLAY_RESX", Some("240")),
+ ("DISPLAY_RESY", Some("240")),
+ ]);
+ lib.add_source("display/none/display_driver.c");
+ Ok(())
+}
+
fn add_driver_ltdc_dsi(lib: &mut CLibrary) -> Result<()> {
if cfg!(feature = "mcu_stm32u5g") {
lib.add_sources([
diff --git a/core/embed/io/display/none/display_driver.c b/core/embed/io/display/none/display_driver.c
new file mode 100644
index 00000000..4493c5d8
--- /dev/null
+++ b/core/embed/io/display/none/display_driver.c
@@ -0,0 +1,137 @@
+/*
+ * 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/>.
+ */
+
+// Headless ("none") display driver.
+//
+// Used by boards that have no display hardware (e.g. bare development
+// boards). It implements the full `io/display.h` interface but performs no
+// drawing: all bitblt operations are discarded and there is no framebuffer.
+// This lets the boot chain (boardloader/bootloader) link and run on
+// display-less hardware while the UI code keeps working (it simply renders to
+// nowhere).
+
+#include <trezor_rtl.h>
+
+#include <io/display.h>
+
+// All display entry points live in the kernel; the unprivileged side reaches
+// them through syscall stubs (sys/syscall/.../syscall_stubs.c). Gating the
+// whole driver on KERNEL_MODE avoids duplicate definitions in firmware builds.
+#ifdef KERNEL_MODE
+
+typedef struct {
+ // Set if the driver is initialized
+ bool initialized;
+ // Current display orientation (always 0)
+ int orientation_angle;
+ // Current backlight level ranging from 0 to 255
+ uint8_t backlight_level;
+} display_driver_t;
+
+static display_driver_t g_display_driver = {
+ .initialized = false,
+};
+
+bool display_init(display_content_mode_t mode) {
+ display_driver_t *drv = &g_display_driver;
+
+ if (drv->initialized) {
+ return true;
+ }
+
+ drv->backlight_level = 0;
+ drv->orientation_angle = 0;
+ drv->initialized = true;
+ return true;
+}
+
+void display_deinit(display_content_mode_t mode) {
+ display_driver_t *drv = &g_display_driver;
+ drv->initialized = false;
+}
+
+void display_set_unpriv_access(bool unpriv) {}
+
+bool display_set_backlight(uint8_t level) {
+ display_driver_t *drv = &g_display_driver;
+
+ if (!drv->initialized) {
+ return false;
+ }
+
+ drv->backlight_level = level;
+ return true;
+}
+
+uint8_t display_get_backlight(void) {
+ display_driver_t *drv = &g_display_driver;
+
+ if (!drv->initialized) {
+ return 0;
+ }
+
+ return drv->backlight_level;
+}
+
+int display_set_orientation(int angle) {
+ display_driver_t *drv = &g_display_driver;
+
+ if (!drv->initialized) {
+ return 0;
+ }
+
+ // The headless driver only supports the default orientation.
+ if (angle == 0) {
+ drv->orientation_angle = angle;
+ }
+ return drv->orientation_angle;
+}
+
+int display_get_orientation(void) {
+ display_driver_t *drv = &g_display_driver;
+
+ if (!drv->initialized) {
+ return 0;
+ }
+
+ return drv->orientation_angle;
+}
+
+#ifdef FRAMEBUFFER
+
+bool display_get_frame_buffer(display_fb_info_t *fb) {
+ memset(fb, 0, sizeof(display_fb_info_t));
+ return false;
+}
+
+#else // FRAMEBUFFER
+
+void display_wait_for_sync(void) {}
+
+#endif
+
+void display_refresh(void) {}
+
+void display_fill(const gfx_bitblt_t *bb) {}
+
+void display_copy_rgb565(const gfx_bitblt_t *bb) {}
+
+void display_copy_mono1p(const gfx_bitblt_t *bb) {}
+
+#endif // KERNEL_MODE
Why this scored 15/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.