mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-22 07:28:10 +00:00
feat(core): Add Rust bindings to MicroPython and trezorhal
core: Remove dangling module decls
core: Use new Cargo feature resolver, use external MacOS debug info
core: Rust docs improvements
core: Upgrade bindgen
core: Add test target to Rust
ci: build rust sources
build(core): .ARM.exidx.text.__aeabi_ui2f in t1 firmware size
It's an unwind table for softfloat function inserted by rustc, probably
can be removed to save 8 bytes:
599c58db70/link.x.in (L175-L182)
scons: Remove dead code
core: Move Rust target to build/rust
core: Replace extern with a FFI version
core: Add some explanatory Rust comments
core: Use correct path for the Rust lib
core: Remove Buffer::as_mut()
Mutable buffer access needs MP_BUFFER_WRITE flag. TBD in the Protobuf PR.
core: Improve docs for micropython::Buffer
core: Minor Rust docs changes
core: Rewrite trezor_obj_get_ll_checked
core: Fix incorrect doc comment
core: Remove cc from deps
fixup! core: Rewrite trezor_obj_get_ll_checked
core: update safety comments
This commit is contained in:
parent
77d00206ba
commit
6257584951
35
ci/shell.nix
35
ci/shell.nix
@ -2,25 +2,35 @@
|
||||
, hardwareTest ? false
|
||||
}:
|
||||
|
||||
# the last successful build of nixpkgs-unstable as of 2021-03-25
|
||||
with import (builtins.fetchTarball {
|
||||
url = "https://github.com/NixOS/nixpkgs/archive/c0e881852006b132236cbf0301bd1939bb50867e.tar.gz";
|
||||
sha256 = "0fy7z7yxk5n7yslsvx5cyc6h21qwi4bhxf3awhirniszlbvaazy2";
|
||||
})
|
||||
{ };
|
||||
|
||||
let
|
||||
moneroTests = fetchurl {
|
||||
mozillaOverlay = import (builtins.fetchTarball {
|
||||
url = "https://github.com/mozilla/nixpkgs-mozilla/archive/8c007b60731c07dd7a052cce508de3bb1ae849b4.tar.gz";
|
||||
sha256 = "1zybp62zz0h077zm2zmqs2wcg3whg6jqaah9hcl1gv4x8af4zhs6";
|
||||
});
|
||||
# the last successful build of nixpkgs-unstable as of 2021-03-25
|
||||
nixpkgs = import (builtins.fetchTarball {
|
||||
url = "https://github.com/NixOS/nixpkgs/archive/c0e881852006b132236cbf0301bd1939bb50867e.tar.gz";
|
||||
sha256 = "0fy7z7yxk5n7yslsvx5cyc6h21qwi4bhxf3awhirniszlbvaazy2";
|
||||
}) { overlays = [ mozillaOverlay ]; };
|
||||
moneroTests = nixpkgs.fetchurl {
|
||||
url = "https://github.com/ph4r05/monero/releases/download/v0.17.1.9-tests/trezor_tests";
|
||||
sha256 = "410bc4ff2ff1edc65e17f15b549bd1bf8a3776cf67abdea86aed52cf4bce8d9d";
|
||||
};
|
||||
moneroTestsPatched = runCommandCC "monero_trezor_tests" {} ''
|
||||
moneroTestsPatched = nixpkgs.runCommandCC "monero_trezor_tests" {} ''
|
||||
cp ${moneroTests} $out
|
||||
chmod +wx $out
|
||||
${patchelf}/bin/patchelf --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" "$out"
|
||||
${nixpkgs.patchelf}/bin/patchelf --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" "$out"
|
||||
chmod -w $out
|
||||
'';
|
||||
rustNightly = (nixpkgs.rustChannelOf { date = "2021-03-29"; channel = "nightly"; }).rust.override {
|
||||
targets = [
|
||||
"x86_64-unknown-linux-gnu" # emulator
|
||||
"thumbv7em-none-eabihf" # TT
|
||||
"thumbv7em-none-eabi" # T1
|
||||
];
|
||||
};
|
||||
in
|
||||
with nixpkgs;
|
||||
stdenv.mkDerivation ({
|
||||
name = "trezor-firmware-env";
|
||||
buildInputs = lib.optionals fullDeps [
|
||||
@ -38,6 +48,7 @@ stdenv.mkDerivation ({
|
||||
bash
|
||||
check
|
||||
clang-tools
|
||||
clang
|
||||
editorconfig-checker
|
||||
gcc
|
||||
gcc-arm-embedded
|
||||
@ -52,6 +63,8 @@ stdenv.mkDerivation ({
|
||||
pkgconfig
|
||||
poetry
|
||||
protobuf3_6
|
||||
rustfmt
|
||||
rustNightly
|
||||
wget
|
||||
zlib
|
||||
] ++ lib.optionals (!stdenv.isDarwin) [
|
||||
@ -80,6 +93,8 @@ stdenv.mkDerivation ({
|
||||
# Fix bdist-wheel problem by setting source date epoch to a more recent date
|
||||
SOURCE_DATE_EPOCH = 1600000000;
|
||||
|
||||
# Used by rust bindgen
|
||||
LIBCLANG_PATH = "${llvmPackages.libclang}/lib";
|
||||
} // (lib.optionalAttrs fullDeps) {
|
||||
TREZOR_MONERO_TESTS_PATH = moneroTestsPatched;
|
||||
})
|
||||
|
1
core/.changelog.d/1540.added
Normal file
1
core/.changelog.d/1540.added
Normal file
@ -0,0 +1 @@
|
||||
Rust FFI for MicroPython.
|
@ -12,6 +12,7 @@ PRODTEST_BUILD_DIR = $(BUILD_DIR)/prodtest
|
||||
REFLASH_BUILD_DIR = $(BUILD_DIR)/reflash
|
||||
FIRMWARE_BUILD_DIR = $(BUILD_DIR)/firmware
|
||||
UNIX_BUILD_DIR = $(BUILD_DIR)/unix
|
||||
RUST_BUILD_DIR = $(BUILD_DIR)/rust
|
||||
|
||||
UNAME_S := $(shell uname -s)
|
||||
UNIX_PORT_OPTS ?=
|
||||
@ -179,10 +180,10 @@ clean_reflash: ## clean reflash build
|
||||
rm -rf $(REFLASH_BUILD_DIR)
|
||||
|
||||
clean_firmware: ## clean firmware build
|
||||
rm -rf $(FIRMWARE_BUILD_DIR)
|
||||
rm -rf $(FIRMWARE_BUILD_DIR) $(RUST_BUILD_DIR)
|
||||
|
||||
clean_unix: ## clean unix build
|
||||
rm -rf $(UNIX_BUILD_DIR)
|
||||
rm -rf $(UNIX_BUILD_DIR) $(RUST_BUILD_DIR)
|
||||
|
||||
clean_cross: ## clean mpy-cross build
|
||||
$(MAKE) -C vendor/micropython/mpy-cross clean $(CROSS_PORT_OPTS)
|
||||
|
@ -1,4 +1,5 @@
|
||||
# pylint: disable=E0602
|
||||
# fmt: off
|
||||
|
||||
import os
|
||||
|
||||
@ -49,6 +50,7 @@ CPPDEFINES_MOD += [
|
||||
('USE_EOS', '1' if EVERYTHING else '0'),
|
||||
]
|
||||
SOURCE_MOD += [
|
||||
'embed/extmod/trezorobj.c',
|
||||
'embed/extmod/modtrezorcrypto/crc.c',
|
||||
'embed/extmod/modtrezorcrypto/modtrezorcrypto.c',
|
||||
'embed/extmod/modtrezorcrypto/rand.c',
|
||||
@ -394,11 +396,13 @@ if TREZOR_MODEL == 'T':
|
||||
CPU_CCFLAGS = '-mthumb -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -mtune=cortex-m4 '
|
||||
CPU_MODEL = 'STM32F427xx'
|
||||
LD_VARIANT = '_zkp' if FEATURE_FLAGS["SECP256K1_ZKP"] else ''
|
||||
RUST_TARGET = 'thumbv7em-none-eabihf'
|
||||
elif TREZOR_MODEL == '1':
|
||||
CPU_ASFLAGS = '-mthumb -mcpu=cortex-m3 -mfloat-abi=soft'
|
||||
CPU_CCFLAGS = '-mthumb -mtune=cortex-m3 -mcpu=cortex-m3 -mfloat-abi=soft '
|
||||
CPU_MODEL = 'STM32F405xx'
|
||||
LD_VARIANT = '' if EVERYTHING else '_min'
|
||||
RUST_TARGET = 'thumbv7em-none-eabi'
|
||||
else:
|
||||
raise ValueError('Unknown Trezor model')
|
||||
|
||||
@ -416,6 +420,7 @@ env.Replace(
|
||||
LINKFLAGS='-T embed/firmware/memory_${TREZOR_MODEL}%s.ld -Wl,--gc-sections -Wl,-Map=build/firmware/firmware.map -Wl,--warn-common' % LD_VARIANT,
|
||||
CPPPATH=[
|
||||
'.',
|
||||
'embed/rust',
|
||||
'embed/firmware',
|
||||
'embed/trezorhal',
|
||||
'embed/extmod/modtrezorui',
|
||||
@ -616,6 +621,33 @@ if FEATURE_FLAGS["SECP256K1_ZKP"]:
|
||||
action='cd ${SOURCE.dir}; ./gen_context',
|
||||
)
|
||||
|
||||
#
|
||||
# Rust library
|
||||
#
|
||||
|
||||
RUST_PROFILE = 'release'
|
||||
RUST_LIB = 'trezor_lib'
|
||||
RUST_LIBDIR = f'build/rust/{RUST_TARGET}/{RUST_PROFILE}'
|
||||
RUST_LIBPATH = f'{RUST_LIBDIR}/lib{RUST_LIB}.a'
|
||||
|
||||
def cargo_build():
|
||||
# Determine the profile build flags.
|
||||
if RUST_PROFILE == 'release':
|
||||
profile = '--release'
|
||||
else:
|
||||
profile = ''
|
||||
return f'cd embed/rust; cargo build {profile} --target={RUST_TARGET}'
|
||||
|
||||
rust = env.Command(
|
||||
target=RUST_LIBPATH,
|
||||
source='',
|
||||
action=cargo_build(), )
|
||||
|
||||
env.Depends(rust, qstr_generated)
|
||||
|
||||
env.Append(LINKFLAGS=f' -L{RUST_LIBDIR}')
|
||||
env.Append(LINKFLAGS=f' -l{RUST_LIB}')
|
||||
|
||||
#
|
||||
# Program objects
|
||||
#
|
||||
@ -658,9 +690,11 @@ program_elf = env.Command(
|
||||
target='firmware.elf',
|
||||
source=obj_program,
|
||||
action=
|
||||
'$LINK -o $TARGET $CCFLAGS $CFLAGS $LINKFLAGS $SOURCES -lc_nano -lm -lgcc',
|
||||
'$LINK -o $TARGET $CCFLAGS $CFLAGS $SOURCES $LINKFLAGS -lc_nano -lm -lgcc',
|
||||
)
|
||||
|
||||
env.Depends(program_elf, rust)
|
||||
|
||||
if TREZOR_MODEL == 'T':
|
||||
action_bin=[
|
||||
'$OBJCOPY -O binary -j .vendorheader -j .header -j .flash -j .data --pad-to 0x08100000 $SOURCE ${TARGET}.p1',
|
||||
|
@ -1,4 +1,5 @@
|
||||
# pylint: disable=E0602
|
||||
# fmt: off
|
||||
|
||||
import os
|
||||
|
||||
@ -48,6 +49,7 @@ CPPDEFINES_MOD += [
|
||||
('USE_EOS', '1' if EVERYTHING else '0'),
|
||||
]
|
||||
SOURCE_MOD += [
|
||||
'embed/extmod/trezorobj.c',
|
||||
'embed/extmod/modtrezorcrypto/modtrezorcrypto.c',
|
||||
'embed/extmod/modtrezorcrypto/crc.c',
|
||||
'vendor/trezor-crypto/address.c',
|
||||
@ -367,6 +369,7 @@ env.Replace(
|
||||
LIBS=['m'],
|
||||
CPPPATH=[
|
||||
'.',
|
||||
'embed/rust',
|
||||
'embed/unix',
|
||||
'embed/extmod/modtrezorui',
|
||||
'vendor/micropython',
|
||||
@ -567,6 +570,33 @@ if FEATURE_FLAGS["SECP256K1_ZKP"]:
|
||||
action='cd ${SOURCE.dir}; ./gen_context',
|
||||
)
|
||||
|
||||
#
|
||||
# Rust library
|
||||
#
|
||||
|
||||
RUST_PROFILE = 'release'
|
||||
RUST_LIB = 'trezor_lib'
|
||||
RUST_LIBDIR = f'build/rust/{RUST_PROFILE}'
|
||||
RUST_LIBPATH = f'{RUST_LIBDIR}/lib{RUST_LIB}.a'
|
||||
|
||||
def cargo_build():
|
||||
# Determine the profile build flags.
|
||||
if RUST_PROFILE == 'release':
|
||||
profile = '--release'
|
||||
else:
|
||||
profile = ''
|
||||
return f'cd embed/rust; cargo build {profile}'
|
||||
|
||||
rust = env.Command(
|
||||
target=RUST_LIBPATH,
|
||||
source='',
|
||||
action=cargo_build(), )
|
||||
|
||||
env.Depends(rust, qstr_generated)
|
||||
|
||||
env.Append(LINKFLAGS=f'-L{RUST_LIBDIR}')
|
||||
env.Append(LINKFLAGS=f'-l{RUST_LIB}')
|
||||
|
||||
#
|
||||
# Program objects
|
||||
#
|
||||
@ -588,3 +618,5 @@ program = env.Command(
|
||||
target='trezor-emu-core',
|
||||
source=obj_program,
|
||||
action='$CC -o $TARGET $SOURCES $_LIBDIRFLAGS $_LIBFLAGS $LINKFLAGS', )
|
||||
|
||||
env.Depends(program, rust)
|
||||
|
64
core/embed/extmod/trezorobj.c
Normal file
64
core/embed/extmod/trezorobj.c
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "memzero.h"
|
||||
#include "py/objint.h"
|
||||
|
||||
static bool mpz_as_ll_checked(const mpz_t *i, long long *value) {
|
||||
// Analogue of `mpz_as_int_checked` from mpz.c
|
||||
|
||||
unsigned long long val = 0;
|
||||
mpz_dig_t *d = i->dig + i->len;
|
||||
|
||||
while (d-- > i->dig) {
|
||||
if (val > (~0x8000000000000000 >> MPZ_DIG_SIZE)) {
|
||||
// will overflow
|
||||
*value = 0;
|
||||
return false;
|
||||
}
|
||||
val = (val << MPZ_DIG_SIZE) | *d;
|
||||
}
|
||||
|
||||
if (i->neg != 0) {
|
||||
val = -val;
|
||||
}
|
||||
|
||||
*value = val;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool trezor_obj_get_ll_checked(mp_obj_t obj, long long *value) {
|
||||
if (mp_obj_is_small_int(obj)) {
|
||||
// Value is fitting in a small int range. Return it directly.
|
||||
*value = MP_OBJ_SMALL_INT_VALUE(obj);
|
||||
return true;
|
||||
|
||||
} else if (mp_obj_is_type(obj, &mp_type_int)) {
|
||||
// Value is not fitting into small int range, but is an integer.
|
||||
mp_obj_int_t *self = MP_OBJ_TO_PTR(obj);
|
||||
// Try to get the long long value out of the MPZ struct.
|
||||
return mpz_as_ll_checked(&self->mpz, value);
|
||||
} else {
|
||||
// Value is not integer.
|
||||
*value = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
@ -75,4 +75,6 @@ static inline uint8_t trezor_obj_get_uint8(mp_obj_t obj) {
|
||||
return u;
|
||||
}
|
||||
|
||||
bool trezor_obj_get_ll_checked(mp_obj_t obj, long long *value);
|
||||
|
||||
#endif
|
||||
|
@ -26,7 +26,8 @@ sram_end = ORIGIN(SRAM) + LENGTH(SRAM);
|
||||
_ram_start = sram_start;
|
||||
_ram_end = sram_end;
|
||||
|
||||
_codelen = SIZEOF(.flash) + SIZEOF(.data) + SIZEOF(.ARM.exidx);
|
||||
/* .ARM.exidx.text.__aeabi_ui2f is probably not needed as long as we are using panic = "abort" */
|
||||
_codelen = SIZEOF(.flash) + SIZEOF(.data) + SIZEOF(.ARM.exidx) + SIZEOF(.ARM.exidx.text.__aeabi_ui2f);
|
||||
_flash_start = ORIGIN(FLASH);
|
||||
_flash_end = ORIGIN(FLASH) + LENGTH(FLASH);
|
||||
_heap_start = ADDR(.heap);
|
||||
|
@ -26,7 +26,8 @@ sram_end = ORIGIN(SRAM) + LENGTH(SRAM);
|
||||
_ram_start = sram_start;
|
||||
_ram_end = sram_end;
|
||||
|
||||
_codelen = SIZEOF(.flash) + SIZEOF(.data);
|
||||
/* .ARM.exidx.text.__aeabi_ui2f is probably not needed as long as we are using panic = "abort" */
|
||||
_codelen = SIZEOF(.flash) + SIZEOF(.data) + SIZEOF(.ARM.exidx.text.__aeabi_ui2f);
|
||||
_flash_start = ORIGIN(FLASH);
|
||||
_flash_end = ORIGIN(FLASH) + LENGTH(FLASH);
|
||||
_heap_start = ADDR(.heap);
|
||||
|
2
core/embed/rust/.cargo/config.toml
Normal file
2
core/embed/rust/.cargo/config.toml
Normal file
@ -0,0 +1,2 @@
|
||||
[build]
|
||||
target-dir = "../../build/rust"
|
385
core/embed/rust/Cargo.lock
generated
Normal file
385
core/embed/rust/Cargo.lock
generated
Normal file
@ -0,0 +1,385 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.58.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5d2549a1ca679efa833b9d88a424955109722c9fd95418b95414c19fd6f4bda"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cexpr",
|
||||
"clang-sys",
|
||||
"clap",
|
||||
"env_logger",
|
||||
"lazy_static",
|
||||
"lazycell",
|
||||
"log",
|
||||
"peeking_take_while",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"rustc-hash",
|
||||
"shlex",
|
||||
"which",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.67"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd"
|
||||
|
||||
[[package]]
|
||||
name = "cexpr"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27"
|
||||
dependencies = [
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clang-sys"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0659001ab56b791be01d4b729c44376edc6718cf389a502e579b77b758f3296c"
|
||||
dependencies = [
|
||||
"glob",
|
||||
"libc",
|
||||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.33.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"atty",
|
||||
"bitflags",
|
||||
"strsim",
|
||||
"textwrap",
|
||||
"unicode-width",
|
||||
"vec_map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cstr_core"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26bdf52fc09b421407bd990b4662a380420b6b6f61708bcba417731dca1b65ef"
|
||||
dependencies = [
|
||||
"cty",
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cty"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7313c0d620d0cb4dbd9d019e461a4beb501071ff46ec0ab933efb4daa76d73e3"
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26ecb66b4bdca6c1409b40fb255eefc2bd4f6d135dab3c3124f80ffa2a9661e"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"humantime",
|
||||
"log",
|
||||
"regex",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "lazycell"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89203f3fba0a3795506acaad8ebce3c80c0af93f994d5a1d7a0b1eeb23271929"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.6.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "351a32417a12d5f7e82c368a66781e307834dae04c6ce0cd4456d52989229883"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcf3805d4480bb5b86070dcfeb9e2cb2ebc148adb753c5cca5f884d1d65a42b2"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "5.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "peeking_take_while"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
"thread_local",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42a568c8f2cd051a4d283bd6eb0343ac214c1b0f1ac19f93e1175b2dee38c73d"
|
||||
|
||||
[[package]]
|
||||
name = "staticvec"
|
||||
version = "0.10.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c87f4be0fd89694157f3814ca88715ad8ba6010c453b1e89ca264aee04d70b9"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb9bc092d0d51e76b2b19d9d85534ffc9ec2db959a2523cdae0697e2972cd447"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "trezor_lib"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
"cc",
|
||||
"cstr_core",
|
||||
"cty",
|
||||
"staticvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "3.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
34
core/embed/rust/Cargo.toml
Normal file
34
core/embed/rust/Cargo.toml
Normal file
@ -0,0 +1,34 @@
|
||||
[package]
|
||||
name = "trezor_lib"
|
||||
version = "0.1.0"
|
||||
authors = ["SatoshiLabs <info@satoshilabs.com>"]
|
||||
edition = "2018"
|
||||
resolver = "2"
|
||||
build = "build.rs"
|
||||
|
||||
[lib]
|
||||
crate-type = ["staticlib"]
|
||||
|
||||
[profile.dev]
|
||||
panic = "abort"
|
||||
split-debuginfo = "unpacked"
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
opt-level = "z"
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
|
||||
[dependencies]
|
||||
cty = "0.2.1"
|
||||
|
||||
[dependencies.staticvec]
|
||||
version = "0.10.5"
|
||||
default_features = false
|
||||
|
||||
[dependencies.cstr_core]
|
||||
version = "0.2.2"
|
||||
default_features = false
|
||||
|
||||
[build-dependencies]
|
||||
bindgen = "0.58.0"
|
166
core/embed/rust/build.rs
Normal file
166
core/embed/rust/build.rs
Normal file
@ -0,0 +1,166 @@
|
||||
use std::{env, path::PathBuf, process::Command};
|
||||
|
||||
fn main() {
|
||||
generate_qstr_bindings();
|
||||
generate_micropython_bindings();
|
||||
}
|
||||
|
||||
/// Generates Rust module that exports QSTR constants used in firmware.
|
||||
fn generate_qstr_bindings() {
|
||||
let out_path = env::var("OUT_DIR").unwrap();
|
||||
let target = env::var("TARGET").unwrap();
|
||||
|
||||
// Tell cargo to invalidate the built crate whenever the header changes.
|
||||
println!("cargo:rerun-if-changed=qstr.h");
|
||||
|
||||
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 target.starts_with("thumbv7em-none-eabi") {
|
||||
"../../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(PathBuf::from(out_path).join("qstr.rs"))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn generate_micropython_bindings() {
|
||||
let out_path = env::var("OUT_DIR").unwrap();
|
||||
let target = env::var("TARGET").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()
|
||||
.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_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_get_ull_checked")
|
||||
// buffer
|
||||
.allowlist_function("mp_get_buffer")
|
||||
.allowlist_var("MP_BUFFER_READ")
|
||||
.allowlist_var("MP_BUFFER_WRITE")
|
||||
.allowlist_var("MP_BUFFER_RW")
|
||||
// dict
|
||||
.allowlist_type("mp_obj_dict_t")
|
||||
.allowlist_function("mp_obj_new_dict")
|
||||
.allowlist_function("mp_obj_dict_store")
|
||||
.allowlist_var("mp_type_dict")
|
||||
// fun
|
||||
.allowlist_type("mp_obj_fun_builtin_fixed_t")
|
||||
.allowlist_var("mp_type_fun_builtin_1")
|
||||
.allowlist_var("mp_type_fun_builtin_2")
|
||||
.allowlist_var("mp_type_fun_builtin_3")
|
||||
// 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_var("mp_type_list")
|
||||
// map
|
||||
.allowlist_type("mp_map_elem_t")
|
||||
.allowlist_type("mp_map_lookup_kind_t")
|
||||
.allowlist_function("mp_map_init")
|
||||
.allowlist_function("mp_map_init_fixed_table")
|
||||
.allowlist_function("mp_map_lookup")
|
||||
// runtime
|
||||
.allowlist_function("mp_raise_ValueError")
|
||||
// typ
|
||||
.allowlist_var("mp_type_type");
|
||||
|
||||
// `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");
|
||||
|
||||
// Pass in correct include paths and defines.
|
||||
if target.starts_with("thumbv7em-none-eabi") {
|
||||
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()));
|
||||
|
||||
bindings = bindings.clang_args(include_args);
|
||||
} else {
|
||||
bindings = bindings.clang_args(&[
|
||||
"-I../unix",
|
||||
"-I../../build/unix",
|
||||
"-I../../vendor/micropython",
|
||||
]);
|
||||
}
|
||||
|
||||
bindings
|
||||
// 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))
|
||||
// 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"))
|
||||
.unwrap();
|
||||
}
|
5
core/embed/rust/micropython.h
Normal file
5
core/embed/rust/micropython.h
Normal file
@ -0,0 +1,5 @@
|
||||
#include "py/gc.h"
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
|
||||
#include "../extmod/trezorobj.h"
|
5
core/embed/rust/qstr.h
Normal file
5
core/embed/rust/qstr.h
Normal file
@ -0,0 +1,5 @@
|
||||
enum Qstr {
|
||||
#define QDEF(id, str) id,
|
||||
#include "genhdr/qstrdefs.generated.h"
|
||||
#undef QDEF
|
||||
};
|
2
core/embed/rust/rust-toolchain
Normal file
2
core/embed/rust/rust-toolchain
Normal file
@ -0,0 +1,2 @@
|
||||
nightly
|
||||
|
2
core/embed/rust/rustfmt.toml
Normal file
2
core/embed/rust/rustfmt.toml
Normal file
@ -0,0 +1,2 @@
|
||||
wrap_comments = true
|
||||
|
49
core/embed/rust/src/error.rs
Normal file
49
core/embed/rust/src/error.rs
Normal file
@ -0,0 +1,49 @@
|
||||
use core::convert::Infallible;
|
||||
use core::fmt;
|
||||
|
||||
use cstr_core::CStr;
|
||||
|
||||
pub enum Error {
|
||||
Missing,
|
||||
OutOfRange,
|
||||
InvalidType,
|
||||
NotBuffer,
|
||||
NotInt,
|
||||
}
|
||||
|
||||
impl Error {
|
||||
pub fn as_cstr(&self) -> &'static CStr {
|
||||
// SAFETY: Safe because we are passing in \0-terminated literals.
|
||||
unsafe {
|
||||
let cstr = |s: &'static str| CStr::from_bytes_with_nul_unchecked(s.as_bytes());
|
||||
match self {
|
||||
Error::Missing => cstr("Missing\0"),
|
||||
Error::OutOfRange => cstr("OutOfRange\0"),
|
||||
Error::InvalidType => cstr("InvalidType\0"),
|
||||
Error::NotBuffer => cstr("NotBuffer\0"),
|
||||
Error::NotInt => cstr("NotInt\0"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Error> for &'static CStr {
|
||||
fn from(val: Error) -> Self {
|
||||
val.as_cstr()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.as_cstr().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
// Implements a conversion from `core::convert::Infallible` to `Error` to so
|
||||
// that code generic over `TryFrom` can work with values covered by the blanket
|
||||
// impl for `Into`: `https://doc.rust-lang.org/std/convert/enum.Infallible.html`
|
||||
impl From<Infallible> for Error {
|
||||
fn from(_: Infallible) -> Self {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
37
core/embed/rust/src/lib.rs
Normal file
37
core/embed/rust/src/lib.rs
Normal file
@ -0,0 +1,37 @@
|
||||
#![cfg_attr(not(test), no_std)]
|
||||
#![feature(never_type)]
|
||||
#![feature(unsize)]
|
||||
#![feature(coerce_unsized)]
|
||||
#![feature(dispatch_from_dyn)]
|
||||
#![deny(clippy::all)]
|
||||
#![deny(unsafe_op_in_unsafe_fn)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
mod error;
|
||||
#[macro_use]
|
||||
mod micropython;
|
||||
mod trezorhal;
|
||||
mod util;
|
||||
|
||||
#[cfg(not(test))]
|
||||
use core::panic::PanicInfo;
|
||||
#[cfg(not(test))]
|
||||
use cstr_core::CStr;
|
||||
|
||||
#[cfg(not(test))]
|
||||
#[panic_handler]
|
||||
fn panic(_info: &PanicInfo) -> ! {
|
||||
// Although it would be ideal to use the original error message, ignoring it
|
||||
// lets us avoid the `fmt` machinery and its code size and is also important for
|
||||
// security reasons, as we do not always controls the message contents. We
|
||||
// should also avoid printing "panic" or "rust" on the user screen to avoid any
|
||||
// confusion.
|
||||
|
||||
// SAFETY: Safe because we are passing in \0-terminated literals.
|
||||
let empty = unsafe { CStr::from_bytes_with_nul_unchecked("\0".as_bytes()) };
|
||||
let msg = unsafe { CStr::from_bytes_with_nul_unchecked("rs\0".as_bytes()) };
|
||||
|
||||
// TODO: Ideally we would take the file and line info out of
|
||||
// `PanicInfo::location()`.
|
||||
trezorhal::common::fatal_error(&empty, &msg, &empty, 0, &empty);
|
||||
}
|
66
core/embed/rust/src/micropython/buffer.rs
Normal file
66
core/embed/rust/src/micropython/buffer.rs
Normal file
@ -0,0 +1,66 @@
|
||||
use core::{convert::TryFrom, ops::Deref, ptr, slice};
|
||||
|
||||
use crate::{error::Error, micropython::obj::Obj};
|
||||
|
||||
use super::ffi;
|
||||
|
||||
/// Represents an immutable slice of bytes stored on the MicroPython heap and
|
||||
/// owned by values that obey the buffer protocol, such as `bytes`, `str`,
|
||||
/// `bytearray` or `memoryview`.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// In most cases, it is unsound to store `Buffer` values in a GC-unreachable
|
||||
/// location, such as static data.
|
||||
pub struct Buffer {
|
||||
ptr: *const u8,
|
||||
len: usize,
|
||||
}
|
||||
|
||||
impl TryFrom<Obj> for Buffer {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(obj: Obj) -> Result<Self, Self::Error> {
|
||||
let mut bufinfo = ffi::mp_buffer_info_t {
|
||||
buf: ptr::null_mut(),
|
||||
len: 0,
|
||||
typecode: 0,
|
||||
};
|
||||
// SAFETY: We assume that if `ffi::mp_get_buffer` returns successfully,
|
||||
// `bufinfo.buf` contains a pointer to data of `bufinfo.len` bytes. Here
|
||||
// we consider this data either GC-allocated or effectively 'static, and
|
||||
// store the pointer directly in `Buffer`. It is unsound to store
|
||||
// `Buffer` values in a GC-unreachable location, such as static data.
|
||||
if unsafe { ffi::mp_get_buffer(obj, &mut bufinfo, ffi::MP_BUFFER_READ as _) } {
|
||||
Ok(Self {
|
||||
ptr: bufinfo.buf as _,
|
||||
len: bufinfo.len as _,
|
||||
})
|
||||
} else {
|
||||
Err(Error::NotBuffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Buffer {
|
||||
type Target = [u8];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for Buffer {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
if self.ptr.is_null() {
|
||||
// `self.ptr` can be null if len == 0.
|
||||
&[]
|
||||
} else {
|
||||
// SAFETY: We assume that `self.ptr` is pointing to memory:
|
||||
// - without any mutable references,
|
||||
// - immutable for the whole lifetime of `&self`,
|
||||
// - with at least `self.len` bytes.
|
||||
unsafe { slice::from_raw_parts(self.ptr, self.len) }
|
||||
}
|
||||
}
|
||||
}
|
70
core/embed/rust/src/micropython/dict.rs
Normal file
70
core/embed/rust/src/micropython/dict.rs
Normal file
@ -0,0 +1,70 @@
|
||||
use core::convert::TryFrom;
|
||||
|
||||
use crate::error::Error;
|
||||
|
||||
use super::{ffi, gc::Gc, map::Map, obj::Obj};
|
||||
|
||||
/// Insides of the MicroPython `dict` object.
|
||||
pub type Dict = ffi::mp_obj_dict_t;
|
||||
|
||||
impl Dict {
|
||||
/// Allocate a new dictionary on the GC heap, empty, but with a capacity of
|
||||
/// `capacity` items.
|
||||
pub fn alloc_with_capacity(capacity: usize) -> Gc<Self> {
|
||||
unsafe {
|
||||
// SAFETY: We expect that `ffi::mp_obj_new_dict` either returns a GC-allocated
|
||||
// pointer to `ffi::mp_obj_dict_t` or raises (i.e. on allocation failure).
|
||||
let ptr = ffi::mp_obj_new_dict(capacity);
|
||||
Gc::from_raw(ptr.as_ptr().cast())
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs a dictionary definition by taking ownership of given [`Map`].
|
||||
pub fn with_map(map: Map) -> Self {
|
||||
unsafe {
|
||||
Self {
|
||||
base: ffi::mp_obj_base_t {
|
||||
type_: &ffi::mp_type_dict,
|
||||
},
|
||||
map,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a reference to the contained [`Map`].
|
||||
pub fn map(&self) -> &Map {
|
||||
&self.map
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the contained [`Map`].
|
||||
pub fn map_mut(&mut self) -> &mut Map {
|
||||
&mut self.map
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Gc<Dict>> for Obj {
|
||||
fn from(value: Gc<Dict>) -> Self {
|
||||
// SAFETY:
|
||||
// - `value` is an object struct with a base and a type.
|
||||
// - `value` is GC-allocated.
|
||||
unsafe { Obj::from_ptr(Gc::into_raw(value).cast()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Obj> for Gc<Dict> {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: Obj) -> Result<Self, Self::Error> {
|
||||
if unsafe { ffi::mp_type_dict.is_type_of(value) } {
|
||||
// SAFETY: We assume that if `value` is an object pointer with the correct type,
|
||||
// it is managed by MicroPython GC (see `Gc::from_raw` for details).
|
||||
let this = unsafe { Gc::from_raw(value.as_ptr().cast()) };
|
||||
Ok(this)
|
||||
} else {
|
||||
Err(Error::InvalidType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: We are in a single-threaded environment.
|
||||
unsafe impl Sync for Dict {}
|
5
core/embed/rust/src/micropython/ffi.rs
Normal file
5
core/embed/rust/src/micropython/ffi.rs
Normal file
@ -0,0 +1,5 @@
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_upper_case_globals)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/micropython.rs"));
|
18
core/embed/rust/src/micropython/func.rs
Normal file
18
core/embed/rust/src/micropython/func.rs
Normal file
@ -0,0 +1,18 @@
|
||||
use super::{ffi, obj::Obj};
|
||||
|
||||
pub type Func = ffi::mp_obj_fun_builtin_fixed_t;
|
||||
|
||||
impl Func {
|
||||
/// Convert a "static const" function to a MicroPython object.
|
||||
pub const fn to_obj(&'static self) -> Obj {
|
||||
// SAFETY:
|
||||
// - We are an object struct with a base and a type.
|
||||
// - 'static lifetime holds us in place.
|
||||
// - MicroPython is smart enough not to mutate `mp_obj_fun_builtin_fixed_t`
|
||||
// objects.
|
||||
unsafe { Obj::from_ptr(self as *const _ as *mut _) }
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: We are in a single-threaded environment.
|
||||
unsafe impl Sync for Func {}
|
81
core/embed/rust/src/micropython/gc.rs
Normal file
81
core/embed/rust/src/micropython/gc.rs
Normal file
@ -0,0 +1,81 @@
|
||||
use core::{
|
||||
alloc::Layout,
|
||||
marker::Unsize,
|
||||
ops::{CoerceUnsized, Deref, DispatchFromDyn},
|
||||
ptr::{self, NonNull},
|
||||
};
|
||||
|
||||
use super::ffi;
|
||||
|
||||
/// A pointer type for values on the garbage-collected heap.
|
||||
///
|
||||
/// Although a garbage-collected pointer type technically should implement
|
||||
/// `Copy` and `Clone`, we avoid doing this until proven necessary.
|
||||
pub struct Gc<T: ?Sized>(NonNull<T>);
|
||||
|
||||
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Gc<U>> for Gc<T> {}
|
||||
impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<Gc<U>> for Gc<T> {}
|
||||
|
||||
impl<T> Gc<T> {
|
||||
/// Allocate memory on the heap managed by the MicroPython garbage collector
|
||||
/// and then place `v` into it. `v` will _not_ get its destructor called.
|
||||
pub fn new(v: T) -> Self {
|
||||
let layout = Layout::for_value(&v);
|
||||
// TODO: Assert that `layout.align()` is the same as the GC alignment.
|
||||
// SAFETY:
|
||||
// - Unfortunately we cannot respect `layout.align()` as MicroPython GC does
|
||||
// not support custom alignment.
|
||||
// - `ptr` is guaranteed to stay valid as long as it's reachable from the stack
|
||||
// or the MicroPython heap.
|
||||
unsafe {
|
||||
let raw = ffi::gc_alloc(layout.size(), 0).cast();
|
||||
ptr::write(raw, v);
|
||||
Self::from_raw(raw)
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a mutable reference to the value.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `Gc` values can originate in the MicroPython interpreter, and these can
|
||||
/// be both shared and mutable. Before calling this function, you have to
|
||||
/// ensure that `this` is unique for the whole lifetime of the
|
||||
/// returned mutable reference.
|
||||
pub unsafe fn as_mut(this: &mut Self) -> &mut T {
|
||||
// SAFETY: The caller must guarantee that `this` meets all the requirements for
|
||||
// a mutable reference.
|
||||
unsafe { this.0.as_mut() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Gc<T> {
|
||||
/// Construct a `Gc` from a raw pointer.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is unsafe because the caller has to guarantee that `ptr`
|
||||
/// is pointing to a memory understood by the MicroPython GC, that is:
|
||||
/// - previously allocated through `Gc::new()` or `gc_alloc()`, or
|
||||
/// - through the MicroPython interpreter, or
|
||||
/// - one of the GC roots (sys.argv, sys.modules, etc.).
|
||||
pub unsafe fn from_raw(ptr: *mut T) -> Self {
|
||||
// SAFETY: The caller must guarantee that `ptr` is something the MicroPython GC
|
||||
// can reason about.
|
||||
unsafe { Self(NonNull::new_unchecked(ptr)) }
|
||||
}
|
||||
|
||||
/// Convert `this` into a raw pointer. This will _not_ drop the contained
|
||||
/// value.
|
||||
pub fn into_raw(this: Self) -> *mut T {
|
||||
this.0.as_ptr()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Deref for Gc<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { self.0.as_ref() }
|
||||
}
|
||||
}
|
69
core/embed/rust/src/micropython/iter.rs
Normal file
69
core/embed/rust/src/micropython/iter.rs
Normal file
@ -0,0 +1,69 @@
|
||||
use core::ptr;
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::micropython::obj::Obj;
|
||||
|
||||
use super::ffi;
|
||||
|
||||
pub struct IterBuf {
|
||||
iter_buf: ffi::mp_obj_iter_buf_t,
|
||||
}
|
||||
|
||||
impl IterBuf {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
iter_buf: ffi::mp_obj_iter_buf_t {
|
||||
base: ffi::mp_obj_base_t {
|
||||
type_: ptr::null_mut(),
|
||||
},
|
||||
buf: [Obj::const_null(), Obj::const_null(), Obj::const_null()],
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Iter<'a> {
|
||||
iter: Obj,
|
||||
iter_buf: &'a mut IterBuf,
|
||||
finished: bool,
|
||||
}
|
||||
|
||||
impl<'a> Iter<'a> {
|
||||
pub fn try_from_obj_with_buf(o: Obj, iter_buf: &'a mut IterBuf) -> Result<Self, Error> {
|
||||
// SAFETY:
|
||||
// - In the common case, `ffi::mp_getiter` does not heap-allocate, but instead
|
||||
// uses memory from the passed `iter_buf`. We maintain this invariant by
|
||||
// taking a mut ref to `IterBuf` and tying it to the lifetime of returned
|
||||
// `Iter`.
|
||||
// - Raises if `o` is not iterable and in other cases as well.
|
||||
// - Returned obj is referencing into `iter_buf`.
|
||||
// TODO: Use a fn that doesn't raise on non-iterable object.
|
||||
let iter = unsafe { ffi::mp_getiter(o, &mut iter_buf.iter_buf) };
|
||||
Ok(Self {
|
||||
iter,
|
||||
iter_buf,
|
||||
finished: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Iter<'a> {
|
||||
type Item = Obj;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.finished {
|
||||
return None;
|
||||
}
|
||||
// SAFETY:
|
||||
// - We assume that `mp_iternext` returns objects without any lifetime
|
||||
// invariants, i.e. heap-allocated, unlike `mp_getiter`.
|
||||
// - Can raise.
|
||||
let item = unsafe { ffi::mp_iternext(self.iter) };
|
||||
if item == Obj::const_stop_iteration() {
|
||||
self.finished = true;
|
||||
None
|
||||
} else {
|
||||
Some(item)
|
||||
}
|
||||
}
|
||||
}
|
50
core/embed/rust/src/micropython/list.rs
Normal file
50
core/embed/rust/src/micropython/list.rs
Normal file
@ -0,0 +1,50 @@
|
||||
use core::convert::TryFrom;
|
||||
|
||||
use crate::error::Error;
|
||||
|
||||
use super::{ffi, gc::Gc, obj::Obj};
|
||||
|
||||
pub type List = ffi::mp_obj_list_t;
|
||||
|
||||
impl List {
|
||||
pub fn alloc(values: &[Obj]) -> Gc<Self> {
|
||||
// SAFETY: Altough `values` are copied into the new list and not mutated,
|
||||
// `mp_obj_new_list` is taking them through a mut pointer.
|
||||
unsafe {
|
||||
let list = ffi::mp_obj_new_list(values.len(), values.as_ptr() as *mut Obj);
|
||||
Gc::from_raw(list.as_ptr().cast())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn append(&mut self, value: Obj) {
|
||||
unsafe {
|
||||
let ptr = self as *mut Self;
|
||||
let list = Obj::from_ptr(ptr.cast());
|
||||
ffi::mp_obj_list_append(list, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Gc<List>> for Obj {
|
||||
fn from(value: Gc<List>) -> Self {
|
||||
// SAFETY:
|
||||
// - `value` is an object struct with a base and a type.
|
||||
// - `value` is GC-allocated.
|
||||
unsafe { Obj::from_ptr(Gc::into_raw(value).cast()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Obj> for Gc<List> {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: Obj) -> Result<Self, Self::Error> {
|
||||
if unsafe { ffi::mp_type_list.is_type_of(value) } {
|
||||
// SAFETY: We assume that if `value` is an object pointer with the correct type,
|
||||
// it is managed by MicroPython GC (see `Gc::from_raw` for details).
|
||||
let this = unsafe { Gc::from_raw(value.as_ptr().cast()) };
|
||||
Ok(this)
|
||||
} else {
|
||||
Err(Error::InvalidType)
|
||||
}
|
||||
}
|
||||
}
|
132
core/embed/rust/src/micropython/macros.rs
Normal file
132
core/embed/rust/src/micropython/macros.rs
Normal file
@ -0,0 +1,132 @@
|
||||
macro_rules! obj_fn_1 {
|
||||
($f:expr) => {{
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe {
|
||||
use $crate::micropython::ffi;
|
||||
|
||||
ffi::mp_obj_fun_builtin_fixed_t {
|
||||
base: ffi::mp_obj_base_t {
|
||||
type_: &ffi::mp_type_fun_builtin_1,
|
||||
},
|
||||
fun: ffi::_mp_obj_fun_builtin_fixed_t__bindgen_ty_1 { _1: Some($f) },
|
||||
}
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! obj_fn_2 {
|
||||
($f:expr) => {{
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe {
|
||||
use $crate::micropython::ffi;
|
||||
|
||||
ffi::mp_obj_fun_builtin_fixed_t {
|
||||
base: ffi::mp_obj_base_t {
|
||||
type_: &ffi::mp_type_fun_builtin_2,
|
||||
},
|
||||
fun: ffi::_mp_obj_fun_builtin_fixed_t__bindgen_ty_1 { _2: Some($f) },
|
||||
}
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! obj_fn_3 {
|
||||
($f:expr) => {{
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe {
|
||||
use $crate::micropython::ffi;
|
||||
|
||||
ffi::mp_obj_fun_builtin_fixed_t {
|
||||
base: ffi::mp_obj_base_t {
|
||||
type_: &ffi::mp_type_fun_builtin_3,
|
||||
},
|
||||
fun: ffi::_mp_obj_fun_builtin_fixed_t__bindgen_ty_1 { _3: Some($f) },
|
||||
}
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
/// Construct fixed static const `Map` from `key` => `val` pairs.
|
||||
macro_rules! obj_map {
|
||||
($($key:expr => $val:expr),*) => ({
|
||||
Map::from_fixed_static(&[
|
||||
$(
|
||||
Map::at($key, $val),
|
||||
)*
|
||||
])
|
||||
});
|
||||
($($key:expr => $val:expr),* ,) => ({
|
||||
obj_map!($($key => $val),*)
|
||||
});
|
||||
}
|
||||
|
||||
/// Construct a `Dict` from the backing `Map`. See `obj_map` above.
|
||||
macro_rules! obj_dict {
|
||||
($map:expr) => {{
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe {
|
||||
use $crate::micropython::ffi;
|
||||
|
||||
ffi::mp_obj_dict_t {
|
||||
base: ffi::mp_obj_base_t {
|
||||
type_: &ffi::mp_type_dict,
|
||||
},
|
||||
map: $map,
|
||||
}
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! obj_type {
|
||||
(name: $name:expr,
|
||||
$(locals: $locals:expr,)?
|
||||
$(attr_fn: $attr_fn:ident,)?
|
||||
$(call_fn: $call_fn:ident,)?
|
||||
) => {{
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe {
|
||||
use $crate::micropython::ffi;
|
||||
|
||||
let name = $name.to_u16();
|
||||
|
||||
#[allow(unused_mut)]
|
||||
#[allow(unused_assignments)]
|
||||
let mut attr: ffi::mp_attr_fun_t = None;
|
||||
$(attr = Some($attr_fn);)?
|
||||
|
||||
#[allow(unused_mut)]
|
||||
#[allow(unused_assignments)]
|
||||
let mut call: ffi::mp_call_fun_t = None;
|
||||
$(call = Some($call_fn);)?
|
||||
|
||||
// TODO: This is safe only if we pass in `Dict` with fixed `Map` (created by
|
||||
// `Map::fixed()`), because only then will Micropython treat `locals_dict` as
|
||||
// immutable, and make the mutable cast safe.
|
||||
#[allow(unused_mut)]
|
||||
#[allow(unused_assignments)]
|
||||
let mut locals_dict = ::core::ptr::null_mut();
|
||||
$(locals_dict = $locals as *const _ as *mut _;)?
|
||||
|
||||
ffi::mp_obj_type_t {
|
||||
base: ffi::mp_obj_base_t {
|
||||
type_: &ffi::mp_type_type,
|
||||
},
|
||||
flags: 0,
|
||||
name,
|
||||
print: None,
|
||||
make_new: None,
|
||||
call,
|
||||
unary_op: None,
|
||||
binary_op: None,
|
||||
attr,
|
||||
subscr: None,
|
||||
getiter: None,
|
||||
iternext: None,
|
||||
buffer_p: ffi::mp_buffer_p_t { get_buffer: None },
|
||||
protocol: ::core::ptr::null(),
|
||||
parent: ::core::ptr::null(),
|
||||
locals_dict,
|
||||
}
|
||||
}
|
||||
}};
|
||||
}
|
193
core/embed/rust/src/micropython/map.rs
Normal file
193
core/embed/rust/src/micropython/map.rs
Normal file
@ -0,0 +1,193 @@
|
||||
use core::{marker::PhantomData, mem::MaybeUninit, ops::Deref, ptr, slice};
|
||||
|
||||
use crate::{
|
||||
error::Error,
|
||||
micropython::{obj::Obj, qstr::Qstr},
|
||||
};
|
||||
|
||||
use super::ffi;
|
||||
|
||||
pub type Map = ffi::mp_map_t;
|
||||
pub type MapElem = ffi::mp_map_elem_t;
|
||||
|
||||
impl Map {
|
||||
pub const fn from_fixed_static<const N: usize>(table: &'static [MapElem; N]) -> Self {
|
||||
// We can't use the regular accessors generated by bindgen for us, as they are
|
||||
// not `const`. Instead, we build the bitfield manually.
|
||||
|
||||
// micropython/py/obj.h MP_DEFINE_CONST_DICT
|
||||
// .all_keys_are_qstrs = 1,
|
||||
// .is_fixed = 1,
|
||||
// .is_ordered = 1,
|
||||
// .used = MP_ARRAY_SIZE(table_name),
|
||||
// .alloc = MP_ARRAY_SIZE(table_name),
|
||||
let bits = 0b111 | N << 3;
|
||||
let bitfield = ffi::__BindgenBitfieldUnit::new(bits.to_ne_bytes());
|
||||
Self {
|
||||
_bitfield_align_1: [],
|
||||
_bitfield_1: bitfield,
|
||||
alloc: N,
|
||||
table: table.as_ptr() as *mut MapElem,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn at(key: Qstr, value: Obj) -> MapElem {
|
||||
MapElem {
|
||||
key: key.to_obj(),
|
||||
value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Map {
|
||||
pub fn from_fixed<'a>(table: &'a [MapElem]) -> MapRef<'a> {
|
||||
let mut map = MaybeUninit::uninit();
|
||||
// SAFETY: `mp_map_init` completely initializes all fields of `map`.
|
||||
unsafe {
|
||||
ffi::mp_map_init_fixed_table(map.as_mut_ptr(), table.len(), table.as_ptr().cast());
|
||||
MapRef::new(map.assume_init())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
let mut map = MaybeUninit::uninit();
|
||||
// SAFETY: `mp_map_init` completely initializes all fields of `map`, allocating
|
||||
// the backing storage for `capacity` items on the heap.
|
||||
unsafe {
|
||||
ffi::mp_map_init(map.as_mut_ptr(), capacity);
|
||||
map.assume_init()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.used()
|
||||
}
|
||||
|
||||
pub fn elems(&self) -> &[MapElem] {
|
||||
// SAFETY: `self.table` should always point to an array of `MapElem` of
|
||||
// `self.len()` items valid at least for the lifetime of `self`.
|
||||
unsafe { slice::from_raw_parts(self.table, self.len()) }
|
||||
}
|
||||
|
||||
pub fn contains_key(&self, index: impl Into<Obj>) -> bool {
|
||||
self.get_obj(index.into()).is_ok()
|
||||
}
|
||||
|
||||
pub fn get(&self, index: impl Into<Obj>) -> Result<Obj, Error> {
|
||||
self.get_obj(index.into())
|
||||
}
|
||||
|
||||
pub fn get_obj(&self, index: Obj) -> Result<Obj, Error> {
|
||||
// SAFETY:
|
||||
// - `mp_map_lookup` returns either NULL or a pointer to a `mp_map_elem_t`
|
||||
// value with a lifetime valid for the whole lifetime of the passed immutable
|
||||
// ref of `map`.
|
||||
// - with `_mp_map_lookup_kind_t_MP_MAP_LOOKUP`, `map` stays unmodified and the
|
||||
// cast to mut ptr is therefore safe.
|
||||
unsafe {
|
||||
let map = self as *const Self as *mut Self;
|
||||
let elem = ffi::mp_map_lookup(map, index, ffi::_mp_map_lookup_kind_t_MP_MAP_LOOKUP)
|
||||
.as_ref()
|
||||
.ok_or(Error::Missing)?;
|
||||
Ok(elem.value)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set(&mut self, index: impl Into<Obj>, value: impl Into<Obj>) {
|
||||
self.set_obj(index.into(), value.into())
|
||||
}
|
||||
|
||||
pub fn set_obj(&mut self, index: Obj, value: Obj) {
|
||||
// SAFETY:
|
||||
// - `mp_map_lookup` with `_mp_map_lookup_kind_t_MP_MAP_LOOKUP_ADD_IF_NOT_FOUND
|
||||
// returns a pointer to a `mp_map_elem_t` value with a lifetime valid for the
|
||||
// whole lifetime of `&mut self`.
|
||||
// - adding an element is an allocation, so it might raise.
|
||||
// - the original `elem.value` might be an `Obj` that will get GCd when we
|
||||
// replace it.
|
||||
unsafe {
|
||||
let map = self as *mut Self;
|
||||
let elem = ffi::mp_map_lookup(
|
||||
map,
|
||||
index,
|
||||
ffi::_mp_map_lookup_kind_t_MP_MAP_LOOKUP_ADD_IF_NOT_FOUND,
|
||||
)
|
||||
.as_mut()
|
||||
.unwrap();
|
||||
elem.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn delete(&mut self, index: impl Into<Obj>) {
|
||||
self.delete_obj(index.into())
|
||||
}
|
||||
|
||||
pub fn delete_obj(&mut self, index: Obj) {
|
||||
unsafe {
|
||||
let map = self as *mut Self;
|
||||
ffi::mp_map_lookup(
|
||||
map,
|
||||
index,
|
||||
ffi::_mp_map_lookup_kind_t_MP_MAP_LOOKUP_REMOVE_IF_FOUND,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Map {
|
||||
fn clone(&self) -> Self {
|
||||
let mut map = Self::with_capacity(self.len());
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping(self.table, map.table, self.len());
|
||||
}
|
||||
map.set_used(self.used());
|
||||
map.set_all_keys_are_qstrs(self.all_keys_are_qstrs());
|
||||
map.set_is_ordered(self.is_ordered());
|
||||
map.set_is_fixed(0);
|
||||
map
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Map {
|
||||
fn default() -> Self {
|
||||
Self::with_capacity(0)
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: We are in a single-threaded environment.
|
||||
unsafe impl Sync for Map {}
|
||||
unsafe impl Sync for MapElem {}
|
||||
|
||||
/// Wrapper over a private `Map` value with element lifetime `'a`.
|
||||
///
|
||||
/// `Map` itself does not have any lifetime parameter, so it can effectively
|
||||
/// only reference element tables with `'static` lifetime. `MapRef` is tying a
|
||||
/// private `Map` value to a lifetime `'a`, exposing it only by-ref within the
|
||||
/// `'a` lifetime. It's not possible to move the value out from the reference,
|
||||
/// and because `Map` is not `Clone`, it is also impossible to clone it. All
|
||||
/// this only holds for immutable ref, therefore we only impl `Deref`, not
|
||||
/// `DerefMut`.
|
||||
//
|
||||
// TODO: Better solution would be to make Map a wrapper type instead of a type
|
||||
// alias and add a lifetime parameter.
|
||||
pub struct MapRef<'a> {
|
||||
map: Map,
|
||||
_phantom: PhantomData<&'a Map>,
|
||||
}
|
||||
|
||||
impl<'a> MapRef<'a> {
|
||||
fn new(map: Map) -> Self {
|
||||
Self {
|
||||
map,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deref for MapRef<'a> {
|
||||
type Target = Map;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.map
|
||||
}
|
||||
}
|
15
core/embed/rust/src/micropython/mod.rs
Normal file
15
core/embed/rust/src/micropython/mod.rs
Normal file
@ -0,0 +1,15 @@
|
||||
#[macro_use]
|
||||
pub mod macros;
|
||||
|
||||
pub mod buffer;
|
||||
pub mod dict;
|
||||
pub mod ffi;
|
||||
pub mod func;
|
||||
pub mod gc;
|
||||
pub mod iter;
|
||||
pub mod list;
|
||||
pub mod map;
|
||||
pub mod obj;
|
||||
pub mod qstr;
|
||||
pub mod runtime;
|
||||
pub mod typ;
|
327
core/embed/rust/src/micropython/obj.rs
Normal file
327
core/embed/rust/src/micropython/obj.rs
Normal file
@ -0,0 +1,327 @@
|
||||
use core::convert::{TryFrom, TryInto};
|
||||
use core::num::TryFromIntError;
|
||||
|
||||
use crate::error::Error;
|
||||
|
||||
use super::ffi;
|
||||
|
||||
pub type Obj = ffi::mp_obj_t;
|
||||
pub type ObjBase = ffi::mp_obj_base_t;
|
||||
|
||||
impl PartialEq for Obj {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.as_bits() == other.as_bits()
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Obj {}
|
||||
|
||||
impl Obj {
|
||||
pub const unsafe fn from_ptr(ptr: *mut cty::c_void) -> Self {
|
||||
Self(ptr)
|
||||
}
|
||||
|
||||
pub const fn as_ptr(self) -> *mut cty::c_void {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub const unsafe fn from_bits(bits: cty::uintptr_t) -> Self {
|
||||
Self(bits as _)
|
||||
}
|
||||
|
||||
pub fn as_bits(self) -> cty::uintptr_t {
|
||||
self.0 as _
|
||||
}
|
||||
}
|
||||
|
||||
impl Obj {
|
||||
pub fn is_small_int(self) -> bool {
|
||||
// micropython/py/obj.h mp_obj_is_small_int
|
||||
self.as_bits() & 1 != 0
|
||||
}
|
||||
|
||||
pub fn is_qstr(self) -> bool {
|
||||
// micropython/py/obj.h mp_obj_is_qstr
|
||||
self.as_bits() & 7 == 2
|
||||
}
|
||||
|
||||
pub fn is_immediate(self) -> bool {
|
||||
// micropython/py/obj.h mp_obj_is_immediate_obj
|
||||
self.as_bits() & 7 == 6
|
||||
}
|
||||
|
||||
pub fn is_ptr(self) -> bool {
|
||||
// micropython/py/obj.h mp_obj_is_obj
|
||||
self.as_bits() & 3 == 0
|
||||
}
|
||||
}
|
||||
|
||||
impl Obj {
|
||||
pub const fn const_null() -> Self {
|
||||
// micropython/py/obj.h
|
||||
// #define MP_OBJ_NULL (MP_OBJ_FROM_PTR((void *)0))
|
||||
unsafe { Self::from_bits(0) }
|
||||
}
|
||||
|
||||
pub const fn const_stop_iteration() -> Self {
|
||||
// micropython/py/obj.h
|
||||
// #define MP_OBJ_STOP_ITERATION (MP_OBJ_FROM_PTR((void *)0))
|
||||
unsafe { Self::from_bits(0) }
|
||||
}
|
||||
|
||||
pub const fn const_none() -> Self {
|
||||
// micropython/py/obj.h
|
||||
// #define mp_const_none MP_OBJ_NEW_IMMEDIATE_OBJ(0)
|
||||
unsafe { Self::immediate(0) }
|
||||
}
|
||||
|
||||
pub const fn const_false() -> Self {
|
||||
// micropython/py/obj.h
|
||||
// #define mp_const_false MP_OBJ_NEW_IMMEDIATE_OBJ(1)
|
||||
unsafe { Self::immediate(1) }
|
||||
}
|
||||
|
||||
pub const fn const_true() -> Self {
|
||||
// micropython/py/obj.h
|
||||
// #define mp_const_true MP_OBJ_NEW_IMMEDIATE_OBJ(3)
|
||||
unsafe { Self::immediate(3) }
|
||||
}
|
||||
|
||||
const unsafe fn immediate(val: usize) -> Self {
|
||||
// SAFETY:
|
||||
// - `val` is in `0..=3` range.
|
||||
// - MicroPython compiled with `MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_A`.
|
||||
// - MicroPython compiled with `MICROPY_OBJ_IMMEDIATE_OBJS`.
|
||||
// micropython/py/obj.h #define MP_OBJ_NEW_IMMEDIATE_OBJ(val)
|
||||
// ((mp_obj_t)(((val) << 3) | 6))
|
||||
unsafe { Self::from_bits((val << 3) | 6) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Obj {
|
||||
pub fn call_with_n_args(self, args: &[Obj]) -> Obj {
|
||||
// SAFETY:
|
||||
// - Can call python code and therefore raise.
|
||||
// - Each of `args` has no lifetime bounds.
|
||||
unsafe { ffi::mp_call_function_n_kw(self, args.len(), 0, args.as_ptr()) }
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// # Converting `Obj` into plain data.
|
||||
//
|
||||
|
||||
impl TryFrom<Obj> for bool {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(obj: Obj) -> Result<Self, Self::Error> {
|
||||
// TODO: Avoid type casts on the Python side.
|
||||
// SAFETY:
|
||||
// - Can call python code and therefore raise.
|
||||
if unsafe { ffi::mp_obj_is_true(obj) } {
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Obj> for i32 {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(obj: Obj) -> Result<Self, Self::Error> {
|
||||
let mut int: ffi::mp_int_t = 0;
|
||||
|
||||
// TODO: Avoid type casts on the Python side.
|
||||
// SAFETY:
|
||||
// - Can raise if `obj` is int but cannot fit into `cty::mp_int_t`.
|
||||
if unsafe { ffi::mp_obj_get_int_maybe(obj.as_ptr(), &mut int) } {
|
||||
let int = int.try_into()?;
|
||||
Ok(int)
|
||||
} else {
|
||||
Err(Error::NotInt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Obj> for i64 {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(obj: Obj) -> Result<Self, Self::Error> {
|
||||
let mut ll: cty::c_longlong = 0;
|
||||
|
||||
if unsafe { ffi::trezor_obj_get_ll_checked(obj, &mut ll) } {
|
||||
Ok(ll)
|
||||
} else {
|
||||
Err(Error::NotInt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// # Converting plain data into `Obj`.
|
||||
//
|
||||
|
||||
impl From<bool> for Obj {
|
||||
fn from(val: bool) -> Self {
|
||||
if val {
|
||||
Obj::const_true()
|
||||
} else {
|
||||
Obj::const_false()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i32> for Obj {
|
||||
fn from(val: i32) -> Self {
|
||||
// `mp_obj_new_int` accepts a `mp_int_t` argument, which is word-sized. We
|
||||
// primarily target 32-bit architecture, and therefore keep the primary signed
|
||||
// conversion type as `i32`, but convert through `into()` if the types differ.
|
||||
|
||||
// SAFETY:
|
||||
// - Can raise if allocation fails.
|
||||
unsafe { ffi::mp_obj_new_int(val.into()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i64> for Obj {
|
||||
fn from(val: i64) -> Self {
|
||||
// Because `mp_obj_new_int_from_ll` allocates even if `val` fits into small-int,
|
||||
// we try to go through `mp_obj_new_int` first.
|
||||
match i32::try_from(val) {
|
||||
Ok(smaller_val) => smaller_val.into(),
|
||||
// SAFETY:
|
||||
// - Can raise if allocation fails.
|
||||
Err(_) => unsafe { ffi::mp_obj_new_int_from_ll(val) },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for Obj {
|
||||
fn from(val: u32) -> Self {
|
||||
// `mp_obj_new_int_from_uint` accepts a `mp_uint_t` argument, which is
|
||||
// word-sized. We primarily target 32-bit architecture, and therefore keep
|
||||
// the primary unsigned conversion type as `u32`, but convert through `into()`
|
||||
// if the types differ.
|
||||
|
||||
// SAFETY:
|
||||
// - Can raise if allocation fails.
|
||||
unsafe { ffi::mp_obj_new_int_from_uint(val.into()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u64> for Obj {
|
||||
fn from(val: u64) -> Self {
|
||||
// Because `mp_obj_new_int_from_ull` allocates even if `val` fits into
|
||||
// small-int, we try to go through `mp_obj_new_int_from_uint` first.
|
||||
match u32::try_from(val) {
|
||||
Ok(smaller_val) => smaller_val.into(),
|
||||
// SAFETY:
|
||||
// - Can raise if allocation fails.
|
||||
Err(_) => unsafe { ffi::mp_obj_new_int_from_ull(val) },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Byte slices are converted into `bytes` MicroPython objects, by allocating
|
||||
/// new space on the heap and copying.
|
||||
impl From<&[u8]> for Obj {
|
||||
fn from(val: &[u8]) -> Self {
|
||||
// SAFETY:
|
||||
// - Can raise if allocation fails.
|
||||
unsafe { ffi::mp_obj_new_bytes(val.as_ptr(), val.len()) }
|
||||
}
|
||||
}
|
||||
|
||||
/// String slices are converted into `str` MicroPython objects. Strings that are
|
||||
/// already interned will turn up as QSTRs, strings not found in the QSTR pool
|
||||
/// will be allocated on the heap and copied.
|
||||
impl From<&str> for Obj {
|
||||
fn from(val: &str) -> Self {
|
||||
// SAFETY:
|
||||
// - Can raise if allocation fails.
|
||||
// - `str` is guaranteed to be UTF-8.
|
||||
unsafe { ffi::mp_obj_new_str(val.as_ptr().cast(), val.len()) }
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// # Additional conversions based on the methods above.
|
||||
//
|
||||
|
||||
impl From<u8> for Obj {
|
||||
fn from(val: u8) -> Self {
|
||||
u32::from(val).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u16> for Obj {
|
||||
fn from(val: u16) -> Self {
|
||||
u32::from(val).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<usize> for Obj {
|
||||
fn from(val: usize) -> Self {
|
||||
// Willingly truncate the bits on 128-bit architectures.
|
||||
(val as u64).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Obj> for u8 {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(obj: Obj) -> Result<Self, Self::Error> {
|
||||
let val = i32::try_from(obj)?;
|
||||
let this = Self::try_from(val)?;
|
||||
Ok(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Obj> for u16 {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(obj: Obj) -> Result<Self, Self::Error> {
|
||||
let val = i32::try_from(obj)?;
|
||||
let this = Self::try_from(val)?;
|
||||
Ok(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Obj> for u32 {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(obj: Obj) -> Result<Self, Self::Error> {
|
||||
let val = i64::try_from(obj)?;
|
||||
let this = Self::try_from(val)?;
|
||||
Ok(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Obj> for u64 {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(obj: Obj) -> Result<Self, Self::Error> {
|
||||
// TODO: Support full range.
|
||||
let val = i64::try_from(obj)?;
|
||||
let this = Self::try_from(val)?;
|
||||
Ok(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Obj> for usize {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(obj: Obj) -> Result<Self, Self::Error> {
|
||||
// TODO: Support full range.
|
||||
let val = i64::try_from(obj)?;
|
||||
let this = Self::try_from(val)?;
|
||||
Ok(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TryFromIntError> for Error {
|
||||
fn from(_: TryFromIntError) -> Self {
|
||||
Self::OutOfRange
|
||||
}
|
||||
}
|
59
core/embed/rust/src/micropython/qstr.rs
Normal file
59
core/embed/rust/src/micropython/qstr.rs
Normal file
@ -0,0 +1,59 @@
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_upper_case_globals)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
use core::convert::TryFrom;
|
||||
|
||||
use crate::{error::Error, micropython::obj::Obj};
|
||||
|
||||
impl Qstr {
|
||||
pub const fn to_obj(self) -> Obj {
|
||||
// SAFETY:
|
||||
// - Micropython compiled with `MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_A`.
|
||||
// micropython/py/obj.h #define MP_OBJ_NEW_QSTR(qst)
|
||||
// ((mp_obj_t)((((mp_uint_t)(qst)) << 3) | 2))
|
||||
let bits = (self.0 << 3) | 2;
|
||||
unsafe { Obj::from_bits(bits as usize) }
|
||||
}
|
||||
|
||||
pub const fn from_obj_bits(bits: cty::uintptr_t) -> Self {
|
||||
let bits = (bits >> 3) as u16; // See `Self::to_obj`.
|
||||
Self::from_u16(bits)
|
||||
}
|
||||
|
||||
pub const fn to_u16(self) -> u16 {
|
||||
// TODO: Change the internal representation of Qstr to u16.
|
||||
self.0 as _
|
||||
}
|
||||
|
||||
pub const fn from_u16(val: u16) -> Self {
|
||||
// TODO: Change the internal representation of Qstr to u16.
|
||||
Self(val as _)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u16> for Qstr {
|
||||
fn from(val: u16) -> Self {
|
||||
Self::from_u16(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Obj> for Qstr {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: Obj) -> Result<Self, Self::Error> {
|
||||
if value.is_qstr() {
|
||||
Ok(Self::from_obj_bits(value.as_bits()))
|
||||
} else {
|
||||
Err(Error::InvalidType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Qstr> for Obj {
|
||||
fn from(value: Qstr) -> Self {
|
||||
value.to_obj()
|
||||
}
|
||||
}
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/qstr.rs"));
|
10
core/embed/rust/src/micropython/runtime.rs
Normal file
10
core/embed/rust/src/micropython/runtime.rs
Normal file
@ -0,0 +1,10 @@
|
||||
use cstr_core::CStr;
|
||||
|
||||
use super::ffi;
|
||||
|
||||
pub fn raise_value_error(msg: &'static CStr) -> ! {
|
||||
unsafe {
|
||||
ffi::mp_raise_ValueError(msg.as_ptr());
|
||||
}
|
||||
panic!();
|
||||
}
|
28
core/embed/rust/src/micropython/typ.rs
Normal file
28
core/embed/rust/src/micropython/typ.rs
Normal file
@ -0,0 +1,28 @@
|
||||
use super::{
|
||||
ffi,
|
||||
obj::{Obj, ObjBase},
|
||||
};
|
||||
|
||||
pub type Type = ffi::mp_obj_type_t;
|
||||
|
||||
impl Type {
|
||||
pub fn is_type_of(&'static self, obj: Obj) -> bool {
|
||||
if obj.is_ptr() {
|
||||
// SAFETY: If `obj` is a pointer, it should always point to an object having
|
||||
// `ObjBase` as the first field, making this cast safe.
|
||||
unsafe {
|
||||
let base = obj.as_ptr() as *const ObjBase;
|
||||
(*base).type_ == self
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_base(&'static self) -> ObjBase {
|
||||
ObjBase { type_: self }
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: We are in a single-threaded environment.
|
||||
unsafe impl Sync for Type {}
|
24
core/embed/rust/src/trezorhal/common.rs
Normal file
24
core/embed/rust/src/trezorhal/common.rs
Normal file
@ -0,0 +1,24 @@
|
||||
use cstr_core::CStr;
|
||||
|
||||
extern "C" {
|
||||
// trezorhal/common.c
|
||||
fn __fatal_error(
|
||||
expr: *const cty::c_char,
|
||||
msg: *const cty::c_char,
|
||||
file: *const cty::c_char,
|
||||
line: i32,
|
||||
func: *const cty::c_char,
|
||||
) -> !;
|
||||
}
|
||||
|
||||
pub fn fatal_error(expr: &CStr, msg: &CStr, file: &CStr, line: i32, func: &CStr) -> ! {
|
||||
unsafe {
|
||||
__fatal_error(
|
||||
expr.as_ptr(),
|
||||
msg.as_ptr(),
|
||||
file.as_ptr(),
|
||||
line,
|
||||
func.as_ptr(),
|
||||
);
|
||||
}
|
||||
}
|
69
core/embed/rust/src/trezorhal/display.rs
Normal file
69
core/embed/rust/src/trezorhal/display.rs
Normal file
@ -0,0 +1,69 @@
|
||||
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_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,
|
||||
);
|
||||
}
|
||||
|
||||
const WIDTH: i32 = 240;
|
||||
const HEIGHT: i32 = 240;
|
||||
|
||||
pub fn width() -> i32 {
|
||||
WIDTH
|
||||
}
|
||||
|
||||
pub fn height() -> i32 {
|
||||
HEIGHT
|
||||
}
|
||||
|
||||
pub fn backlight(val: i32) -> i32 {
|
||||
unsafe { display_backlight(val) }
|
||||
}
|
||||
|
||||
pub fn text(baseline_x: i32, baseline_y: i32, text: &[u8], font: i32, fgcolor: u16, bgcolor: u16) {
|
||||
unsafe {
|
||||
display_text(
|
||||
baseline_x,
|
||||
baseline_y,
|
||||
text.as_ptr() as _,
|
||||
text.len() as _,
|
||||
font,
|
||||
fgcolor,
|
||||
bgcolor,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn text_width(text: &[u8], font: i32) -> i32 {
|
||||
unsafe { display_text_width(text.as_ptr() as _, text.len() as _, font) }
|
||||
}
|
||||
|
||||
pub fn bar(x: i32, y: i32, w: i32, h: i32, fgcolor: u16) {
|
||||
unsafe { 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) }
|
||||
}
|
3
core/embed/rust/src/trezorhal/mod.rs
Normal file
3
core/embed/rust/src/trezorhal/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
pub mod common;
|
||||
pub mod display;
|
||||
pub mod random;
|
16
core/embed/rust/src/trezorhal/random.rs
Normal file
16
core/embed/rust/src/trezorhal/random.rs
Normal file
@ -0,0 +1,16 @@
|
||||
extern "C" {
|
||||
// trezor-crypto/rand.h
|
||||
fn random_uniform(n: u32) -> u32;
|
||||
}
|
||||
|
||||
pub fn uniform(n: u32) -> u32 {
|
||||
unsafe { random_uniform(n) }
|
||||
}
|
||||
|
||||
pub fn shuffle<T>(slice: &mut [T]) {
|
||||
// Fisher-Yates shuffle.
|
||||
for i in (1..slice.len()).rev() {
|
||||
let j = uniform(i as u32 + 1) as usize;
|
||||
slice.swap(i, j);
|
||||
}
|
||||
}
|
63
core/embed/rust/src/util.rs
Normal file
63
core/embed/rust/src/util.rs
Normal file
@ -0,0 +1,63 @@
|
||||
use core::slice;
|
||||
|
||||
use crate::{
|
||||
error::Error,
|
||||
micropython::{
|
||||
map::{Map, MapElem},
|
||||
obj::Obj,
|
||||
runtime::raise_value_error,
|
||||
},
|
||||
};
|
||||
|
||||
pub fn try_or_raise(func: impl FnOnce() -> Result<Obj, Error>) -> Obj {
|
||||
func().unwrap_or_else(|err| raise_value_error(err.as_cstr()))
|
||||
}
|
||||
|
||||
pub fn try_with_kwargs(kwargs: *const Map, func: impl FnOnce(&Map) -> Result<Obj, Error>) -> Obj {
|
||||
try_or_raise(|| {
|
||||
let kwargs = unsafe { kwargs.as_ref() }.ok_or(Error::Missing)?;
|
||||
|
||||
func(kwargs)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn try_with_args_and_kwargs(
|
||||
n_args: usize,
|
||||
args: *const Obj,
|
||||
kwargs: *const Map,
|
||||
func: impl FnOnce(&[Obj], &Map) -> Result<Obj, Error>,
|
||||
) -> Obj {
|
||||
try_or_raise(|| {
|
||||
let args = if args.is_null() {
|
||||
&[]
|
||||
} else {
|
||||
unsafe { slice::from_raw_parts(args, n_args) }
|
||||
};
|
||||
let kwargs = unsafe { kwargs.as_ref() }.ok_or(Error::Missing)?;
|
||||
|
||||
func(args, kwargs)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn try_with_args_and_kwargs_inline(
|
||||
n_args: usize,
|
||||
n_kw: usize,
|
||||
args: *const Obj,
|
||||
func: impl FnOnce(&[Obj], &Map) -> Result<Obj, Error>,
|
||||
) -> Obj {
|
||||
try_or_raise(|| {
|
||||
let args_slice: &[Obj];
|
||||
let kwargs_slice: &[MapElem];
|
||||
|
||||
if args.is_null() {
|
||||
args_slice = &[];
|
||||
kwargs_slice = &[];
|
||||
} else {
|
||||
args_slice = unsafe { slice::from_raw_parts(args, n_args) };
|
||||
kwargs_slice = unsafe { slice::from_raw_parts(args.add(n_args).cast(), n_kw) };
|
||||
}
|
||||
|
||||
let kw_map = Map::from_fixed(kwargs_slice);
|
||||
func(args_slice, &kw_map)
|
||||
})
|
||||
}
|
Loading…
Reference in New Issue
Block a user