#[cfg(feature = "test")] 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 = "crypto")] generate_crypto_bindings(); #[cfg(feature = "test")] link_core_objects(); } fn mcu_type() -> String { match env::var("MCU_TYPE") { Ok(mcu) => mcu, Err(_) => String::from("STM32F427xx"), } } fn model() -> String { match env::var("TREZOR_MODEL") { Ok(model) => model, Err(_) => String::from("T"), } } // fn block_words() -> String { // match env::var("FLASH_BLOCK_WORDS") { // Ok(model) => model, // Err(_) => panic!("FLASH_BLOCK_WORDS not set") // } // } fn board() -> String { if !is_firmware() { return String::from("boards/board-unix.h"); } match env::var("TREZOR_BOARD") { Ok(board) => { format!("boards/{}", board) } Err(_) => String::from("boards/trezor_t.h"), } } /// 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(); // Tell cargo to invalidate the built crate whenever the header changes. println!("cargo:rerun-if-changed=qstr.h"); let dest_file = PathBuf::from(out_path).join("qstr.rs"); bindgen::Builder::default() .header("qstr.h") // Build the Qstr enum as a newtype so we can define method on it. .default_enum_style(bindgen::EnumVariation::NewType { is_bitfield: false }) // Pass in correct include paths. .clang_args(&[ "-I", if is_firmware() { "../../build/firmware" } else { "../../build/unix" }, ]) // Customize the standard types. .use_core() .ctypes_prefix("cty") .size_t_is_usize(true) // Tell cargo to invalidate the built crate whenever any of the // included header files change. .parse_callbacks(Box::new(bindgen::CargoCallbacks)) .generate() .expect("Unable to generate Rust QSTR bindings") .write_to_file(&dest_file) .unwrap(); // rewrite the file to change internal representation of the qstr newtype let qstr_generated = std::fs::read_to_string(&dest_file).unwrap(); let qstr_modified = qstr_generated.replace( "pub struct Qstr(pub cty::c_uint);", "pub struct Qstr(pub usize);", ); assert_ne!(qstr_generated, qstr_modified, "Failed to rewrite type of Qstr in qstr.rs file.\nThis indicates that the generated file has changed. Please update the rewriting code."); std::fs::write(&dest_file, qstr_modified).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../../vendor/micropython/lib/uzlib", "-I../lib", "-I../trezorhal", "-I../models", format!("-D{}", mcu_type()).as_str(), format!("-DTREZOR_MODEL_{}", model()).as_str(), format!("-DTREZOR_BOARD=\"{}\"", board()).as_str(), ]); #[cfg(feature = "xframebuffer")] { bindings = bindings.clang_args(&["-DXFRAMEBUFFER"]); } #[cfg(feature = "new_rendering")] { bindings = bindings.clang_args(["-DNEW_RENDERING"]); } // Pass in correct include paths and defines. if is_firmware() { let mut clang_args: Vec<&str> = Vec::new(); let includes = env::var("RUST_INCLUDES").unwrap(); let args = includes.split(';'); for arg in args { clang_args.push(arg); } clang_args.push("-nostdinc"); clang_args.push("-I../firmware"); clang_args.push("-I../../build/firmware"); clang_args.push("-DUSE_HAL_DRIVER"); bindings = bindings.clang_args(&clang_args); // 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../trezorhal/unix", "-I../../build/unix", "-I../../vendor/micropython/ports/unix", "-DTREZOR_EMULATOR", "-DFLASH_BIT_ACCESS=1", "-DFLASH_BLOCK_WORDS=1", ]); } 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)) } #[cfg(feature = "micropython")] 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 bindings = prepare_bindings() .header("micropython.h") // obj .new_type_alias("mp_obj_t") .allowlist_type("mp_obj_type_t") .allowlist_type("mp_obj_base_t") .allowlist_function("mp_obj_new_int") .allowlist_function("mp_obj_new_int_from_ll") .allowlist_function("mp_obj_new_int_from_ull") .allowlist_function("mp_obj_new_int_from_uint") .allowlist_function("mp_obj_new_bytes") .allowlist_function("mp_obj_new_str") .allowlist_function("mp_obj_new_tuple") .allowlist_function("mp_obj_new_attrtuple") .allowlist_function("mp_obj_get_int_maybe") .allowlist_function("mp_obj_is_true") .allowlist_function("mp_call_function_n_kw") .allowlist_function("trezor_obj_get_ll_checked") .allowlist_function("trezor_obj_str_from_rom_text") // buffer .allowlist_function("mp_get_buffer") .allowlist_var("MP_BUFFER_READ") .allowlist_var("MP_BUFFER_WRITE") .allowlist_var("mp_type_str") .allowlist_var("mp_type_bytes") .allowlist_var("mp_type_bytearray") .allowlist_var("mp_type_memoryview") // dict .allowlist_type("mp_obj_dict_t") .allowlist_function("mp_obj_new_dict") .allowlist_var("mp_type_dict") // fun .allowlist_type("mp_obj_fun_builtin_fixed_t") .allowlist_var("mp_type_fun_builtin_0") .allowlist_var("mp_type_fun_builtin_1") .allowlist_var("mp_type_fun_builtin_2") .allowlist_var("mp_type_fun_builtin_3") .allowlist_type("mp_obj_fun_builtin_var_t") .allowlist_var("mp_type_fun_builtin_var") // gc .allowlist_function("gc_alloc") // iter .allowlist_type("mp_obj_iter_buf_t") .allowlist_function("mp_getiter") .allowlist_function("mp_iternext") // list .allowlist_type("mp_obj_list_t") .allowlist_function("mp_obj_new_list") .allowlist_function("mp_obj_list_append") .allowlist_function("mp_obj_list_get") .allowlist_function("mp_obj_list_set_len") .allowlist_var("mp_type_list") // map .allowlist_type("mp_map_elem_t") .allowlist_function("mp_map_init") .allowlist_function("mp_map_init_fixed_table") .allowlist_function("mp_map_lookup") // exceptions .allowlist_function("nlr_jump") .allowlist_function("mp_obj_new_exception") .allowlist_function("mp_obj_new_exception_args") .allowlist_function("trezor_obj_call_protected") .allowlist_var("mp_type_AttributeError") .allowlist_var("mp_type_EOFError") .allowlist_var("mp_type_IndexError") .allowlist_var("mp_type_KeyError") .allowlist_var("mp_type_MemoryError") .allowlist_var("mp_type_OverflowError") .allowlist_var("mp_type_ValueError") .allowlist_var("mp_type_TypeError") // time .allowlist_function("mp_hal_ticks_ms") .allowlist_function("mp_hal_delay_ms") // debug .allowlist_function("mp_print_strn") .allowlist_var("mp_plat_print") // typ .allowlist_var("mp_type_type") // module .allowlist_type("mp_obj_module_t") .allowlist_var("mp_type_module") // qstr .allowlist_function("qstr_data") // tuple .allowlist_type("mp_obj_tuple_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. .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(); } fn generate_trezorhal_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=trezorhal.h"); let bindings = prepare_bindings() .header("trezorhal.h") // model .allowlist_var("MODEL_INTERNAL_NAME") .allowlist_var("MODEL_FULL_NAME") // common .allowlist_var("HW_ENTROPY_DATA") // secbool .allowlist_type("secbool") .must_use_type("secbool") .allowlist_var("sectrue") .allowlist_var("secfalse") // flash .allowlist_function("flash_init") // storage .allowlist_var("EXTERNAL_SALT_SIZE") .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_ensure_not_wipe_code") .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") .allowlist_function("translations_read") .allowlist_function("translations_write") .allowlist_function("translations_erase") .allowlist_function("translations_area_bytesize") // display .allowlist_function("display_clear") .allowlist_function("display_offset") .allowlist_function("display_refresh") .allowlist_function("display_backlight") .allowlist_function("display_text") .allowlist_function("display_text_render_buffer") .allowlist_function("display_pixeldata") .allowlist_function("display_pixeldata_dirty") .allowlist_function("display_set_window") .allowlist_function("display_sync") .allowlist_function("display_get_fb_addr") .allowlist_function("display_get_wr_addr") .allowlist_var("DISPLAY_DATA_ADDRESS") .allowlist_var("DISPLAY_FRAMEBUFFER_WIDTH") .allowlist_var("DISPLAY_FRAMEBUFFER_HEIGHT") .allowlist_var("DISPLAY_FRAMEBUFFER_OFFSET_X") .allowlist_var("DISPLAY_FRAMEBUFFER_OFFSET_Y") .allowlist_var("DISPLAY_RESX") .allowlist_var("DISPLAY_RESY") .allowlist_function("display_get_frame_addr") .allowlist_function("display_fill") .allowlist_function("display_copy_rgb565") // dma2d .allowlist_type("dma2d_params_t") .allowlist_function("rgb565_fill") .allowlist_function("rgb565_copy_mono4") .allowlist_function("rgb565_copy_rgb565") .allowlist_function("rgb565_blend_mono4") .allowlist_function("rgba8888_fill") .allowlist_function("rgba8888_copy_mono4") .allowlist_function("rgba8888_copy_rgb565") .allowlist_function("rgba8888_copy_rgba8888") .allowlist_function("rgba8888_blend_mono4") .allowlist_function("mono8_fill") .allowlist_function("mono8_copy_mono1p") .allowlist_function("mono8_copy_mono4") .allowlist_function("mono8_blend_mono1p") .allowlist_function("mono8_blend_mono4") // fonts .allowlist_function("font_height") .allowlist_function("font_max_height") .allowlist_function("font_baseline") .allowlist_function("font_get_glyph") .allowlist_function("font_text_width") // uzlib .allowlist_function("uzlib_uncompress_init") .allowlist_function("uzlib_uncompress") // 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") .allowlist_var("SLIP39_WORDLIST") .allowlist_var("SLIP39_WORD_COUNT") // random .allowlist_function("random_uniform") // rgb led .allowlist_function("rgb_led_set_color") // time .allowlist_function("hal_delay") .allowlist_function("hal_ticks_ms") // toif .allowlist_type("toif_format_t") // dma2d .allowlist_function("dma2d_setup_const") .allowlist_function("dma2d_setup_4bpp") .allowlist_function("dma2d_setup_16bpp") .allowlist_function("dma2d_setup_4bpp_over_4bpp") .allowlist_function("dma2d_setup_4bpp_over_16bpp") .allowlist_function("dma2d_start") .allowlist_function("dma2d_start_blend") .allowlist_function("dma2d_start_const") .allowlist_function("dma2d_start_const_multiline") .allowlist_function("dma2d_wait_for_transfer") //buffers .allowlist_function("buffers_get_line_16bpp") .allowlist_function("buffers_free_line_16bpp") .allowlist_function("buffers_get_line_4bpp") .allowlist_function("buffers_free_line_4bpp") .allowlist_function("buffers_get_text") .allowlist_function("buffers_free_text") .allowlist_function("buffers_get_jpeg") .allowlist_function("buffers_free_jpeg") .allowlist_function("buffers_get_jpeg_work") .allowlist_function("buffers_free_jpeg_work") .allowlist_function("buffers_get_blurring") .allowlist_function("buffers_free_blurring") .allowlist_function("buffers_get_blurring_totals") .allowlist_function("buffers_free_blurring_totals") .allowlist_var("TEXT_BUFFER_HEIGHT") .no_copy("buffer_line_16bpp_t") .no_copy("buffer_line_4bpp_t") .no_copy("buffer_text_t") .no_copy("buffer_jpeg_t") .no_copy("buffer_jpeg_work_t") .no_copy("buffer_blurring_t") .no_copy("buffer_blurring_totals_t") //usb .allowlist_function("usb_configured") // touch .allowlist_function("touch_read") // button .allowlist_function("button_read") // haptic .allowlist_type("haptic_effect_t") .allowlist_function("haptic_play"); // 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("trezorhal.rs")) .unwrap(); } fn generate_crypto_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=crypto.h"); let bindings = prepare_bindings() .header("crypto.h") // ed25519 .allowlist_type("ed25519_signature") .allowlist_type("ed25519_public_key") .allowlist_function("ed25519_cosi_combine_publickeys") // incorrect signature from bindgen, see crypto::ed25519:ffi_override //.allowlist_function("ed25519_sign_open") // sha256 .allowlist_var("SHA256_DIGEST_LENGTH") .allowlist_type("SHA256_CTX") .no_copy("SHA256_CTX") .allowlist_function("sha256_Init") .allowlist_function("sha256_Update") .allowlist_function("sha256_Final"); // Write the bindings to a file in the OUR_DIR. bindings .clang_arg("-fgnuc-version=0") // avoid weirdness with ed25519.h CONST definition .generate() .expect("Unable to generate bindings") .write_to_file(PathBuf::from(out_path).join("crypto.rs")) .unwrap(); } fn is_firmware() -> bool { let target = env::var("TARGET").unwrap(); target.starts_with("thumbv7") || target.starts_with("thumbv8") } #[cfg(feature = "test")] fn link_core_objects() { let crate_path = env::var("CARGO_MANIFEST_DIR").unwrap(); let build_path = format!("{}/../../build/unix", crate_path); // List of object filenames to ignore in the `embed` directory let embed_blocklist = [OsStr::new("main_main.o")]; // Collect all objects that the `core` library uses, and link it in. We have to // make sure to avoid the object with the `_main` symbol, so we don't get any // duplicates. let mut cc = cc::Build::new(); for obj in glob::glob(&format!("{}/embed/**/*.o", build_path)).unwrap() { let obj = obj.unwrap(); if embed_blocklist.contains(&obj.file_name().unwrap()) { // Ignore. } else { cc.object(obj); } } for obj in glob::glob(&format!("{}/vendor/**/*.o", build_path)).unwrap() { let obj = obj.unwrap(); cc.object(obj); } // Add frozen modules, if present. for obj in glob::glob(&format!("{}/*.o", build_path)).unwrap() { cc.object(obj.unwrap()); } // Compile all the objects into a static library and link it in automatically. cc.compile("core_lib"); println!("cargo:rustc-link-lib=SDL2"); println!("cargo:rustc-link-lib=SDL2_image"); }