feat(core/rust): add trezorhal bindings

pull/2332/head
Jan Pochyla 2 years ago committed by matejcik
parent 1e9acf10fa
commit c3b82fd651

@ -21,6 +21,7 @@
#define __DISPLAY_H__
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#if defined TREZOR_MODEL_T

@ -4,9 +4,9 @@ version = 3
[[package]]
name = "bindgen"
version = "0.59.1"
version = "0.60.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "453c49e5950bb0eb63bb3df640e31618846c89d5b7faa54040d76e98e0134375"
checksum = "062dddbc1ba4aca46de6338e2bf87771414c335f7b2f2036e8f3e9befebf88e6"
dependencies = [
"bitflags",
"cexpr",
@ -27,18 +27,6 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitvec"
version = "0.19.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8942c8d352ae1838c9dda0b0ca2ab657696ef2232a20147cf1b30ae1a9cb4321"
dependencies = [
"funty",
"radium",
"tap",
"wyz",
]
[[package]]
name = "byteorder"
version = "1.4.3"
@ -53,9 +41,9 @@ checksum = "d26a6ce4b6a484fa3edb70f7efa6fc430fd2b87285fe8b84304fd0936faa0dc0"
[[package]]
name = "cexpr"
version = "0.5.0"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db507a7679252d2276ed0dd8113c6875ec56d3089f9225b2b42c30cc1f8e5c89"
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
dependencies = [
"nom",
]
@ -93,12 +81,6 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35"
[[package]]
name = "funty"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7"
[[package]]
name = "glob"
version = "0.3.0"
@ -168,16 +150,20 @@ version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "nom"
version = "6.1.2"
version = "7.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7413f999671bd4745a7b624bd370a569fb6bc574b23c83a3c5ed2e453f3d5e2"
checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36"
dependencies = [
"bitvec",
"funty",
"memchr",
"version_check",
"minimal-lexical",
]
[[package]]
@ -204,12 +190,6 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "radium"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8"
[[package]]
name = "regex"
version = "1.5.6"
@ -258,12 +238,6 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "tap"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]]
name = "trezor_lib"
version = "0.1.0"
@ -282,12 +256,6 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "version_check"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
[[package]]
name = "winapi"
version = "0.3.9"
@ -309,9 +277,3 @@ name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "wyz"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214"

@ -50,7 +50,7 @@ default_features = false
# Build dependencies
[build-dependencies.bindgen]
version = "0.59.1"
version = "0.60.1"
default_features = false
features = ["runtime"]

@ -5,10 +5,18 @@ use std::{env, path::PathBuf, process::Command};
fn main() {
generate_qstr_bindings();
generate_micropython_bindings();
generate_trezorhal_bindings();
#[cfg(feature = "test")]
link_core_objects();
}
fn model() -> String {
match env::var("TREZOR_MODEL") {
Ok(model) => model,
Err(_) => String::from("T"),
}
}
/// Generates Rust module that exports QSTR constants used in firmware.
fn generate_qstr_bindings() {
let out_path = env::var("OUT_DIR").unwrap();
@ -42,13 +50,80 @@ fn generate_qstr_bindings() {
.unwrap();
}
fn prepare_bindings() -> bindgen::Builder {
let mut bindings = bindgen::Builder::default();
// Common include paths and defines
bindings = bindings.clang_args([
"-I../../../crypto",
"-I../../../storage",
"-I../../vendor/micropython",
"-I../extmod/modtrezorui", // for display.h
format!("-DTREZOR_MODEL_{}", model()).as_str(),
]);
// Pass in correct include paths and defines.
if is_firmware() {
bindings = bindings.clang_args(&[
"-nostdinc",
"-I../firmware",
"-I../trezorhal",
"-I../../build/firmware",
"-I../../vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Inc",
"-I../../vendor/micropython/lib/stm32lib/CMSIS/STM32F4xx/Include",
"-I../../vendor/micropython/lib/cmsis/inc",
"-DSTM32F427xx",
"-DUSE_HAL_DRIVER",
"-DSTM32_HAL_H=<stm32f4xx.h>",
]);
// Append gcc-arm-none-eabi's include paths.
let cc_output = Command::new("arm-none-eabi-gcc")
.arg("-E")
.arg("-Wp,-v")
.arg("-")
.output()
.expect("arm-none-eabi-gcc failed to execute");
if !cc_output.status.success() {
panic!("arm-none-eabi-gcc failed");
}
let include_paths =
String::from_utf8(cc_output.stderr).expect("arm-none-eabi-gcc returned invalid output");
let include_args = include_paths
.lines()
.skip_while(|s| !s.contains("search starts here:"))
.take_while(|s| !s.contains("End of search list."))
.filter(|s| s.starts_with(' '))
.map(|s| format!("-I{}", s.trim()));
bindings = bindings.clang_args(include_args);
} else {
bindings = bindings.clang_args(&[
"-I../unix",
"-I../../build/unix",
"-I../../vendor/micropython/ports/unix",
]);
}
bindings
// Customize the standard types.
.use_core()
.ctypes_prefix("cty")
.size_t_is_usize(true)
// Disable the layout tests. They spew out a lot of code-style bindings, and are not too
// relevant for our use-case.
.layout_tests(false)
// Tell cargo to invalidate the built crate whenever any of the
// included header files change.
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
}
fn generate_micropython_bindings() {
let out_path = env::var("OUT_DIR").unwrap();
// Tell cargo to invalidate the built crate whenever the header changes.
println!("cargo:rerun-if-changed=micropython.h");
let mut bindings = bindgen::Builder::default()
let bindings = prepare_bindings()
.header("micropython.h")
// obj
.new_type_alias("mp_obj_t")
@ -121,74 +196,86 @@ fn generate_micropython_bindings() {
.allowlist_var("mp_type_type")
// module
.allowlist_type("mp_obj_module_t")
.allowlist_var("mp_type_module");
.allowlist_var("mp_type_module")
// `ffi::mp_map_t` type is not allowed to be `Clone` or `Copy` because we tie it
// to the data lifetimes with the `MapRef` type, see `src/micropython/map.rs`.
// TODO: We should disable `Clone` and `Copy` for all types and only allow-list
// the specific cases we require.
.no_copy("_mp_map_t");
// `ffi::mp_map_t` type is not allowed to be `Clone` or `Copy` because we tie it
// to the data lifetimes with the `MapRef` type, see `src/micropython/map.rs`.
// TODO: We should disable `Clone` and `Copy` for all types and only allow-list
// the specific cases we require.
bindings = bindings.no_copy("_mp_map_t");
// Write the bindings to a file in the OUR_DIR.
bindings
.generate()
.expect("Unable to generate bindings")
.write_to_file(PathBuf::from(out_path).join("micropython.rs"))
.unwrap();
}
// Pass in correct include paths and defines.
if is_firmware() {
bindings = bindings.clang_args(&[
"-nostdinc",
"-I../firmware",
"-I../trezorhal",
"-I../../build/firmware",
"-I../../vendor/micropython",
"-I../../vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Inc",
"-I../../vendor/micropython/lib/stm32lib/CMSIS/STM32F4xx/Include",
"-I../../vendor/micropython/lib/cmsis/inc",
"-DTREZOR_MODEL_T",
"-DSTM32F405xx",
"-DUSE_HAL_DRIVER",
"-DSTM32_HAL_H=<stm32f4xx.h>",
]);
// Append gcc-arm-none-eabi's include paths.
let cc_output = Command::new("arm-none-eabi-gcc")
.arg("-E")
.arg("-Wp,-v")
.arg("-")
.output()
.expect("arm-none-eabi-gcc failed to execute");
if !cc_output.status.success() {
panic!("arm-none-eabi-gcc failed");
}
let include_paths =
String::from_utf8(cc_output.stderr).expect("arm-none-eabi-gcc returned invalid output");
let include_args = include_paths
.lines()
.skip_while(|s| !s.contains("search starts here:"))
.take_while(|s| !s.contains("End of search list."))
.filter(|s| s.starts_with(' '))
.map(|s| format!("-I{}", s.trim()));
fn generate_trezorhal_bindings() {
let out_path = env::var("OUT_DIR").unwrap();
bindings = bindings.clang_args(include_args);
} else {
bindings = bindings.clang_args(&[
"-I../unix",
"-I../../build/unix",
"-I../../vendor/micropython",
"-I../../vendor/micropython/ports/unix",
]);
}
// Tell cargo to invalidate the built crate whenever the header changes.
println!("cargo:rerun-if-changed=trezorhal.h");
let bindings = prepare_bindings()
.header("trezorhal.h")
// secbool
.allowlist_type("secbool")
.must_use_type("secbool")
.allowlist_var("sectrue")
.allowlist_var("secfalse")
// storage
.allowlist_var("EXTERNAL_SALT_SIZE")
.allowlist_var("FLAG_PUBLIC")
.allowlist_var("FLAGS_WRITE")
.allowlist_var("MAX_APPID")
.allowlist_type("PIN_UI_WAIT_CALLBACK")
.allowlist_function("storage_init")
.allowlist_function("storage_wipe")
.allowlist_function("storage_is_unlocked")
.allowlist_function("storage_lock")
.allowlist_function("storage_unlock")
.allowlist_function("storage_has_pin")
.allowlist_function("storage_get_pin_rem")
.allowlist_function("storage_change_pin")
.allowlist_function("storage_has")
.allowlist_function("storage_get")
.allowlist_function("storage_set")
.allowlist_function("storage_delete")
.allowlist_function("storage_set_counter")
.allowlist_function("storage_next_counter")
// display
.allowlist_function("display_init")
.allowlist_function("display_refresh")
.allowlist_function("display_backlight")
.allowlist_function("display_text")
.allowlist_function("display_text_width")
.allowlist_function("display_text_height")
.allowlist_function("display_bar")
.allowlist_function("display_bar_radius")
.allowlist_function("display_icon")
.allowlist_function("display_toif_info")
.allowlist_function("display_loader")
.allowlist_function("display_pixeldata")
.allowlist_function("display_pixeldata_dirty")
.allowlist_function("display_set_window")
.allowlist_var("DISPLAY_CMD_ADDRESS")
.allowlist_var("DISPLAY_DATA_ADDRESS")
// bip39
.allowlist_function("mnemonic_word_completion_mask")
.allowlist_var("BIP39_WORDLIST_ENGLISH")
.allowlist_var("BIP39_WORD_COUNT")
// slip39
.allowlist_function("slip39_word_completion_mask")
.allowlist_function("button_sequence_to_word")
// random
.allowlist_function("random_uniform");
// Write the bindings to a file in the OUR_DIR.
bindings
// Customize the standard types.
.use_core()
.ctypes_prefix("cty")
.size_t_is_usize(true)
// Disable the layout tests. They spew out a lot of code-style bindings, and are not too
// relevant for our use-case.
.layout_tests(false)
// Tell cargo to invalidate the built crate whenever any of the
// included header files change.
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
// Write the bindings to a file in the OUR_DIR.
.generate()
.expect("Unable to generate Rust Micropython bindings")
.write_to_file(PathBuf::from(out_path).join("micropython.rs"))
.expect("Unable to generate bindings")
.write_to_file(PathBuf::from(out_path).join("trezorhal.rs"))
.unwrap();
}

@ -1,15 +1,7 @@
use super::ffi;
use core::cmp::Ordering;
use cstr_core::CStr;
// TODO: expose from trezor-crypto via build.rs
const BIP39_WORD_COUNT: usize = 2048;
extern "C" {
// trezor-crypto/bip39.h
fn mnemonic_word_completion_mask(prefix: *const cty::c_char, len: cty::c_int) -> u32;
pub static BIP39_WORDLIST_ENGLISH: [*const cty::c_char; BIP39_WORD_COUNT];
}
unsafe fn from_utf8_unchecked<'a>(word: *const cty::c_char) -> &'a str {
// SAFETY: caller must pass a valid 0-terminated UTF-8 string.
// This assumption holds for usage on words of the BIP-39 wordlist.
@ -49,14 +41,14 @@ pub fn complete_word(prefix: &str) -> Option<&'static str> {
pub fn word_completion_mask(prefix: &str) -> u32 {
// SAFETY: `mnemonic_word_completion_mask` shouldn't retain nor modify the
// passed byte string, making the call safe.
unsafe { mnemonic_word_completion_mask(prefix.as_ptr() as _, prefix.len() as _) }
unsafe { ffi::mnemonic_word_completion_mask(prefix.as_ptr() as _, prefix.len() as _) }
}
pub struct Wordlist(&'static [*const cty::c_char]);
impl Wordlist {
pub fn all() -> Self {
Self(unsafe { &BIP39_WORDLIST_ENGLISH })
Self(unsafe { &ffi::BIP39_WORDLIST_ENGLISH })
}
pub const fn empty() -> Self {
@ -105,6 +97,8 @@ impl Wordlist {
mod tests {
use super::*;
const BIP39_WORD_COUNT: usize = ffi::BIP39_WORD_COUNT as usize;
#[test]
fn test_prefix_cmp() {
assert_eq!(unsafe { prefix_cmp("", "".as_ptr() as _) }, Ordering::Equal);

@ -1,62 +1,6 @@
use super::ffi;
use core::ptr;
extern "C" {
// trezorhal/display.c
fn display_backlight(val: cty::c_int) -> cty::c_int;
fn display_text(
x: cty::c_int,
y: cty::c_int,
text: *const cty::c_char,
textlen: cty::c_int,
font: cty::c_int,
fgcolor: cty::uint16_t,
bgcolor: cty::uint16_t,
);
fn display_text_width(
text: *const cty::c_char,
textlen: cty::c_int,
font: cty::c_int,
) -> cty::c_int;
fn display_text_height(font: cty::c_int) -> cty::c_int;
fn display_bar(x: cty::c_int, y: cty::c_int, w: cty::c_int, h: cty::c_int, c: cty::uint16_t);
fn display_bar_radius(
x: cty::c_int,
y: cty::c_int,
w: cty::c_int,
h: cty::c_int,
c: cty::uint16_t,
b: cty::uint16_t,
r: cty::uint8_t,
);
fn display_icon(
x: cty::c_int,
y: cty::c_int,
w: cty::c_int,
h: cty::c_int,
data: *const cty::c_void,
len: cty::uint32_t,
fgcolor: cty::uint16_t,
bgcolor: cty::uint16_t,
);
fn display_toif_info(
data: *const cty::uint8_t,
len: cty::uint32_t,
out_w: *mut cty::uint16_t,
out_h: *mut cty::uint16_t,
out_grayscale: *mut bool,
) -> bool;
fn display_loader(
progress: cty::uint16_t,
indeterminate: bool,
yoffset: cty::c_int,
fgcolor: cty::uint16_t,
bgcolor: cty::uint16_t,
icon: *const cty::uint8_t,
iconlen: cty::uint32_t,
iconfgcolor: cty::uint16_t,
);
}
pub struct ToifInfo {
pub width: u16,
pub height: u16,
@ -64,12 +8,12 @@ pub struct ToifInfo {
}
pub fn backlight(val: i32) -> i32 {
unsafe { display_backlight(val) }
unsafe { ffi::display_backlight(val) }
}
pub fn text(baseline_x: i32, baseline_y: i32, text: &str, font: i32, fgcolor: u16, bgcolor: u16) {
unsafe {
display_text(
ffi::display_text(
baseline_x,
baseline_y,
text.as_ptr() as _,
@ -82,7 +26,7 @@ pub fn text(baseline_x: i32, baseline_y: i32, text: &str, font: i32, fgcolor: u1
}
pub fn text_width(text: &str, font: i32) -> i32 {
unsafe { display_text_width(text.as_ptr() as _, text.len() as _, font) }
unsafe { ffi::display_text_width(text.as_ptr() as _, text.len() as _, font) }
}
pub fn char_width(ch: char, font: i32) -> i32 {
@ -92,20 +36,20 @@ pub fn char_width(ch: char, font: i32) -> i32 {
}
pub fn text_height(font: i32) -> i32 {
unsafe { display_text_height(font) }
unsafe { ffi::display_text_height(font) }
}
pub fn bar(x: i32, y: i32, w: i32, h: i32, fgcolor: u16) {
unsafe { display_bar(x, y, w, h, fgcolor) }
unsafe { ffi::display_bar(x, y, w, h, fgcolor) }
}
pub fn bar_radius(x: i32, y: i32, w: i32, h: i32, fgcolor: u16, bgcolor: u16, radius: u8) {
unsafe { display_bar_radius(x, y, w, h, fgcolor, bgcolor, radius) }
unsafe { ffi::display_bar_radius(x, y, w, h, fgcolor, bgcolor, radius) }
}
pub fn icon(x: i32, y: i32, w: i32, h: i32, data: &[u8], fgcolor: u16, bgcolor: u16) {
unsafe {
display_icon(
ffi::display_icon(
x,
y,
w,
@ -123,7 +67,7 @@ pub fn toif_info(data: &[u8]) -> Result<ToifInfo, ()> {
let mut height: cty::uint16_t = 0;
let mut grayscale: bool = false;
if unsafe {
display_toif_info(
ffi::display_toif_info(
data.as_ptr() as _,
data.len() as _,
&mut width,
@ -151,7 +95,7 @@ pub fn loader(
iconfgcolor: u16,
) {
unsafe {
display_loader(
ffi::display_loader(
progress,
indeterminate,
yoffset,

@ -0,0 +1,5 @@
#![allow(non_camel_case_types)]
#![allow(non_upper_case_globals)]
#![allow(dead_code)]
include!(concat!(env!("OUT_DIR"), "/trezorhal.rs"));

@ -2,5 +2,6 @@ pub mod bip39;
pub mod common;
#[cfg(feature = "ui")]
pub mod display;
mod ffi;
pub mod random;
pub mod slip39;

@ -1,10 +1,5 @@
extern "C" {
// trezor-crypto/rand.h
fn random_uniform(n: u32) -> u32;
}
pub fn uniform(n: u32) -> u32 {
unsafe { random_uniform(n) }
unsafe { super::ffi::random_uniform(n) }
}
pub fn shuffle<T>(slice: &mut [T]) {

@ -1,12 +1,6 @@
use cstr_core::CStr;
mod ffi {
extern "C" {
// trezor-crypto/slip39.h
pub fn slip39_word_completion_mask(prefix: u16) -> u16;
pub fn button_sequence_to_word(sequence: u16) -> *const cty::c_char;
}
}
use super::ffi;
/// Calculates which buttons still can be pressed after some already were.
/// Returns a 9-bit bitmask, where each bit specifies which buttons

@ -0,0 +1,7 @@
#include "display.h"
#include "secbool.h"
#include "storage.h"
#include "bip39.h"
#include "rand.h"
#include "slip39.h"
Loading…
Cancel
Save