feat(core/rust): make Rust buildable without Micropython

Features `micropython` and `protobuf` are defined. Protobuf implies micropython
because our protobuf impl is pretty much _for_ micropython.

The respective subdirs are included only if the matching feature is defined.

util.rs is moved to micropython because it mostly concerns micropython interop

ResultExt, useful only for ui_debug, is moved to ui::util.

A new module `trezorhal::time` is provided. It mirrors functionality of
`micropython::time` via stmlib functions.  The intended use is to always use
functions from `trezorhal::time`. The right micropython variants are used when
micropython is available, otherwise the pure stmlib versions are called.

ui::*::layout is conditional for micropython feature, because it only concerns
micropython layouts. If we want to reuse layouts defined there, we will need to
export them to not depend on Objs and Qstrs etc.
pull/2342/head
matejcik 2 years ago committed by matejcik
parent 7d37109eb8
commit 2aa427a6e3

@ -83,7 +83,7 @@ test: ## run unit tests
cd tests ; ./run_tests.sh $(TESTOPTS)
test_rust: ## run rs unit tests
cd embed/rust ; cargo test --no-default-features --features model_t$(shell echo $(TREZOR_MODEL) | tr "T" "t"),test,ui,ui_debug -- --test-threads=1
cd embed/rust ; cargo test --no-default-features --features model_t$(shell echo $(TREZOR_MODEL) | tr "TR" "tr"),test -- --test-threads=1
test_emu: ## run selected device tests from python-trezor
$(EMU_TEST) $(PYTEST) $(TESTPATH)/device_tests $(TESTOPTS)

@ -704,18 +704,14 @@ def cargo_build():
profile = '--release'
else:
profile = ''
if TREZOR_MODEL in ("1",):
features = ["model_t1"]
elif TREZOR_MODEL in ("R",):
features = ["model_tr"]
else:
features = ["model_tt"]
if BITCOIN_ONLY == "1":
features.append("bitcoin_only")
features = ['micropython', 'protobuf', f'model_t{TREZOR_MODEL.lower()}']
if BITCOIN_ONLY == '1':
features.append('bitcoin_only')
if UI2:
features.append("ui")
if PYOPT == "0":
features.append("ui_debug")
features.append('ui')
if PYOPT == '0':
features.append('ui_debug')
return f'cd embed/rust; cargo build {profile} --target={RUST_TARGET} --target-dir=../../build/firmware/rust --no-default-features --features "{" ".join(features)}"'

@ -665,18 +665,13 @@ RUST_LIB = 'trezor_lib'
RUST_LIBPATH = f'{RUST_LIBDIR}/lib{RUST_LIB}.a'
def cargo_build():
if TREZOR_MODEL in ('1',):
features = ["model_t1"]
elif TREZOR_MODEL in ('R',):
features = ["model_tr"]
else:
features = ["model_tt"]
if BITCOIN_ONLY == "1":
features.append("bitcoin_only")
features = ['micropython', 'protobuf', f'model_t{TREZOR_MODEL.lower()}']
if BITCOIN_ONLY == '1':
features.append('bitcoin_only')
if UI2:
features.append("ui")
if PYOPT == "0" or not FROZEN:
features.append("ui_debug")
features.append('ui')
if PYOPT == '0':
features.append('ui_debug')
return f'cd embed/rust; cargo build --profile {RUST_PROFILE} --target-dir=../../build/unix/rust --no-default-features --features "{" ".join(features)}"'

@ -11,12 +11,14 @@ bitcoin_only = []
model_tt = ["touch"]
model_t1 = ["buttons"]
model_tr = ["buttons"]
micropython = []
protobuf = ["micropython"]
ui = []
ui_debug = []
buttons = []
touch = []
clippy = []
test = ["cc", "glob"]
test = ["cc", "glob", "micropython", "protobuf", "ui", "ui_debug"]
[lib]
crate-type = ["staticlib"]

@ -3,7 +3,9 @@ use std::ffi::OsStr;
use std::{env, path::PathBuf, process::Command};
fn main() {
#[cfg(feature = "micropython")]
generate_qstr_bindings();
#[cfg(feature = "micropython")]
generate_micropython_bindings();
generate_trezorhal_bindings();
#[cfg(feature = "test")]
@ -18,6 +20,7 @@ fn model() -> String {
}
/// Generates Rust module that exports QSTR constants used in firmware.
#[cfg(feature = "micropython")]
fn generate_qstr_bindings() {
let out_path = env::var("OUT_DIR").unwrap();
@ -117,6 +120,7 @@ fn prepare_bindings() -> bindgen::Builder {
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
}
#[cfg(feature = "micropython")]
fn generate_micropython_bindings() {
let out_path = env::var("OUT_DIR").unwrap();
@ -269,7 +273,10 @@ fn generate_trezorhal_bindings() {
.allowlist_function("slip39_word_completion_mask")
.allowlist_function("button_sequence_to_word")
// random
.allowlist_function("random_uniform");
.allowlist_function("random_uniform")
// time
.allowlist_function("hal_delay")
.allowlist_function("hal_ticks_ms");
// Write the bindings to a file in the OUR_DIR.
bindings

@ -1,7 +1,11 @@
use core::convert::{Infallible, TryInto};
use core::{
convert::{Infallible, TryInto},
num::TryFromIntError,
};
use cstr_core::CStr;
#[cfg(feature = "micropython")]
use crate::micropython::{ffi, obj::Obj, qstr::Qstr};
#[allow(clippy::enum_variant_names)] // We mimic the Python exception classnames here.
@ -11,13 +15,19 @@ pub enum Error {
OutOfRange,
MissingKwargs,
AllocationFailed,
#[cfg(feature = "micropython")]
CaughtException(Obj),
#[cfg(feature = "micropython")]
KeyError(Obj),
#[cfg(feature = "micropython")]
AttributeError(Qstr),
#[cfg(feature = "micropython")]
ValueError(&'static CStr),
#[cfg(feature = "micropython")]
ValueErrorParam(&'static CStr, Obj),
}
#[cfg(feature = "micropython")]
impl Error {
/// Create an exception instance matching the error code. The result of this
/// call should only be used to immediately raise the exception, because the
@ -67,3 +77,9 @@ impl From<Infallible> for Error {
unreachable!()
}
}
impl From<TryFromIntError> for Error {
fn from(_: TryFromIntError) -> Self {
Self::OutOfRange
}
}

@ -5,8 +5,10 @@
#![allow(dead_code)]
mod error;
#[cfg(feature = "micropython")]
#[macro_use]
mod micropython;
#[cfg(feature = "protobuf")]
mod protobuf;
mod time;
#[cfg(feature = "ui_debug")]
@ -16,7 +18,6 @@ mod trezorhal;
#[cfg(feature = "ui")]
#[macro_use]
pub mod ui;
mod util;
#[cfg(not(test))]
#[cfg(any(not(feature = "test"), feature = "clippy"))]

@ -16,6 +16,7 @@ pub mod qstr;
pub mod runtime;
pub mod time;
pub mod typ;
pub mod util;
#[cfg(test)]
pub mod testutil;

@ -1,7 +1,4 @@
use core::{
convert::{TryFrom, TryInto},
num::TryFromIntError,
};
use core::convert::{TryFrom, TryInto};
use cstr_core::CStr;
@ -413,9 +410,3 @@ impl Obj {
}
}
}
impl From<TryFromIntError> for Error {
fn from(_: TryFromIntError) -> Self {
Self::OutOfRange
}
}

@ -81,16 +81,3 @@ pub unsafe fn try_with_args_and_kwargs_inline(
};
unsafe { try_or_raise(block) }
}
pub trait ResultExt {
fn assert_if_debugging_ui(self, message: &str);
}
impl<T, E> ResultExt for Result<T, E> {
fn assert_if_debugging_ui(self, #[allow(unused)] message: &str) {
#[cfg(feature = "ui_debug")]
if self.is_err() {
panic!("{}", message);
}
}
}

@ -5,8 +5,7 @@ use core::{
use crate::{
error::Error,
micropython::{buffer::Buffer, gc::Gc, list::List, map::Map, obj::Obj, qstr::Qstr},
util,
micropython::{buffer::Buffer, gc::Gc, list::List, map::Map, obj::Obj, qstr::Qstr, util},
};
use super::{

@ -9,8 +9,8 @@ use crate::{
list::List,
obj::Obj,
qstr::Qstr,
util,
},
util,
};
use super::{

@ -10,8 +10,8 @@ use crate::{
obj::{Obj, ObjBase},
qstr::Qstr,
typ::Type,
util,
},
util,
};
use super::{

@ -3,7 +3,7 @@ use core::{
ops::{Div, Mul},
};
use crate::micropython::time;
use crate::trezorhal::time;
const MILLIS_PER_SEC: u32 = 1000;
@ -83,7 +83,6 @@ pub struct Instant {
impl Instant {
pub fn now() -> Self {
// TODO: We should move this to `micropython::time`.
Self {
millis: time::ticks_ms(),
}

@ -5,3 +5,8 @@ pub mod display;
mod ffi;
pub mod random;
pub mod slip39;
#[cfg(not(feature = "micropython"))]
pub mod time;
#[cfg(feature = "micropython")]
pub use crate::micropython::time;

@ -0,0 +1,13 @@
use crate::time::Duration;
use super::ffi;
pub fn ticks_ms() -> u32 {
unsafe { ffi::hal_ticks_ms() as _ }
}
pub fn sleep(delay: Duration) {
unsafe {
ffi::hal_delay(delay.to_millis() as _);
}
}

@ -1,5 +1,8 @@
use super::constant;
use crate::{micropython::time, time::Duration, trezorhal::display};
use crate::{
time::Duration,
trezorhal::{display, time},
};
use super::geometry::{Offset, Point, Rect};

@ -11,6 +11,7 @@ use crate::{
obj::{Obj, ObjBase},
qstr::Qstr,
typ::Type,
util,
},
time::Duration,
ui::{
@ -18,7 +19,6 @@ use crate::{
constant,
geometry::Rect,
},
util,
};
#[cfg(feature = "buttons")]

@ -7,6 +7,9 @@ pub mod constant;
pub mod display;
pub mod event;
pub mod geometry;
mod util;
#[cfg(feature = "micropython")]
pub mod layout;
#[cfg(feature = "model_t1")]

@ -2,7 +2,7 @@ use core::convert::TryInto;
use crate::{
error::Error,
micropython::{buffer::StrBuffer, map::Map, module::Module, obj::Obj, qstr::Qstr},
micropython::{buffer::StrBuffer, map::Map, module::Module, obj::Obj, qstr::Qstr, util},
ui::{
component::{
base::Component,
@ -15,7 +15,6 @@ use crate::{
result::{CANCELLED, CONFIRMED},
},
},
util,
};
use super::{

@ -1,4 +1,6 @@
pub mod component;
pub mod constant;
pub mod layout;
pub mod theme;
#[cfg(feature = "micropython")]
pub mod layout;

@ -2,7 +2,7 @@ use core::convert::TryInto;
use crate::{
error::Error,
micropython::{buffer::StrBuffer, map::Map, module::Module, obj::Obj, qstr::Qstr},
micropython::{buffer::StrBuffer, map::Map, module::Module, obj::Obj, qstr::Qstr, util},
ui::{
component::{
base::Component,
@ -15,7 +15,6 @@ use crate::{
result::{CANCELLED, CONFIRMED},
},
},
util,
};
use super::{

@ -1,4 +1,6 @@
pub mod component;
pub mod constant;
pub mod layout;
pub mod theme;
#[cfg(feature = "micropython")]
pub mod layout;

@ -6,8 +6,8 @@ use crate::{
component::{Event, EventCtx, TimerToken},
display::{self, Color, Font},
geometry::{Offset, Point, Rect},
util::ResultExt,
},
util::ResultExt,
};
pub const HEADER_HEIGHT: i32 = 25;

@ -18,8 +18,8 @@ use crate::{
},
theme,
},
util::ResultExt,
},
util::ResultExt,
};
const MAX_LENGTH: usize = 8;

@ -2,7 +2,7 @@ use core::{convert::TryInto, ops::Deref};
use crate::{
error::Error,
micropython::{buffer::StrBuffer, map::Map, module::Module, obj::Obj, qstr::Qstr},
micropython::{buffer::StrBuffer, map::Map, module::Module, obj::Obj, qstr::Qstr, util},
ui::{
component::{
base::ComponentExt,
@ -15,7 +15,6 @@ use crate::{
result::{CANCELLED, CONFIRMED, INFO},
},
},
util,
};
use super::{

@ -1,5 +1,7 @@
pub mod component;
pub mod constant;
pub mod event;
pub mod layout;
pub mod theme;
#[cfg(feature = "micropython")]
pub mod layout;

@ -0,0 +1,12 @@
pub trait ResultExt {
fn assert_if_debugging_ui(self, message: &str);
}
impl<T, E> ResultExt for Result<T, E> {
fn assert_if_debugging_ui(self, #[allow(unused)] message: &str) {
#[cfg(feature = "ui_debug")]
if self.is_err() {
panic!("{}", message);
}
}
}

@ -1,3 +1,4 @@
#include "common.h"
#include "display.h"
#include "secbool.h"
#include "storage.h"

@ -134,6 +134,7 @@ void __assert_func(const char *file, int line, const char *func,
#endif
void hal_delay(uint32_t ms) { HAL_Delay(ms); }
uint32_t hal_ticks_ms() { return HAL_GetTick(); }
// reference RM0090 section 35.12.1 Figure 413
#define USB_OTG_HS_DATA_FIFO_RAM (USB_OTG_HS_PERIPH_BASE + 0x20000U)

@ -66,6 +66,7 @@ error_shutdown(const char *line1, const char *line2, const char *line3,
: __fatal_error(#expr, msg, __FILE__, __LINE__, __func__))
void hal_delay(uint32_t ms);
uint32_t hal_ticks_ms();
void clear_otg_hs_memory(void);

@ -20,6 +20,7 @@
#include <SDL.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <unistd.h>
#include "common.h"
@ -116,6 +117,12 @@ error_shutdown(const char *line1, const char *line2, const char *line3,
void hal_delay(uint32_t ms) { usleep(1000 * ms); }
uint32_t hal_ticks_ms() {
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec * 1000 + tv.tv_usec / 1000;
}
static int SDLCALL emulator_event_filter(void *userdata, SDL_Event *event) {
switch (event->type) {
case SDL_QUIT:

Loading…
Cancel
Save