diff --git a/core/Makefile b/core/Makefile index 13184c643a..8b06bb6601 100644 --- a/core/Makefile +++ b/core/Makefile @@ -16,6 +16,7 @@ BOOTLOADER_BUILD_DIR = $(BUILD_DIR)/bootloader BOOTLOADER_CI_BUILD_DIR = $(BUILD_DIR)/bootloader_ci BOOTLOADER_EMU_BUILD_DIR = $(BUILD_DIR)/bootloader_emu PRODTEST_BUILD_DIR = $(BUILD_DIR)/prodtest +PRODTEST_EMU_BUILD_DIR = $(BUILD_DIR)/prodtest_emu REFLASH_BUILD_DIR = $(BUILD_DIR)/reflash KERNEL_BUILD_DIR = $(BUILD_DIR)/kernel FIRMWARE_BUILD_DIR = $(BUILD_DIR)/firmware @@ -295,6 +296,9 @@ build_bootloader_emu_debug: ## build the unix bootloader emulator build_prodtest: ## build production test firmware $(SCONS) $(PRODTEST_BUILD_DIR)/prodtest.bin +build_prodtest_emu: ## build the unix prodtest emulator + $(SCONS) $(PRODTEST_EMU_BUILD_DIR)/prodtest.elf + build_reflash: ## build reflash firmware + reflash image $(SCONS) $(REFLASH_BUILD_DIR)/reflash.bin dd if=build/boardloader/boardloader.bin of=$(REFLASH_BUILD_DIR)/sdimage.bin bs=1 seek=0 diff --git a/core/SConscript.prodtest b/core/SConscript.prodtest index 7349bef1b6..155df0f7d2 100644 --- a/core/SConscript.prodtest +++ b/core/SConscript.prodtest @@ -132,6 +132,7 @@ FEATURES_AVAILABLE = models.configure_board(TREZOR_MODEL, HW_REVISION, FEATURES_ SOURCE_PRODTEST = [ 'embed/projects/prodtest/header.S', 'embed/projects/prodtest/main.c', + 'embed/projects/prodtest/commands.c', 'embed/projects/prodtest/cmd/prodtest_boardloader.c', 'embed/projects/prodtest/cmd/prodtest_ble.c', 'embed/projects/prodtest/cmd/prodtest_bootloader.c', diff --git a/core/SConscript.prodtest_emu b/core/SConscript.prodtest_emu new file mode 100644 index 0000000000..91930402b3 --- /dev/null +++ b/core/SConscript.prodtest_emu @@ -0,0 +1,289 @@ +# pylint: disable=E0602 + +import os +import shlex +import tools, models, ui + +TREZOR_MODEL = ARGUMENTS.get('TREZOR_MODEL', 'T2T1') +CMAKELISTS = int(ARGUMENTS.get('CMAKELISTS', 0)) +HW_REVISION = 'emulator' + +FEATURE_FLAGS = { + "AES_GCM": True, +} + +if not models.has_emulator(TREZOR_MODEL): + # skip prodtest build + env = Environment() + def build_prodtest(target,source,env): + print(f'prodtest: nothing to build for Model {TREZOR_MODEL}') + program_bin = env.Command( + target='prodtest.elf', + source=None, + action=build_prodtest + ) + Return() + +FEATURES_WANTED = ["input", "rgb_led", "display", "tropic", "sd_card", "usb"] + +CCFLAGS_MOD = '' +CPPPATH_MOD = [] +CPPDEFINES_MOD = [ + 'KERNEL_MODE', + 'AES_128', + 'USE_INSECURE_PRNG', +] +SOURCE_MOD = [] +SOURCE_MOD_CRYPTO = [] +CPPDEFINES_HAL = [] +SOURCE_HAL = [] +PATH_HAL = [] +RUST_UI_FEATURES = [] + +# modtrezorcrypto +CPPPATH_MOD += [ + 'vendor/trezor-crypto', + 'vendor/trezor-storage', +] +SOURCE_MOD += [ + 'vendor/trezor-storage/flash_area.c', +] +SOURCE_MOD_CRYPTO += [ + 'vendor/trezor-crypto/aes/aes_modes.c', + 'vendor/trezor-crypto/aes/aesccm.c', + 'vendor/trezor-crypto/aes/aescrypt.c', + 'vendor/trezor-crypto/aes/aeskey.c', + 'vendor/trezor-crypto/aes/aestab.c', + 'vendor/trezor-crypto/bignum.c', + 'vendor/trezor-crypto/blake256.c', + 'vendor/trezor-crypto/blake2b.c', + 'vendor/trezor-crypto/buffer.c', + 'vendor/trezor-crypto/chacha_drbg.c', + 'vendor/trezor-crypto/chacha20poly1305/chacha_merged.c', + 'vendor/trezor-crypto/der.c', + 'vendor/trezor-crypto/ecdsa.c', + 'vendor/trezor-crypto/ed25519-donna/curve25519-donna-32bit.c', + 'vendor/trezor-crypto/ed25519-donna/curve25519-donna-helpers.c', + 'vendor/trezor-crypto/ed25519-donna/curve25519-donna-scalarmult-base.c', + 'vendor/trezor-crypto/ed25519-donna/ed25519-donna-32bit-tables.c', + 'vendor/trezor-crypto/ed25519-donna/ed25519-donna-basepoint-table.c', + 'vendor/trezor-crypto/ed25519-donna/ed25519-donna-impl-base.c', + 'vendor/trezor-crypto/ed25519-donna/ed25519-keccak.c', + 'vendor/trezor-crypto/ed25519-donna/ed25519-sha3.c', + 'vendor/trezor-crypto/ed25519-donna/ed25519.c', + 'vendor/trezor-crypto/ed25519-donna/modm-donna-32bit.c', + 'vendor/trezor-crypto/groestl.c', + 'vendor/trezor-crypto/hasher.c', + 'vendor/trezor-crypto/hmac.c', + 'vendor/trezor-crypto/hmac_drbg.c', + 'vendor/trezor-crypto/memzero.c', + 'vendor/trezor-crypto/nist256p1.c', + 'vendor/trezor-crypto/rand.c', + 'vendor/trezor-crypto/ripemd160.c', + 'vendor/trezor-crypto/rfc6979.c', + 'vendor/trezor-crypto/secp256k1.c', + 'vendor/trezor-crypto/sha2.c', + 'vendor/trezor-crypto/sha3.c', + 'vendor/trezor-crypto/tls_prf.c', +] + +# AES-GCM +if FEATURE_FLAGS["AES_GCM"]: + CPPDEFINES_MOD += [ + 'USE_AES_GCM', + 'AES_VAR', + ] + SOURCE_MOD_CRYPTO += [ + 'vendor/trezor-crypto/aes/gf128mul.c', + 'vendor/trezor-crypto/aes/aesgcm.c', + ] + + +# modtrezorui +CPPPATH_MOD += [ + 'vendor/micropython/lib/uzlib' +] + +SOURCE_MOD += [ + 'embed/gfx/bitblt/gfx_bitblt.c', + 'embed/gfx/bitblt/gfx_bitblt_rgb565.c', + 'embed/gfx/bitblt/gfx_bitblt_rgba8888.c', + 'embed/gfx/bitblt/gfx_bitblt_mono8.c', + 'embed/gfx/fonts/font_bitmap.c', + 'embed/gfx/gfx_color.c', + 'embed/gfx/gfx_draw.c', + 'embed/gfx/terminal.c', + 'embed/io/display/display_utils.c', + 'embed/util/image/image.c', + 'embed/util/rsod/rsod.c', + 'embed/util/scm_revision/scm_revision.c', + 'embed/rtl/cli.c', + 'embed/rtl/error_handling.c', + 'embed/rtl/mini_printf.c', + 'embed/rtl/strutils.c', + 'embed/rtl/unit_test.c', + 'vendor/micropython/lib/uzlib/adler32.c', + 'vendor/micropython/lib/uzlib/crc32.c', + 'vendor/micropython/lib/uzlib/tinflate.c', +] + +ui.init_ui(TREZOR_MODEL, "prodtest", RUST_UI_FEATURES) + +env = Environment( + ENV=os.environ, + CFLAGS=ARGUMENTS.get('CFLAGS', '') + f" -DCONFIDENTIAL= -DPRODUCTION={ARGUMENTS.get('PRODUCTION', '0')}", + CPPDEFPREFIX="-D'", + CPPDEFSUFFIX="'", +) + +FEATURES_AVAILABLE = models.configure_board(TREZOR_MODEL, HW_REVISION, FEATURES_WANTED, env, CPPDEFINES_HAL, SOURCE_HAL, PATH_HAL) + +SOURCE_PRODTEST = [ + 'embed/projects/prodtest/main.c', + 'embed/projects/prodtest/commands.c', + 'embed/projects/prodtest/cmd/prodtest_boardloader.c', + 'embed/projects/prodtest/cmd/prodtest_ble.c', + 'embed/projects/prodtest/cmd/prodtest_bootloader.c', + 'embed/projects/prodtest/cmd/prodtest_button.c', + 'embed/projects/prodtest/cmd/prodtest_display.c', + 'embed/projects/prodtest/cmd/prodtest_prodtest.c', + 'embed/projects/prodtest/cmd/prodtest_backup_ram.c', + 'embed/projects/prodtest/cmd/prodtest_get_cpuid.c', + 'embed/projects/prodtest/cmd/prodtest_haptic.c', + 'embed/projects/prodtest/cmd/prodtest_help.c', + 'embed/projects/prodtest/cmd/prodtest_hw_revision.c', + 'embed/projects/prodtest/cmd/prodtest_nfc.c', + 'embed/projects/prodtest/cmd/prodtest_nrf.c', + 'embed/projects/prodtest/cmd/prodtest_optiga.c', + 'embed/projects/prodtest/cmd/prodtest_otp_batch.c', + 'embed/projects/prodtest/cmd/prodtest_otp_variant.c', + 'embed/projects/prodtest/cmd/prodtest_ping.c', + 'embed/projects/prodtest/cmd/prodtest_power_manager.c', + 'embed/projects/prodtest/cmd/prodtest_reboot.c', + 'embed/projects/prodtest/cmd/prodtest_rgbled.c', + 'embed/projects/prodtest/cmd/prodtest_sdcard.c', + 'embed/projects/prodtest/cmd/prodtest_tamper.c', + 'embed/projects/prodtest/cmd/prodtest_sbu.c', + 'embed/projects/prodtest/cmd/prodtest_touch.c', + 'embed/projects/prodtest/cmd/prodtest_tropic.c', + 'embed/projects/prodtest/cmd/prodtest_wpc', + 'embed/projects/prodtest/emulator.c', +] + +SOURCE_HAL += [ + 'embed/projects/unix/profile.c', +] + +env.Replace( + CAT='cat', + CP='cp', + AS='as', + AR='ar', + CC='gcc', + LINK='ld', + SIZE='size', + STRIP='strip', + OBJCOPY='objcopy', + PYTHON='python', + MAKECMAKELISTS='$PYTHON tools/make_cmakelists.py', ) + +MODEL_AS_NUMBER = str(models.get_hw_model_as_number(TREZOR_MODEL)) + +ALLPATHS = ['embed/rust', + 'embed/projects/prodtest', + 'embed/rtl/inc', + 'embed/models', + 'embed/projects/unix', + 'embed/gfx/inc', + 'embed/util/image/inc', + 'embed/util/rsod/inc', + 'embed/util/scm_revision/inc', + 'embed/util/translations/inc', + ] + CPPPATH_MOD + PATH_HAL + +env.Replace( + COPT=env.get('ENV').get('OPTIMIZE', '-Os'), + CCFLAGS='$COPT ' + '-g ' + '-nostdlib ' + '-std=gnu11 -Wall -Werror -Wpointer-arith -Wno-missing-braces -fno-common ' + '-fdata-sections -ffunction-sections ' + '-ffreestanding ' + '-fstack-protector-all ' + + CCFLAGS_MOD, + LINKFLAGS='', + CPPPATH=ALLPATHS, + CPPDEFINES=[ + 'TREZOR_PRODTEST', + 'TREZOR_EMULATOR', + 'TREZOR_MODEL_'+TREZOR_MODEL, + ] + CPPDEFINES_MOD + CPPDEFINES_HAL, + ASPPFLAGS='$CFLAGS $CCFLAGS', ) + +try: + env.ParseConfig('pkg-config --cflags --libs libjpeg') +except OSError: + print("libjpeg not installed, Emulator build is not possible") + +try: + env.ParseConfig('pkg-config --cflags --libs sdl2 SDL2_image') +except OSError: + print("SDL2 not installed, Emulator build is not possible") + + +env.Replace( + ALLSOURCES=SOURCE_MOD + SOURCE_MOD_CRYPTO + SOURCE_PRODTEST + SOURCE_HAL, + ALLDEFS=tools.get_defs_for_cmake(env['CPPDEFINES'])) + +cmake_gen = env.Command( + target='CMakeLists.txt', + source='', + action='$MAKECMAKELISTS --sources $ALLSOURCES --dirs $CPPPATH --defs $ALLDEFS', +) + +# +# Rust library +# +env.get("ENV")["RUST_TARGET"] = os.popen("rustc -vV | sed -n 's/host: //p'").read().strip() +features = ['prodtest',] + FEATURES_AVAILABLE + RUST_UI_FEATURES +if ARGUMENTS.get('TREZOR_EMULATOR_DEBUGGABLE', '0') == '1': + profile = 'dev' +else: + profile = 'release' + +rust = tools.add_rust_lib( + env=env, + build='prodtest_emu', + profile=profile, + features=features, + all_paths=ALLPATHS, + build_dir=str(Dir('.').abspath), +) + +env.Append(LINKFLAGS=['-lm']) + +if env['PLATFORM'] == 'darwin': + env.Append(LINKFLAGS=['-Wl,-dead_strip']) +else: + env.Append(LINKFLAGS=['-Wl,--gc-sections']) + +# +# Program objects +# + +obj_program = [] +obj_program += env.Object(source=SOURCE_MOD) +obj_program += env.Object(source=SOURCE_MOD_CRYPTO, CCFLAGS='$CCFLAGS -ftrivial-auto-var-init=zero') +obj_program += env.Object(source=SOURCE_PRODTEST) +obj_program += env.Object(source=SOURCE_HAL) + +program_elf = env.Command( + target='prodtest.elf', + source=obj_program, + action= + '$CC -o $TARGET $SOURCES $_LIBDIRFLAGS $_LIBFLAGS $LINKFLAGS') + +env.Depends(program_elf, rust) + +if CMAKELISTS != 0: + env.Depends(program_elf, cmake_gen) diff --git a/core/SConstruct b/core/SConstruct index fe37f6a630..f5ac1b4734 100644 --- a/core/SConstruct +++ b/core/SConstruct @@ -10,6 +10,7 @@ SConscript('SConscript.bootloader_emu', variant_dir='build/bootloader_emu', dupl SConscript('SConscript.kernel', variant_dir='build/kernel', duplicate=False) SConscript('SConscript.firmware', variant_dir='build/firmware', duplicate=False) SConscript('SConscript.prodtest', variant_dir='build/prodtest', duplicate=False) +SConscript('SConscript.prodtest_emu', variant_dir='build/prodtest_emu', duplicate=False) SConscript('SConscript.reflash', variant_dir='build/reflash', duplicate=False) SConscript('SConscript.unix', variant_dir='build/unix', duplicate=False) diff --git a/core/embed/projects/prodtest/cmd/prodtest_help.c b/core/embed/projects/prodtest/cmd/prodtest_help.c index ffe5a046a2..dd4b4fe1e2 100644 --- a/core/embed/projects/prodtest/cmd/prodtest_help.c +++ b/core/embed/projects/prodtest/cmd/prodtest_help.c @@ -21,6 +21,8 @@ #include +#include "commands.h" + static void prodtest_help(cli_t* cli) { const char* prefix = cli_arg(cli, "prefix"); size_t prefix_len = strlen(prefix); @@ -36,12 +38,8 @@ static void prodtest_help(cli_t* cli) { cli_trace(cli, "Available commands (filtered):"); } - extern cli_command_t _prodtest_cli_cmd_section_start; - extern cli_command_t _prodtest_cli_cmd_section_end; - - cli_command_t* cmd = &_prodtest_cli_cmd_section_start; - - while (cmd < &_prodtest_cli_cmd_section_end) { + const cli_command_t* cmd = commands_get_ptr(); + for (int i = 0; i < commands_count(); i++) { if (cmd->name[0] != '$' && strncmp(cmd->name, prefix, prefix_len) == 0) { cli_trace(cli, " %s - %s", cmd->name, cmd->info); } diff --git a/core/embed/projects/prodtest/commands.c b/core/embed/projects/prodtest/commands.c new file mode 100644 index 0000000000..528b6ebb7a --- /dev/null +++ b/core/embed/projects/prodtest/commands.c @@ -0,0 +1,55 @@ +/* + * 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 . + */ + +#include "commands.h" + +#ifdef TREZOR_EMULATOR +#include +const cli_command_t **_cmd_list; +size_t _cmd_count; + +void register_cli_command(const cli_command_t *cmd) { + _cmd_list = realloc(_cmd_list, sizeof(*_cmd_list) * (_cmd_count + 1)); + _cmd_list[_cmd_count++] = cmd; +} + +#endif + +const cli_command_t *commands_get_ptr(void) { +#ifdef TREZOR_EMULATOR + return *_cmd_list; + +#else + extern cli_command_t _prodtest_cli_cmd_section_start; + + return &_prodtest_cli_cmd_section_start; +#endif +} + +size_t commands_count(void) { +#ifdef TREZOR_EMULATOR + return _cmd_count; + +#else + extern cli_command_t _prodtest_cli_cmd_section_start; + extern cli_command_t _prodtest_cli_cmd_section_end; + + return &_prodtest_cli_cmd_section_end - &_prodtest_cli_cmd_section_start; +#endif +} diff --git a/core/embed/projects/prodtest/commands.h b/core/embed/projects/prodtest/commands.h new file mode 100644 index 0000000000..87c80a149d --- /dev/null +++ b/core/embed/projects/prodtest/commands.h @@ -0,0 +1,28 @@ +/* + * 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 . + */ + +#pragma once + +#include + +#include + +const cli_command_t* commands_get_ptr(void); + +size_t commands_count(void); diff --git a/core/embed/projects/prodtest/emulator.c b/core/embed/projects/prodtest/emulator.c new file mode 100644 index 0000000000..07ee4b30c5 --- /dev/null +++ b/core/embed/projects/prodtest/emulator.c @@ -0,0 +1,73 @@ +#include +#include + +#include + +#include + +#include +#include +#include + +#include + +int prodtest_main(void); + +void usage(void) { + printf("Usage: ./build/prodtest/prodtest_emu [options]\n"); + printf( + "To connect via terminal, install socat (i.e. 'sudo apt-get install " + "socat' in Ubuntu)\n"); + printf( + "Bind the UDP with 'socat -d -d " + "pty,link=/dev/ttyVCP0,mode=666,raw,echo=0 UDP:127.0.0.1:21424'\n"); + printf("Then you can connect with you terminal to /dev/ttyVCP0\n"); + printf(" -h show this help\n"); +} + +static int sdl_event_filter(void *userdata, SDL_Event *event) { + switch (event->type) { + case SDL_QUIT: + exit(3); + return 0; + case SDL_KEYUP: + if (event->key.repeat) { + return 0; + } + switch (event->key.keysym.sym) { + case SDLK_ESCAPE: + exit(3); + return 0; + case SDLK_p: + display_save("emu"); + return 0; + } + break; + } + return 1; +} + +int main(int argc, char **argv) { + SDL_SetEventFilter(sdl_event_filter, NULL); + + display_init(DISPLAY_RESET_CONTENT); + flash_init(); + flash_otp_init(); + + int opt; + while ((opt = getopt(argc, argv, "h")) != -1) { + switch (opt) { + default: + usage(); + exit(1); + } + } + + int exit_code = prodtest_main(); + + char msg[64]; + snprintf(msg, sizeof(msg), "Exit code: %d", exit_code); + + error_shutdown_ex("PRODTEST ERROR", msg, "UNEXPECTED EXIT"); + return 0; +} diff --git a/core/embed/projects/prodtest/main.c b/core/embed/projects/prodtest/main.c index c97fba4b61..5c022328df 100644 --- a/core/embed/projects/prodtest/main.c +++ b/core/embed/projects/prodtest/main.c @@ -31,6 +31,7 @@ #include #include +#include "commands.h" #include "rust_types.h" #include "rust_ui_prodtest.h" #include "sys/sysevent.h" @@ -133,6 +134,8 @@ static void vcp_intr(void) { cli_abort(&g_cli); } #define VCP_PACKET_LEN 512 #elif defined(USE_USB_FS) #define VCP_PACKET_LEN 64 +#elif defined(TREZOR_EMULATOR) +#define VCP_PACKET_LEN 64 #else #error "USB type not defined" #endif @@ -171,9 +174,13 @@ static void usb_init_all(void) { .rx_intr_byte = 3, // Ctrl-C .iface_num = VCP_IFACE, .data_iface_num = 0x01, +#ifdef TREZOR_EMULATOR + .emu_port = 21424, +#else .ep_cmd = 0x02, .ep_in = 0x01, .ep_out = 0x01, +#endif .polling_interval = 10, .max_packet_len = VCP_PACKET_LEN, }; @@ -254,7 +261,11 @@ void prodtest_show_homescreen(void) { } } +#ifndef TREZOR_EMULATOR int main(void) { +#else +int prodtest_main(void) { +#endif system_init(&rsod_panic_handler); drivers_init(); @@ -263,12 +274,7 @@ int main(void) { // Initialize command line interface cli_init(&g_cli, console_read, console_write, NULL); - extern cli_command_t _prodtest_cli_cmd_section_start; - extern cli_command_t _prodtest_cli_cmd_section_end; - - cli_set_commands( - &g_cli, &_prodtest_cli_cmd_section_start, - &_prodtest_cli_cmd_section_end - &_prodtest_cli_cmd_section_start); + cli_set_commands(&g_cli, commands_get_ptr(), commands_count()); #ifdef USE_OPTIGA optiga_init(); diff --git a/core/embed/rtl/inc/rtl/cli.h b/core/embed/rtl/inc/rtl/cli.h index 3afa229a9a..1d70cf1a77 100644 --- a/core/embed/rtl/inc/rtl/cli.h +++ b/core/embed/rtl/inc/rtl/cli.h @@ -66,12 +66,23 @@ typedef struct { #define CONCAT_INDIRECT(x, y) x##y #define CONCAT(x, y) CONCAT_INDIRECT(x, y) +#ifdef TREZOR_EMULATOR +#define PRODTEST_CLI_CMD(...) PRODTEST_CLI_CMD_IMPL(__COUNTER__, __VA_ARGS__) +#define PRODTEST_CLI_CMD_IMPL(cnt, ...) \ + extern void register_cli_command(const cli_command_t* cmd); \ + static const cli_command_t CONCAT(_cli_cmd_handler, cnt) = {__VA_ARGS__}; \ + __attribute__((constructor)) static void CONCAT( \ + __register, CONCAT(_cli_cmd_handler, cnt))(void) { \ + register_cli_command(&CONCAT(_cli_cmd_handler, cnt)); \ + } +#else // Registers a command handler by placing its registration structure // into a specially designated linker script section #define PRODTEST_CLI_CMD(...) \ __attribute__((used, \ section(".prodtest_cli_cmd"))) static const cli_command_t \ CONCAT(_cli_cmd_handler, __COUNTER__) = {__VA_ARGS__}; +#endif // Callback for writing characters to console output typedef size_t (*cli_write_cb_t)(void* ctx, const char* buf, size_t len);