diff --git a/core/SConscript.firmware b/core/SConscript.firmware index 68f85621dec..d58fb7dcb44 100644 --- a/core/SConscript.firmware +++ b/core/SConscript.firmware @@ -790,16 +790,14 @@ if TREZOR_MODEL not in ('1',): ' $SOURCE $TARGET', )) if TREZOR_MODEL not in ('DISC1', ): - obj_program.extend( - env.Command( - target='embed/firmware/bootloaders/bootloader.o', - source=f'embed/firmware/bootloaders/bootloader_{BOOTLOADER_SUFFIX}.bin', - action='$OBJCOPY -I binary -O elf32-littlearm -B arm' - ' --rename-section .data=.bootloader' - f' --redefine-sym _binary_embed_firmware_bootloaders_bootloader_{BOOTLOADER_SUFFIX}_bin_start=_binary_embed_firmware_bootloader_bin_start' - f' --redefine-sym _binary_embed_firmware_bootloaders_bootloader_{BOOTLOADER_SUFFIX}_bin_end=_binary_embed_firmware_bootloader_bin_end' - f' --redefine-sym _binary_embed_firmware_bootloaders_bootloader_{BOOTLOADER_SUFFIX}_bin_size=_binary_embed_firmware_bootloader_bin_size' - ' $SOURCE $TARGET', )) + tools.embed_binary( + obj_program, + env, + 'bootloader', + 'embed/firmware/bootloaders/bootloader.o', + f'embed/firmware/bootloaders/bootloader_{BOOTLOADER_SUFFIX}.bin', + ) + env.Depends(obj_program, qstr_generated) diff --git a/core/embed/firmware/bl_check.c b/core/embed/firmware/bl_check.c index a3519ed9674..205093739eb 100644 --- a/core/embed/firmware/bl_check.c +++ b/core/embed/firmware/bl_check.c @@ -24,11 +24,15 @@ #include "common.h" #include "flash.h" #include "image.h" +#include "memzero.h" #include "model.h" +#include "uzlib.h" // symbols from bootloader.bin => bootloader.o -extern const void _binary_embed_firmware_bootloader_bin_start; -extern const void _binary_embed_firmware_bootloader_bin_size; +extern const void + _binary_embed_firmware_bootloaders_bootloader_bin_deflated_start; +extern const void + _binary_embed_firmware_bootloaders_bootloader_bin_deflated_size; /* static secbool known_bootloader(const uint8_t *hash, int len) { @@ -111,11 +115,31 @@ static secbool latest_bootloader(const uint8_t *hash, int len) { } #endif +#define UZLIB_WINDOW_SIZE (1 << 10) + +#if PRODUCTION || BOOTLOADER_QA +static void uzlib_prepare(struct uzlib_uncomp *decomp, uint8_t *window, + const void *src, uint32_t srcsize, void *dest, + uint32_t destsize) { + memzero(decomp, sizeof(struct uzlib_uncomp)); + if (window) { + memzero(window, UZLIB_WINDOW_SIZE); + } + memzero(dest, destsize); + decomp->source = (const uint8_t *)src; + decomp->source_limit = decomp->source + srcsize; + decomp->dest = (uint8_t *)dest; + decomp->dest_limit = decomp->dest + destsize; + uzlib_uncompress_init(decomp, window, window ? UZLIB_WINDOW_SIZE : 0); +} +#endif + void check_and_replace_bootloader(void) { #if PRODUCTION || BOOTLOADER_QA + // compute current bootloader hash uint8_t hash[BLAKE2S_DIGEST_LENGTH]; - const uint32_t bl_len = 128 * 1024; + const uint32_t bl_len = flash_area_get_size(&BOOTLOADER_AREA); const void *bl_data = flash_area_get_address(&BOOTLOADER_AREA, 0, bl_len); blake2s(bl_data, bl_len, hash, BLAKE2S_DIGEST_LENGTH); @@ -130,14 +154,25 @@ void check_and_replace_bootloader(void) { // replace bootloader with the latest one const uint32_t *data = - (const uint32_t *)&_binary_embed_firmware_bootloader_bin_start; + (const uint32_t + *)&_binary_embed_firmware_bootloaders_bootloader_bin_deflated_start; const uint32_t len = - (const uint32_t)&_binary_embed_firmware_bootloader_bin_size; + (const uint32_t)&_binary_embed_firmware_bootloaders_bootloader_bin_deflated_size; + + struct uzlib_uncomp decomp = {0}; + uint8_t decomp_window[UZLIB_WINDOW_SIZE] = {0}; + uint32_t decomp_out[IMAGE_HEADER_SIZE / sizeof(uint32_t)] = {0}; + + uzlib_prepare(&decomp, decomp_window, data, len, decomp_out, + sizeof(decomp_out)); + + ensure((uzlib_uncompress(&decomp) == TINF_OK) ? sectrue : secfalse, + "Bootloader header decompression failed"); const image_header *new_bld_hdr = read_image_header( - (uint8_t *)data, BOOTLOADER_IMAGE_MAGIC, BOOTLOADER_IMAGE_MAXSIZE); + (uint8_t *)decomp_out, BOOTLOADER_IMAGE_MAGIC, BOOTLOADER_IMAGE_MAXSIZE); - ensure(new_bld_hdr == (const image_header *)data ? sectrue : secfalse, + ensure(new_bld_hdr == (const image_header *)decomp_out ? sectrue : secfalse, "Invalid embedded bootloader"); ensure(check_image_model(new_bld_hdr), "Incompatible embedded bootloader"); @@ -177,16 +212,32 @@ void check_and_replace_bootloader(void) { ensure(flash_area_erase(&BOOTLOADER_AREA, NULL), NULL); ensure(flash_unlock_write(), NULL); - for (int i = 0; i < len / sizeof(uint32_t); i++) { - ensure( - flash_area_write_word(&BOOTLOADER_AREA, i * sizeof(uint32_t), data[i]), - NULL); - } - for (int i = len / sizeof(uint32_t); i < 128 * 1024 / sizeof(uint32_t); i++) { - ensure(flash_area_write_word(&BOOTLOADER_AREA, i * sizeof(uint32_t), - 0x00000000), - NULL); + + uint32_t offset = 0; + + do { + uint32_t *p = decomp_out; + uint32_t last_whole_word_addr = (((uint32_t)decomp.dest) & ~3); + while ((uint32_t)p < last_whole_word_addr) { + ensure(flash_area_write_word(&BOOTLOADER_AREA, offset, *p++), NULL); + offset += sizeof(uint32_t); + } + if ((uint8_t *)p < decomp.dest) { + // last few bytes in case of unaligned data + uint32_t d = 0; + memcpy(&d, p, (uint32_t)decomp.dest - (uint32_t)p); + ensure(flash_area_write_word(&BOOTLOADER_AREA, offset, d), NULL); + offset += sizeof(uint32_t); + } + decomp.dest = (uint8_t *)decomp_out; + } while (uzlib_uncompress(&decomp) >= 0); + + // fill the rest of the bootloader area with 0x00 + while (offset < bl_len) { + ensure(flash_area_write_word(&BOOTLOADER_AREA, offset, 0x00000000), NULL); + offset += sizeof(uint32_t); } + ensure(flash_lock_write(), NULL); #endif } diff --git a/core/site_scons/tools.py b/core/site_scons/tools.py index dccbb9f1d40..0cc34f0f266 100644 --- a/core/site_scons/tools.py +++ b/core/site_scons/tools.py @@ -1,6 +1,7 @@ from __future__ import annotations import subprocess +import zlib from pathlib import Path from boards import ( @@ -120,3 +121,51 @@ def get_defs_for_cmake(defs: list[str | tuple[str, str]]) -> list[str]: else: result.append(d) return result + + +def _compress(data: bytes) -> bytes: + z = zlib.compressobj(level=9, wbits=-10) + return z.compress(data) + z.flush() + + +def embed_binary(obj_program, env, section, target_, file): + _in = f"embedded_{section}.bin.deflated" + + def redefine_sym(name): + src = ( + "_binary_build_firmware_" + + _in.replace("/", "_").replace(".", "_") + + "_" + + name + ) + dest = ( + "_binary_" + + target_.replace("/", "_").replace(".o", "_bin_deflated") + + "_" + + name + ) + return f" --redefine-sym {src}={dest}" + + def compress_action(target, source, env): + srcf = Path(str(source[0])) + dstf = Path(str(target[0])) + compressed = _compress(srcf.read_bytes()) + dstf.write_bytes(compressed) + return 0 + + compress = env.Command(target=_in, source=file, action=compress_action) + + obj_program.extend( + env.Command( + target=target_, + source=_in, + action="$OBJCOPY -I binary -O elf32-littlearm -B arm" + f" --rename-section .data=.{section}" + + redefine_sym("start") + + redefine_sym("end") + + redefine_sym("size") + + " $SOURCE $TARGET", + ) + ) + + env.Depends(obj_program, compress) diff --git a/tools/check-bitcoin-only b/tools/check-bitcoin-only index 01f39279ca0..d1d695625a2 100755 --- a/tools/check-bitcoin-only +++ b/tools/check-bitcoin-only @@ -4,12 +4,8 @@ RETURN=0 EXCEPTIONS=() EXCEPTIONS+=( "decred" ) # "decred" figures in field names used by the bitcoin app EXCEPTIONS+=( "omni" ) # OMNI is part of the bitcoin app - # BIP39 or SLIP39 words that have "dash", "nem", or "flo" in them -EXCEPTIONS+=( "cinema" "dash" "enemy" "float" "flock" "flower" "floor" "floral" ) -EXCEPTIONS+=( "mnemonic" ) # has NEM in it -EXCEPTIONS+=( "workflow" "overflow" ) # has Flo in it -EXCEPTIONS+=( "SyntaxError" ) # has Axe in it -EXCEPTIONS+=( "DKDNEM" ) # has NEM in it, some sort of weird coincidence + # BIP39 or SLIP39 words that have "dash" in them +EXCEPTIONS+=( "dash" ) EXCEPTIONS+=( "confirm_ethereum_tx" ) # is model-specific, so is in layout/__init__.py instead of ethereum/layout.py GREP_ARGS=() @@ -17,8 +13,8 @@ for exception in "${EXCEPTIONS[@]}"; do GREP_ARGS+=(-e $exception) done -# dump all coins except the first 3 (Bitcoin, Testnet, Regtest) -ALTCOINS=$(./common/tools/cointool.py dump -l -p -t -d T1B1 -d T2T1 -d T2B1 | grep '"name"' | cut -d '"' -f 4 | tail -n +4) +# dump all coins except the first 3 (Bitcoin, Testnet, Regtest), exclude names with less than 4 characters +ALTCOINS=$(./common/tools/cointool.py dump -l -p -t -d T1B1 -d T2T1 -d T2B1 | grep '"name"' | cut -d '"' -f 4 | tail -n +4 | awk 'length($0)>3') # split on newlines only OLDIFS=$IFS IFS="