diff --git a/core/SConscript.boardloader b/core/SConscript.boardloader index 77548da7f43..696ba852971 100644 --- a/core/SConscript.boardloader +++ b/core/SConscript.boardloader @@ -50,6 +50,7 @@ SOURCE_MOD += [ 'vendor/trezor-crypto/ed25519-donna/modm-donna-32bit.c', 'vendor/trezor-crypto/memzero.c', 'vendor/trezor-crypto/sha2.c', + 'vendor/trezor-storage/flash_area.c', ] # modtrezorui diff --git a/core/SConscript.bootloader b/core/SConscript.bootloader index f42ade77355..a63d80714ba 100644 --- a/core/SConscript.bootloader +++ b/core/SConscript.bootloader @@ -71,6 +71,7 @@ SOURCE_MOD += [ 'vendor/trezor-crypto/memzero.c', 'vendor/trezor-crypto/rand.c', 'vendor/trezor-crypto/sha2.c', + 'vendor/trezor-storage/flash_area.c', ] # modtrezorui diff --git a/core/SConscript.bootloader_ci b/core/SConscript.bootloader_ci index d0cd4d676e4..4a695bae294 100644 --- a/core/SConscript.bootloader_ci +++ b/core/SConscript.bootloader_ci @@ -66,6 +66,7 @@ SOURCE_MOD += [ 'vendor/trezor-crypto/memzero.c', 'vendor/trezor-crypto/rand.c', 'vendor/trezor-crypto/sha2.c', + 'vendor/trezor-storage/flash_area.c', ] # modtrezorui diff --git a/core/SConscript.bootloader_emu b/core/SConscript.bootloader_emu index 90d13ad6f96..a16c535d7e7 100644 --- a/core/SConscript.bootloader_emu +++ b/core/SConscript.bootloader_emu @@ -89,13 +89,9 @@ SOURCE_MOD += [ 'vendor/micropython/lib/uzlib/adler32.c', 'vendor/micropython/lib/uzlib/crc32.c', 'vendor/micropython/lib/uzlib/tinflate.c', + 'vendor/trezor-storage/flash_area.c', ] -if TREZOR_MODEL in ('1', 'T', 'R', 'DISC1'): - SOURCE_MOD += [ - 'vendor/trezor-storage/flash_common_f4.c', - ] - if TREZOR_MODEL in ('1', ): SOURCE_MOD += [ 'embed/models/model_T1B1_layout.c', @@ -129,6 +125,7 @@ SOURCE_TREZORHAL = [ 'embed/trezorhal/unix/display-unix.c', 'embed/trezorhal/unix/fault_handlers.c', 'embed/trezorhal/unix/flash.c', + 'embed/trezorhal/unix/flash_otp.c', 'embed/trezorhal/unix/common.c', 'embed/trezorhal/unix/touch/touch.c', 'embed/trezorhal/unix/rng.c', diff --git a/core/SConscript.firmware b/core/SConscript.firmware index ed971858258..579c48d8e64 100644 --- a/core/SConscript.firmware +++ b/core/SConscript.firmware @@ -57,6 +57,7 @@ SOURCE_MOD += [ 'embed/extmod/modtrezorconfig/modtrezorconfig.c', 'vendor/trezor-storage/norcow.c', 'vendor/trezor-storage/storage.c', + 'vendor/trezor-storage/flash_area.c', ] # modtrezorcrypto diff --git a/core/SConscript.prodtest b/core/SConscript.prodtest index 901c1e56ed8..b3260e9cbbc 100644 --- a/core/SConscript.prodtest +++ b/core/SConscript.prodtest @@ -69,6 +69,7 @@ SOURCE_MOD += [ 'vendor/trezor-crypto/secp256k1.c', 'vendor/trezor-crypto/sha2.c', 'vendor/trezor-crypto/tls_prf.c', + 'vendor/trezor-storage/flash_area.c', ] # modtrezorui diff --git a/core/SConscript.reflash b/core/SConscript.reflash index dab756070e6..d5a286731f6 100644 --- a/core/SConscript.reflash +++ b/core/SConscript.reflash @@ -63,6 +63,7 @@ SOURCE_MOD += [ 'vendor/micropython/lib/uzlib/adler32.c', 'vendor/micropython/lib/uzlib/crc32.c', 'vendor/micropython/lib/uzlib/tinflate.c', + 'vendor/trezor-storage/flash_area.c', ] # fonts diff --git a/core/SConscript.unix b/core/SConscript.unix index cd995e38930..e394c4ab878 100644 --- a/core/SConscript.unix +++ b/core/SConscript.unix @@ -58,13 +58,9 @@ SOURCE_MOD += [ 'embed/extmod/modtrezorconfig/modtrezorconfig.c', 'vendor/trezor-storage/norcow.c', 'vendor/trezor-storage/storage.c', + 'vendor/trezor-storage/flash_area.c', ] -if TREZOR_MODEL in ('1', 'T', 'R', 'DISC1'): - SOURCE_MOD += [ - 'vendor/trezor-storage/flash_common_f4.c', - ] - # modtrezorcrypto CCFLAGS_MOD += '-Wno-sequence-point ' CPPPATH_MOD += [ @@ -376,6 +372,7 @@ SOURCE_UNIX = [ 'embed/trezorhal/unix/common.c', 'embed/trezorhal/unix/display-unix.c', 'embed/trezorhal/unix/flash.c', + 'embed/trezorhal/unix/flash_otp.c', 'embed/trezorhal/unix/random_delays.c', 'embed/trezorhal/unix/rng.c', 'embed/trezorhal/unix/usb.c', diff --git a/core/embed/bootloader/emulator.c b/core/embed/bootloader/emulator.c index 6b089ee39ae..f050fde6417 100644 --- a/core/embed/bootloader/emulator.c +++ b/core/embed/bootloader/emulator.c @@ -7,6 +7,7 @@ #include "common.h" #include "display.h" #include "flash.h" +#include "flash_otp.h" #include "model.h" #include "rust_ui.h" #ifdef USE_OPTIGA @@ -111,6 +112,8 @@ __attribute__((noreturn)) void display_error_and_die(const char *message, __attribute__((noreturn)) int main(int argc, char **argv) { flash_init(); + flash_otp_init(); + FIRMWARE_START = (uint8_t *)flash_area_get_address(&FIRMWARE_AREA, 0, 0); // simulate non-empty storage so that we know whether it was erased or not diff --git a/core/embed/bootloader/main.c b/core/embed/bootloader/main.c index f147b7ce7c8..a1963700d82 100644 --- a/core/embed/bootloader/main.c +++ b/core/embed/bootloader/main.c @@ -25,6 +25,7 @@ #include "display.h" #include "fault_handlers.h" #include "flash.h" +#include "flash_otp.h" #include "image.h" #include "lowlevel.h" #include "messages.pb.h" diff --git a/core/embed/bootloader_ci/main.c b/core/embed/bootloader_ci/main.c index 8285a99af7d..6ad597d6bbc 100644 --- a/core/embed/bootloader_ci/main.c +++ b/core/embed/bootloader_ci/main.c @@ -23,6 +23,7 @@ #include "common.h" #include "display.h" #include "flash.h" +#include "flash_otp.h" #include "image.h" #include "mini_printf.h" #include "mpu.h" diff --git a/core/embed/extmod/modtrezorio/modtrezorio-flash.h b/core/embed/extmod/modtrezorio/modtrezorio-flash.h index 2c2c955d9ea..3815f1e7124 100644 --- a/core/embed/extmod/modtrezorio/modtrezorio-flash.h +++ b/core/embed/extmod/modtrezorio/modtrezorio-flash.h @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "flash.h" +#include "flash_otp.h" #include "embed/extmod/trezorobj.h" diff --git a/core/embed/lib/unit_variant.c b/core/embed/lib/unit_variant.c index 8e1b51a04d6..4cc3b567a8e 100644 --- a/core/embed/lib/unit_variant.c +++ b/core/embed/lib/unit_variant.c @@ -1,5 +1,5 @@ #include "unit_variant.h" -#include "flash.h" +#include "flash_otp.h" #include "model.h" static uint8_t unit_variant_color = 0; diff --git a/core/embed/models/layout_common.h b/core/embed/models/layout_common.h index bf887529ff7..c581b1bb509 100644 --- a/core/embed/models/layout_common.h +++ b/core/embed/models/layout_common.h @@ -1,7 +1,7 @@ #ifndef LAYOUT_COMMON_H #define LAYOUT_COMMON_H -#include "flash.h" +#include "flash_area.h" // OTP blocks allocation #define FLASH_OTP_BLOCK_BATCH 0 diff --git a/core/embed/models/model_D002_layout.c b/core/embed/models/model_D002_layout.c index eae23b69993..283e1f3f0fc 100644 --- a/core/embed/models/model_D002_layout.c +++ b/core/embed/models/model_D002_layout.c @@ -4,7 +4,6 @@ const flash_area_t STORAGE_AREAS[STORAGE_AREAS_COUNT] = { { .num_subareas = 1, - .secure_area = true, .subarea[0] = { .first_sector = 0x18, @@ -13,7 +12,6 @@ const flash_area_t STORAGE_AREAS[STORAGE_AREAS_COUNT] = { }, { .num_subareas = 1, - .secure_area = true, .subarea[0] = { .first_sector = 0x20, @@ -24,7 +22,6 @@ const flash_area_t STORAGE_AREAS[STORAGE_AREAS_COUNT] = { const flash_area_t BOARDLOADER_AREA = { .num_subareas = 1, - .secure_area = true, .subarea[0] = { .first_sector = 1, @@ -34,7 +31,6 @@ const flash_area_t BOARDLOADER_AREA = { const flash_area_t BOOTLOADER_AREA = { .num_subareas = 1, - .secure_area = true, .subarea[0] = { .first_sector = 0x08, @@ -44,7 +40,6 @@ const flash_area_t BOOTLOADER_AREA = { const flash_area_t FIRMWARE_AREA = { .num_subareas = 1, - .secure_area = true, .subarea[0] = { .first_sector = 0x28, @@ -54,7 +49,6 @@ const flash_area_t FIRMWARE_AREA = { const flash_area_t SECRET_AREA = { .num_subareas = 1, - .secure_area = true, .subarea[0] = { .first_sector = 0, @@ -64,7 +58,6 @@ const flash_area_t SECRET_AREA = { const flash_area_t BHK_AREA = { .num_subareas = 1, - .secure_area = true, .subarea[0] = { .first_sector = 1, @@ -74,7 +67,6 @@ const flash_area_t BHK_AREA = { const flash_area_t WIPE_AREA = { .num_subareas = 1, - .secure_area = true, .subarea[0] = { .first_sector = 0x18, @@ -84,7 +76,6 @@ const flash_area_t WIPE_AREA = { const flash_area_t ALL_WIPE_AREA = { .num_subareas = 1, - .secure_area = true, .subarea[0] = { .first_sector = 0x08, diff --git a/core/embed/prodtest/main.c b/core/embed/prodtest/main.c index 2698249afe1..3728272e370 100644 --- a/core/embed/prodtest/main.c +++ b/core/embed/prodtest/main.c @@ -28,6 +28,7 @@ #include "display.h" #include "fault_handlers.h" #include "flash.h" +#include "flash_otp.h" #include "i2c.h" #include "model.h" #include "mpu.h" diff --git a/core/embed/trezorhal/flash.h b/core/embed/trezorhal/flash.h index 11b50f8c09f..2918b11ee55 100644 --- a/core/embed/trezorhal/flash.h +++ b/core/embed/trezorhal/flash.h @@ -22,50 +22,12 @@ #include #include + #include "platform.h" #include "secbool.h" -#define FLASH_OTP_NUM_BLOCKS 16 -#define FLASH_OTP_BLOCK_SIZE 32 - -/** - * Flash driver interface is designed to abstract away differences between - * various MCUs used in Trezor devices. - * - * Generally, flash memory is divided into sectors. On different MCUs, sectors - * may have different sizes, and therefore, different number of sectors are used - * for a given purpose. For example, on STM32F4, the sectors are relatively - * large so we use single sector for Storage. On STM32U5, the sectors are - * smaller, so we use multiple sectors for the Storage. Storage implementation - * should not care about this, and should use flash_area_t interface to access - * the flash memory. - * - * flash_area_t represents a location in flash memory. It may be contiguous, or - * it may be composed of multiple non-contiguous subareas. - * - * flash_subarea_t represents a contiguous area in flash memory, specified by - * first_sector and num_sectors. - */ - -#include "flash_common.h" +#include "flash_ll.h" void flash_init(void); -uint32_t flash_wait_and_clear_status_flags(void); - -// Erases the single sector in the designated flash area -// The 'offset' parameter must indicate the relative sector offset within the -// flash area If 'offset' is outside the bounds of the flash area, -// 'bytes_erased' is set to 0 otherwise, 'bytes_erased' is set to the size of -// the erased sector -secbool flash_area_erase_partial(const flash_area_t *area, uint32_t offset, - uint32_t *bytes_erased); - -secbool __wur flash_otp_read(uint8_t block, uint8_t offset, uint8_t *data, - uint8_t datalen); -secbool __wur flash_otp_write(uint8_t block, uint8_t offset, - const uint8_t *data, uint8_t datalen); -secbool __wur flash_otp_lock(uint8_t block); -secbool __wur flash_otp_is_locked(uint8_t block); - #endif // TREZORHAL_FLASH_H diff --git a/core/embed/trezorhal/flash_otp.h b/core/embed/trezorhal/flash_otp.h new file mode 100644 index 00000000000..c3f8705ab4a --- /dev/null +++ b/core/embed/trezorhal/flash_otp.h @@ -0,0 +1,18 @@ +#ifndef TREZORHAL_FLASH_OTP_H +#define TREZORHAL_FLASH_OTP_H + +#include + +#define FLASH_OTP_NUM_BLOCKS 16 +#define FLASH_OTP_BLOCK_SIZE 32 + +void flash_otp_init(void); + +secbool __wur flash_otp_read(uint8_t block, uint8_t offset, uint8_t *data, + uint8_t datalen); +secbool __wur flash_otp_write(uint8_t block, uint8_t offset, + const uint8_t *data, uint8_t datalen); +secbool __wur flash_otp_lock(uint8_t block); +secbool __wur flash_otp_is_locked(uint8_t block); + +#endif // TREZORHAL_FLASH_OTP_H diff --git a/core/embed/trezorhal/stm32f4/common.c b/core/embed/trezorhal/stm32f4/common.c index 427f10b0be7..d4c5866fac6 100644 --- a/core/embed/trezorhal/stm32f4/common.c +++ b/core/embed/trezorhal/stm32f4/common.c @@ -28,7 +28,7 @@ #ifdef FANCY_FATAL_ERROR #include "rust_ui.h" #endif -#include "flash.h" +#include "flash_otp.h" #include "platform.h" #include "rand.h" #include "supervise.h" diff --git a/core/embed/trezorhal/stm32f4/flash.c b/core/embed/trezorhal/stm32f4/flash.c index a577c1d774f..0df6eca037d 100644 --- a/core/embed/trezorhal/stm32f4/flash.c +++ b/core/embed/trezorhal/stm32f4/flash.c @@ -78,26 +78,6 @@ static const uint32_t FLASH_SECTOR_TABLE[FLASH_SECTOR_COUNT + 1] = { #endif }; -uint32_t flash_wait_and_clear_status_flags(void) { - while (FLASH->SR & FLASH_SR_BSY) - ; // wait for all previous flash operations to complete - const uint32_t result = - FLASH->SR & FLASH_STATUS_ALL_FLAGS; // get the current status flags - FLASH->SR |= FLASH_STATUS_ALL_FLAGS; // clear all status flags - return result; -} - -secbool flash_unlock_write(void) { - HAL_FLASH_Unlock(); - FLASH->SR |= FLASH_STATUS_ALL_FLAGS; // clear all status flags - return sectrue; -} - -secbool flash_lock_write(void) { - HAL_FLASH_Lock(); - return sectrue; -} - const void *flash_get_address(uint16_t sector, uint32_t offset, uint32_t size) { if (sector >= FLASH_SECTOR_COUNT) { return NULL; @@ -110,105 +90,71 @@ const void *flash_get_address(uint16_t sector, uint32_t offset, uint32_t size) { return (const void *)addr; } -uint32_t flash_sector_size(uint16_t sector) { - if (sector >= FLASH_SECTOR_COUNT) { +uint32_t flash_sector_size(uint16_t first_sector, uint16_t sector_count) { + if (first_sector + sector_count > FLASH_SECTOR_COUNT) { return 0; } - return FLASH_SECTOR_TABLE[sector + 1] - FLASH_SECTOR_TABLE[sector]; + return FLASH_SECTOR_TABLE[first_sector + sector_count] - + FLASH_SECTOR_TABLE[first_sector]; } -secbool flash_area_erase_bulk(const flash_area_t *area, int count, - void (*progress)(int pos, int len)) { - ensure(flash_unlock_write(), NULL); - FLASH_EraseInitTypeDef EraseInitStruct = {0}; - EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS; - EraseInitStruct.VoltageRange = FLASH_VOLTAGE_RANGE_3; - EraseInitStruct.NbSectors = 1; +uint16_t flash_sector_find(uint16_t first_sector, uint32_t offset) { + uint16_t sector = first_sector; - int total_sectors = 0; - int done_sectors = 0; - for (int a = 0; a < count; a++) { - for (int i = 0; i < area[a].num_subareas; i++) { - total_sectors += area[a].subarea[i].num_sectors; + while (sector < FLASH_SECTOR_COUNT) { + uint32_t sector_size = + FLASH_SECTOR_TABLE[sector + 1] - FLASH_SECTOR_TABLE[sector]; + + if (offset < sector_size) { + break; } - } - if (progress) { - progress(0, total_sectors); + offset -= sector_size; + sector++; } - for (int a = 0; a < count; a++) { - for (int s = 0; s < area[a].num_subareas; s++) { - for (int i = 0; i < area[a].subarea[s].num_sectors; i++) { - int sector = area[a].subarea[s].first_sector + i; + return sector; +} - EraseInitStruct.Sector = sector; - uint32_t SectorError; - if (HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError) != HAL_OK) { - ensure(flash_lock_write(), NULL); - return secfalse; - } - // check whether the sector was really deleted (contains only 0xFF) - const uint32_t addr_start = FLASH_SECTOR_TABLE[sector], - addr_end = FLASH_SECTOR_TABLE[sector + 1]; - for (uint32_t addr = addr_start; addr < addr_end; addr += 4) { - if (*((const uint32_t *)addr) != 0xFFFFFFFF) { - ensure(flash_lock_write(), NULL); - return secfalse; - } - } - done_sectors++; - if (progress) { - progress(done_sectors, total_sectors); - } - } - } - } - ensure(flash_lock_write(), NULL); +secbool flash_unlock_write(void) { + HAL_FLASH_Unlock(); + FLASH->SR |= FLASH_STATUS_ALL_FLAGS; // clear all status flags return sectrue; } -secbool flash_area_erase_partial(const flash_area_t *area, uint32_t offset, - uint32_t *bytes_erased) { - uint32_t sector_offset = 0; - *bytes_erased = 0; - - for (int s = 0; s < area->num_subareas; s++) { - for (int i = 0; i < area->subarea[s].num_sectors; i++) { - uint32_t sector_index = area->subarea[s].first_sector + i; - uint32_t sector_size = FLASH_SECTOR_TABLE[sector_index + 1] - - FLASH_SECTOR_TABLE[sector_index]; - - if (offset == sector_offset) { - ensure(flash_unlock_write(), NULL); +secbool flash_lock_write(void) { + HAL_FLASH_Lock(); + return sectrue; +} - FLASH_EraseInitTypeDef erase_init = { - .TypeErase = FLASH_TYPEERASE_SECTORS, - .VoltageRange = FLASH_VOLTAGE_RANGE_3, - .Sector = sector_index, - .NbSectors = 1}; +secbool flash_sector_erase(uint16_t sector) { + if (sector >= FLASH_SECTOR_COUNT) { + return secfalse; + } - uint32_t sector_error; + FLASH_EraseInitTypeDef EraseInitStruct = { + .TypeErase = FLASH_TYPEERASE_SECTORS, + .VoltageRange = FLASH_VOLTAGE_RANGE_3, + .Sector = sector, + .NbSectors = 1, + }; - if (HAL_FLASHEx_Erase(&erase_init, §or_error) != HAL_OK) { - ensure(flash_lock_write(), NULL); - return secfalse; - } + uint32_t sector_error; - ensure(flash_lock_write(), NULL); + if (HAL_FLASHEx_Erase(&EraseInitStruct, §or_error) != HAL_OK) { + return secfalse; + } - *bytes_erased = sector_size; - return sectrue; - } + // check whether the sector was really deleted (contains only 0xFF) + uint32_t addr_start = FLASH_SECTOR_TABLE[sector]; + uint32_t addr_end = FLASH_SECTOR_TABLE[sector + 1]; - sector_offset += sector_size; + for (uint32_t addr = addr_start; addr < addr_end; addr += 4) { + if (*((const uint32_t *)addr) != 0xFFFFFFFF) { + return secfalse; } } - if (offset == sector_offset) { - return sectrue; - } - - return secfalse; + return sectrue; } secbool flash_write_byte(uint16_t sector, uint32_t offset, uint8_t data) { @@ -247,51 +193,3 @@ secbool flash_write_word(uint16_t sector, uint32_t offset, uint32_t data) { } return sectrue; } - -#define FLASH_OTP_LOCK_BASE 0x1FFF7A00U - -secbool flash_otp_read(uint8_t block, uint8_t offset, uint8_t *data, - uint8_t datalen) { - if (block >= FLASH_OTP_NUM_BLOCKS || - offset + datalen > FLASH_OTP_BLOCK_SIZE) { - return secfalse; - } - for (uint8_t i = 0; i < datalen; i++) { - data[i] = *(__IO uint8_t *)(FLASH_OTP_BASE + block * FLASH_OTP_BLOCK_SIZE + - offset + i); - } - return sectrue; -} - -secbool flash_otp_write(uint8_t block, uint8_t offset, const uint8_t *data, - uint8_t datalen) { - if (block >= FLASH_OTP_NUM_BLOCKS || - offset + datalen > FLASH_OTP_BLOCK_SIZE) { - return secfalse; - } - ensure(flash_unlock_write(), NULL); - for (uint8_t i = 0; i < datalen; i++) { - uint32_t address = - FLASH_OTP_BASE + block * FLASH_OTP_BLOCK_SIZE + offset + i; - ensure(sectrue * (HAL_OK == HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, - address, data[i])), - NULL); - } - ensure(flash_lock_write(), NULL); - return sectrue; -} - -secbool flash_otp_lock(uint8_t block) { - if (block >= FLASH_OTP_NUM_BLOCKS) { - return secfalse; - } - ensure(flash_unlock_write(), NULL); - HAL_StatusTypeDef ret = HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, - FLASH_OTP_LOCK_BASE + block, 0x00); - ensure(flash_lock_write(), NULL); - return sectrue * (ret == HAL_OK); -} - -secbool flash_otp_is_locked(uint8_t block) { - return sectrue * (0x00 == *(__IO uint8_t *)(FLASH_OTP_LOCK_BASE + block)); -} diff --git a/core/embed/trezorhal/stm32f4/flash_otp.c b/core/embed/trezorhal/stm32f4/flash_otp.c new file mode 100644 index 00000000000..28fbb34ffbf --- /dev/null +++ b/core/embed/trezorhal/stm32f4/flash_otp.c @@ -0,0 +1,76 @@ +/* + * 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 STM32_HAL_H + +#include "flash_otp.h" +#include "common.h" +#include "flash.h" + +#define FLASH_OTP_LOCK_BASE 0x1FFF7A00U + +void flash_otp_init() { + // intentionally left empty +} + +secbool flash_otp_read(uint8_t block, uint8_t offset, uint8_t *data, + uint8_t datalen) { + if (block >= FLASH_OTP_NUM_BLOCKS || + offset + datalen > FLASH_OTP_BLOCK_SIZE) { + return secfalse; + } + for (uint8_t i = 0; i < datalen; i++) { + data[i] = *(__IO uint8_t *)(FLASH_OTP_BASE + block * FLASH_OTP_BLOCK_SIZE + + offset + i); + } + return sectrue; +} + +secbool flash_otp_write(uint8_t block, uint8_t offset, const uint8_t *data, + uint8_t datalen) { + if (block >= FLASH_OTP_NUM_BLOCKS || + offset + datalen > FLASH_OTP_BLOCK_SIZE) { + return secfalse; + } + ensure(flash_unlock_write(), NULL); + for (uint8_t i = 0; i < datalen; i++) { + uint32_t address = + FLASH_OTP_BASE + block * FLASH_OTP_BLOCK_SIZE + offset + i; + ensure(sectrue * (HAL_OK == HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, + address, data[i])), + NULL); + } + ensure(flash_lock_write(), NULL); + return sectrue; +} + +secbool flash_otp_lock(uint8_t block) { + if (block >= FLASH_OTP_NUM_BLOCKS) { + return secfalse; + } + ensure(flash_unlock_write(), NULL); + HAL_StatusTypeDef ret = HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, + FLASH_OTP_LOCK_BASE + block, 0x00); + ensure(flash_lock_write(), NULL); + return sectrue * (ret == HAL_OK); +} + +secbool flash_otp_is_locked(uint8_t block) { + return sectrue * (0x00 == *(__IO uint8_t *)(FLASH_OTP_LOCK_BASE + block)); +} diff --git a/core/embed/trezorhal/stm32f4/lowlevel.c b/core/embed/trezorhal/stm32f4/lowlevel.c index 6170e69fbaf..31879238a0b 100644 --- a/core/embed/trezorhal/stm32f4/lowlevel.c +++ b/core/embed/trezorhal/stm32f4/lowlevel.c @@ -20,7 +20,7 @@ #include STM32_HAL_H #include "lowlevel.h" -#include "flash.h" +#include "flash_otp.h" #pragma GCC optimize( \ "no-stack-protector") // applies to all functions in this file @@ -57,6 +57,19 @@ #define OPTION_BYTES_BANK1_WRP (*(volatile uint16_t* const)0x1FFFC008U) #define OPTION_BYTES_BANK2_WRP (*(volatile uint16_t* const)0x1FFEC008U) +#define FLASH_STATUS_ALL_FLAGS \ + (FLASH_SR_RDERR | FLASH_SR_PGSERR | FLASH_SR_PGPERR | FLASH_SR_PGAERR | \ + FLASH_SR_WRPERR | FLASH_SR_SOP | FLASH_SR_EOP) + +static uint32_t flash_wait_and_clear_status_flags(void) { + while (FLASH->SR & FLASH_SR_BSY) + ; // wait for all previous flash operations to complete + const uint32_t result = + FLASH->SR & FLASH_STATUS_ALL_FLAGS; // get the current status flags + FLASH->SR |= FLASH_STATUS_ALL_FLAGS; // clear all status flags + return result; +} + secbool flash_check_option_bytes(void) { flash_wait_and_clear_status_flags(); // check values stored in flash interface registers diff --git a/core/embed/trezorhal/stm32u5/common.c b/core/embed/trezorhal/stm32u5/common.c index c0cda4ce259..00da12043e6 100644 --- a/core/embed/trezorhal/stm32u5/common.c +++ b/core/embed/trezorhal/stm32u5/common.c @@ -27,7 +27,7 @@ #ifdef FANCY_FATAL_ERROR #include "rust_ui.h" #endif -#include "flash.h" +#include "flash_otp.h" #include "model.h" #include "platform.h" #include "rand.h" diff --git a/core/embed/trezorhal/stm32u5/flash.c b/core/embed/trezorhal/stm32u5/flash.c index bce7e4511c6..a20cc579ade 100644 --- a/core/embed/trezorhal/stm32u5/flash.c +++ b/core/embed/trezorhal/stm32u5/flash.c @@ -19,6 +19,7 @@ #include STM32_HAL_H +#include #include #include "common.h" @@ -33,329 +34,100 @@ #define FLASH_STATUS_ALL_FLAGS \ (FLASH_NSSR_PGSERR | FLASH_NSSR_PGAERR | FLASH_NSSR_WRPERR | FLASH_NSSR_EOP) -uint32_t flash_wait_and_clear_status_flags(void) { - while (FLASH->NSSR & FLASH_NSSR_BSY) - ; // wait for all previous flash operations to complete - - uint32_t result = - FLASH->NSSR & FLASH_STATUS_ALL_FLAGS; // get the current status flags - FLASH->NSSR |= FLASH_STATUS_ALL_FLAGS; // clear all status flags - -#if defined(__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) - while (FLASH->SECSR & FLASH_SECSR_BSY) - ; // wait for all previous flash operations to complete - result |= - FLASH->SECSR & FLASH_STATUS_ALL_FLAGS; // get the current status flags - FLASH->SECSR |= FLASH_STATUS_ALL_FLAGS; // clear all status flags -#endif - return result; -} - -secbool flash_unlock_write(void) { - HAL_FLASH_Unlock(); - FLASH->NSSR |= FLASH_STATUS_ALL_FLAGS; // clear all status flags -#if defined(__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) - FLASH->SECSR |= FLASH_STATUS_ALL_FLAGS; // clear all status flags -#endif - return sectrue; +static bool flash_sector_is_secure(uint32_t sector) { + // We always return true since the entire flash memory is currently secure - + // partially through option bytes and partially through FLASH controller + // settings + return true; } -secbool flash_lock_write(void) { - HAL_FLASH_Lock(); - return sectrue; -} - -const void *flash_get_address(uint16_t sector, uint32_t offset, uint32_t size, - bool secure_area) { +const void *flash_get_address(uint16_t sector, uint32_t offset, uint32_t size) { if (sector >= FLASH_SECTOR_COUNT) { return NULL; } - uint32_t base_addr = FLASH_START_ADDRESS; - if (secure_area) { - base_addr = FLASH_SEC_START_ADDRESS; - } - - const uint32_t addr = base_addr + (FLASH_PAGE_SIZE * sector) + offset; - const uint32_t next = base_addr + (FLASH_PAGE_SIZE * (sector + 1)); - if (addr + size > next) { + if (offset + size > FLASH_PAGE_SIZE) { return NULL; } - return (const void *)addr; -} -uint32_t flash_sector_size(uint16_t sector) { - if (sector >= FLASH_SECTOR_COUNT) { - return 0; - } - return FLASH_PAGE_SIZE; -} + uint32_t base_addr = flash_sector_is_secure(sector) ? FLASH_SEC_START_ADDRESS + : FLASH_START_ADDRESS; -uint32_t flash_subarea_get_size(const flash_subarea_t *sub) { - return FLASH_PAGE_SIZE * sub->num_sectors; + return (const void *)(base_addr + FLASH_PAGE_SIZE * sector + offset); } -uint32_t flash_area_get_size(const flash_area_t *area) { - uint32_t size = 0; - for (int i = 0; i < area->num_subareas; i++) { - size += flash_subarea_get_size(&area->subarea[i]); +uint32_t flash_sector_size(uint16_t first_sector, uint16_t sector_count) { + if (first_sector + sector_count > FLASH_SECTOR_COUNT) { + return 0; } - return size; + return FLASH_PAGE_SIZE * sector_count; } -uint16_t flash_total_sectors(const flash_area_t *area) { - uint16_t total = 0; - for (int i = 0; i < area->num_subareas; i++) { - total += area->subarea[i].num_sectors; - } - return total; +uint16_t flash_sector_find(uint16_t first_sector, uint32_t offset) { + return first_sector + offset / FLASH_PAGE_SIZE; } -const void *flash_area_get_address(const flash_area_t *area, uint32_t offset, - uint32_t size) { - uint32_t tmp_offset = offset; - - for (int i = 0; i < area->num_subareas; i++) { - uint16_t sector = area->subarea[i].first_sector; - - if (tmp_offset >= flash_subarea_get_size(area->subarea)) { - tmp_offset -= flash_subarea_get_size(area->subarea); - continue; - } - - uint32_t base_addr = FLASH_START_ADDRESS; - - if (area->secure_area) { - base_addr = FLASH_SEC_START_ADDRESS; - } - - const uint32_t addr = base_addr + (FLASH_PAGE_SIZE * sector) + tmp_offset; - const uint32_t area_end = - base_addr + (FLASH_PAGE_SIZE * (area->subarea[i].first_sector + - area->subarea[i].num_sectors)); - if (addr + size > area_end) { - return NULL; - } - return (const void *)addr; - } - return NULL; +secbool flash_unlock_write(void) { + HAL_FLASH_Unlock(); + FLASH->NSSR |= FLASH_STATUS_ALL_FLAGS; // clear all status flags +#if defined(__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) + FLASH->SECSR |= FLASH_STATUS_ALL_FLAGS; // clear all status flags +#endif + return sectrue; } -int32_t flash_get_sector_num(const flash_area_t *area, - uint32_t sector_inner_num) { - uint16_t sector = 0; - uint16_t remaining = sector_inner_num; - for (int i = 0; i < area->num_subareas; i++) { - if (remaining < area->subarea[i].num_sectors) { - sector = area->subarea[i].first_sector + remaining; - return sector; - } else { - remaining -= area->subarea[i].num_sectors; - } - } - - return -1; +secbool flash_lock_write(void) { + HAL_FLASH_Lock(); + return sectrue; } -secbool flash_area_erase(const flash_area_t *area, - void (*progress)(int pos, int len)) { - ensure(flash_unlock_write(), NULL); - FLASH_EraseInitTypeDef EraseInitStruct; - EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES; - EraseInitStruct.NbPages = 1; - - int total_pages = 0; - int done_pages = 0; - for (int i = 0; i < area->num_subareas; i++) { - total_pages += area->subarea[i].num_sectors; +secbool flash_sector_erase(uint16_t sector) { + if (sector >= FLASH_SECTOR_COUNT) { + return secfalse; } - if (progress) { - progress(0, total_pages); - } + FLASH_EraseInitTypeDef EraseInitStruct = { + .TypeErase = FLASH_TYPEERASE_PAGES_NS, + .Banks = FLASH_BANK_1, + .Page = sector, + .NbPages = 1, + }; - for (int s = 0; s < area->num_subareas; s++) { - for (int i = 0; i < area->subarea[s].num_sectors; i++) { - int page = area->subarea[s].first_sector + i; - uint32_t sec_start = 0; - uint32_t sec_end = 0; - uint32_t page_idx = 0; - bool secure = area->secure_area; - if (page >= 256) { - EraseInitStruct.Banks = FLASH_BANK_2; - page_idx = page - 256; - sec_start = (FLASH->SECWM2R1 & FLASH_SECWM1R1_SECWM1_PSTRT) >> - FLASH_SECWM1R1_SECWM1_PSTRT_Pos; - sec_end = (FLASH->SECWM2R1 & FLASH_SECWM1R1_SECWM1_PEND) >> - FLASH_SECWM1R1_SECWM1_PEND_Pos; - } else { - EraseInitStruct.Banks = FLASH_BANK_1; - page_idx = page; - sec_start = (FLASH->SECWM1R1 & FLASH_SECWM1R1_SECWM1_PSTRT) >> - FLASH_SECWM1R1_SECWM1_PSTRT_Pos; - sec_end = (FLASH->SECWM1R1 & FLASH_SECWM1R1_SECWM1_PEND) >> - FLASH_SECWM1R1_SECWM1_PEND_Pos; - } - - if (page_idx < sec_start || page_idx > sec_end) { - EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES_NS; - secure = false; - } else { - EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES; - } - - EraseInitStruct.Page = page_idx; - uint32_t SectorError; - if (HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError) != HAL_OK) { - ensure(flash_lock_write(), NULL); - return secfalse; - } - // check whether the sector was really deleted (contains only 0xFF) - const uint32_t addr_start = - (uint32_t)flash_get_address(page, 0, 0, secure); - const uint32_t addr_end = - (uint32_t)flash_get_address(page + 1, 0, 0, secure); - - for (uint32_t addr = addr_start; addr < addr_end; addr += 4) { - if (*((const uint32_t *)addr) != 0xFFFFFFFF) { - ensure(flash_lock_write(), NULL); - return secfalse; - } - } - done_pages++; - if (progress && done_pages % 16 == 0) { - progress(done_pages, total_pages); - } - } + if (sector >= 256) { + EraseInitStruct.Banks = FLASH_BANK_2; + EraseInitStruct.Page = sector - 256; } - ensure(flash_lock_write(), NULL); - return sectrue; -} -secbool flash_area_erase_bulk(const flash_area_t *areas, int count, - void (*progress)(int pos, int len)) { - ensure(flash_unlock_write(), NULL); - FLASH_EraseInitTypeDef EraseInitStruct; - EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES; - EraseInitStruct.NbPages = 1; - - int total_pages = 0; - int done_pages = 0; - for (int c = 0; c < count; c++) { - for (int i = 0; i < areas[c].num_subareas; i++) { - total_pages += areas[c].subarea[i].num_sectors; - } + if (flash_sector_is_secure(sector)) { + EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES; } - if (progress) { - progress(0, total_pages); - } + uint32_t sector_error = 0; - for (int c = 0; c < count; c++) { - for (int s = 0; s < areas[c].num_subareas; s++) { - for (int i = 0; i < areas[c].subarea[s].num_sectors; i++) { - int page = areas[c].subarea[s].first_sector + i; - if (page >= 256) { - EraseInitStruct.Banks = FLASH_BANK_2; - } else { - EraseInitStruct.Banks = FLASH_BANK_1; - } - EraseInitStruct.Page = page; - uint32_t SectorError; - if (HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError) != HAL_OK) { - ensure(flash_lock_write(), NULL); - return secfalse; - } - // check whether the sector was really deleted (contains only 0xFF) - const uint32_t addr_start = - (uint32_t)flash_get_address(page, 0, 0, areas[c].secure_area); - const uint32_t addr_end = - (uint32_t)flash_get_address(page + 1, 0, 0, areas[c].secure_area); - - for (uint32_t addr = addr_start; addr < addr_end; addr += 4) { - if (*((const uint32_t *)addr) != 0xFFFFFFFF) { - ensure(flash_lock_write(), NULL); - return secfalse; - } - } - done_pages++; - if (progress) { - progress(done_pages, total_pages); - } - } - } + if (HAL_FLASHEx_Erase(&EraseInitStruct, §or_error) != HAL_OK) { + return secfalse; } - ensure(flash_lock_write(), NULL); - return sectrue; -} -secbool flash_area_erase_partial(const flash_area_t *area, uint32_t offset, - uint32_t *bytes_erased) { - ensure(flash_unlock_write(), NULL); - FLASH_EraseInitTypeDef EraseInitStruct; - EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES; - EraseInitStruct.NbPages = 1; - - *bytes_erased = 0; - int32_t page = flash_get_sector_num(area, offset / FLASH_PAGE_SIZE); - - if (page >= 0 && page < FLASH_SECTOR_COUNT) { - uint32_t sec_start = 0; - uint32_t sec_end = 0; - uint32_t page_idx = 0; - bool secure = area->secure_area; - if (page >= 256) { - EraseInitStruct.Banks = FLASH_BANK_2; - page_idx = page - 256; - sec_start = (FLASH->SECWM2R1 & FLASH_SECWM1R1_SECWM1_PSTRT) >> - FLASH_SECWM1R1_SECWM1_PSTRT_Pos; - sec_end = (FLASH->SECWM2R1 & FLASH_SECWM1R1_SECWM1_PEND) >> - FLASH_SECWM1R1_SECWM1_PEND_Pos; - } else { - EraseInitStruct.Banks = FLASH_BANK_1; - page_idx = page; - sec_start = (FLASH->SECWM1R1 & FLASH_SECWM1R1_SECWM1_PSTRT) >> - FLASH_SECWM1R1_SECWM1_PSTRT_Pos; - sec_end = (FLASH->SECWM1R1 & FLASH_SECWM1R1_SECWM1_PEND) >> - FLASH_SECWM1R1_SECWM1_PEND_Pos; - } + // check whether the sector was really deleted (contains only 0xFF) + const uint32_t *sector_start = + (const uint32_t *)flash_get_address(sector, 0, 0); - if (page_idx < sec_start || page_idx > sec_end) { - EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES_NS; - secure = false; - } else { - EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES; - } + const uint32_t *sector_end = + sector_start + flash_sector_size(sector, 1) / sizeof(uint32_t); - EraseInitStruct.Page = page_idx; - uint32_t SectorError; - if (HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError) != HAL_OK) { - ensure(flash_lock_write(), NULL); + for (const uint32_t *addr = sector_start; addr < sector_end; addr++) { + if (*addr != 0xFFFFFFFF) { return secfalse; } - // check whether the sector was really deleted (contains only 0xFF) - const uint32_t addr_start = (uint32_t)flash_get_address(page, 0, 0, secure); - const uint32_t addr_end = - (uint32_t)flash_get_address(page + 1, 0, 0, secure); - - for (uint32_t addr = addr_start; addr < addr_end; addr += 4) { - if (*((const uint32_t *)addr) != 0xFFFFFFFF) { - ensure(flash_lock_write(), NULL); - return secfalse; - } - } - ensure(flash_lock_write(), NULL); - *bytes_erased = FLASH_PAGE_SIZE; - return sectrue; } - ensure(flash_lock_write(), NULL); + return sectrue; } secbool flash_write_quadword(uint16_t sector, uint32_t offset, - const uint32_t *data, bool secure_area) { - uint32_t address = (uint32_t)flash_get_address( - sector, offset, 4 * sizeof(uint32_t), secure_area); + const uint32_t *data) { + uint32_t address = + (uint32_t)flash_get_address(sector, offset, 4 * sizeof(uint32_t)); if (address == 0) { return secfalse; } @@ -394,9 +166,9 @@ secbool flash_write_quadword(uint16_t sector, uint32_t offset, } secbool flash_write_burst(uint16_t sector, uint32_t offset, - const uint32_t *data, bool secure_area) { - uint32_t address = (uint32_t)flash_get_address( - sector, offset, 8 * 4 * sizeof(uint32_t), secure_area); + const uint32_t *data) { + uint32_t address = + (uint32_t)flash_get_address(sector, offset, 8 * 4 * sizeof(uint32_t)); if (address == 0) { return secfalse; } @@ -434,129 +206,3 @@ secbool flash_write_burst(uint16_t sector, uint32_t offset, } return sectrue; } - -secbool flash_area_write_quadword(const flash_area_t *area, uint32_t offset, - const uint32_t *data) { - uint32_t tmp_offset = offset; - for (int i = 0; i < area->num_subareas; i++) { - uint16_t sector = area->subarea[i].first_sector; - - uint32_t sub_size = flash_subarea_get_size(&area->subarea[i]); - if (tmp_offset >= sub_size) { - tmp_offset -= sub_size; - continue; - } - - // in correct subarea - for (int s = 0; s < area->subarea[i].num_sectors; s++) { - const uint32_t sector_size = flash_sector_size(sector); - if (tmp_offset >= sector_size) { - tmp_offset -= sector_size; - sector++; - - if (s == area->subarea[i].num_sectors - 1) { - return secfalse; - } - continue; - } - // in correct sector - return flash_write_quadword(sector, tmp_offset, data, area->secure_area); - } - } - return secfalse; -} - -secbool flash_area_write_burst(const flash_area_t *area, uint32_t offset, - const uint32_t *data) { - uint32_t tmp_offset = offset; - for (int i = 0; i < area->num_subareas; i++) { - uint16_t sector = area->subarea[i].first_sector; - - uint32_t sub_size = flash_subarea_get_size(&area->subarea[i]); - if (tmp_offset >= sub_size) { - tmp_offset -= sub_size; - continue; - } - - // in correct subarea - for (int s = 0; s < area->subarea[i].num_sectors; s++) { - const uint32_t sector_size = flash_sector_size(sector); - if (tmp_offset >= sector_size) { - tmp_offset -= sector_size; - sector++; - - if (s == area->subarea[i].num_sectors - 1) { - return secfalse; - } - continue; - } - // in correct sector - return flash_write_burst(sector, tmp_offset, data, area->secure_area); - } - } - return secfalse; -} - -secbool flash_otp_read(uint8_t block, uint8_t offset, uint8_t *data, - uint8_t datalen) { - if (block >= FLASH_OTP_NUM_BLOCKS || - offset + datalen > FLASH_OTP_BLOCK_SIZE) { - return secfalse; - } - for (uint8_t i = 0; i < datalen; i++) { - data[i] = *(__IO uint8_t *)(FLASH_OTP_BASE + block * FLASH_OTP_BLOCK_SIZE + - offset + i); - } - return sectrue; -} - -secbool flash_otp_write(uint8_t block, uint8_t offset, const uint8_t *data, - uint8_t datalen) { - if (datalen % 16 != 0) { - return secfalse; - } - if (block >= FLASH_OTP_NUM_BLOCKS || - offset + datalen > FLASH_OTP_BLOCK_SIZE) { - return secfalse; - } - ensure(flash_unlock_write(), NULL); - for (uint8_t i = 0; i < datalen; i++) { - uint32_t address = - FLASH_OTP_BASE + block * FLASH_OTP_BLOCK_SIZE + offset + i; - ensure(sectrue * (HAL_OK == HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD, - address, (uint32_t)data)), - NULL); - } - ensure(flash_lock_write(), NULL); - return sectrue; -} - -secbool flash_otp_lock(uint8_t block) { - // check that all quadwords in the block have been written to - volatile uint8_t *addr = - (__IO uint8_t *)(FLASH_OTP_BASE + block * FLASH_OTP_BLOCK_SIZE); - - secbool qw_locked = secfalse; - for (uint8_t i = 0; i < FLASH_OTP_BLOCK_SIZE; i++) { - if (addr[i] != 0xFF) { - qw_locked = sectrue; - } - if (i % 16 == 15 && qw_locked == secfalse) { - return secfalse; - } - } - return sectrue; -} - -secbool flash_otp_is_locked(uint8_t block) { - // considering block locked if any quadword in the block is non-0xFF - volatile uint8_t *addr = - (__IO uint8_t *)(FLASH_OTP_BASE + block * FLASH_OTP_BLOCK_SIZE); - - for (uint8_t i = 0; i < FLASH_OTP_BLOCK_SIZE; i++) { - if (addr[i] != 0xFF) { - return sectrue; - } - } - return secfalse; -} diff --git a/core/embed/trezorhal/stm32u5/flash_otp.c b/core/embed/trezorhal/stm32u5/flash_otp.c new file mode 100644 index 00000000000..d76044c9ccb --- /dev/null +++ b/core/embed/trezorhal/stm32u5/flash_otp.c @@ -0,0 +1,92 @@ +/* + * 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 STM32_HAL_H + +#include "flash_otp.h" +#include "common.h" +#include "flash.h" + +void flash_otp_init() { + // intentionally left empty +} + +secbool flash_otp_read(uint8_t block, uint8_t offset, uint8_t *data, + uint8_t datalen) { + if (block >= FLASH_OTP_NUM_BLOCKS || + offset + datalen > FLASH_OTP_BLOCK_SIZE) { + return secfalse; + } + for (uint8_t i = 0; i < datalen; i++) { + data[i] = *(__IO uint8_t *)(FLASH_OTP_BASE + block * FLASH_OTP_BLOCK_SIZE + + offset + i); + } + return sectrue; +} + +secbool flash_otp_write(uint8_t block, uint8_t offset, const uint8_t *data, + uint8_t datalen) { + if (datalen % 16 != 0) { + return secfalse; + } + if (block >= FLASH_OTP_NUM_BLOCKS || + offset + datalen > FLASH_OTP_BLOCK_SIZE) { + return secfalse; + } + ensure(flash_unlock_write(), NULL); + for (uint8_t i = 0; i < datalen; i++) { + uint32_t address = + FLASH_OTP_BASE + block * FLASH_OTP_BLOCK_SIZE + offset + i; + ensure(sectrue * (HAL_OK == HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD, + address, (uint32_t)data)), + NULL); + } + ensure(flash_lock_write(), NULL); + return sectrue; +} + +secbool flash_otp_lock(uint8_t block) { + // check that all quadwords in the block have been written to + volatile uint8_t *addr = + (__IO uint8_t *)(FLASH_OTP_BASE + block * FLASH_OTP_BLOCK_SIZE); + + secbool qw_locked = secfalse; + for (uint8_t i = 0; i < FLASH_OTP_BLOCK_SIZE; i++) { + if (addr[i] != 0xFF) { + qw_locked = sectrue; + } + if (i % 16 == 15 && qw_locked == secfalse) { + return secfalse; + } + } + return sectrue; +} + +secbool flash_otp_is_locked(uint8_t block) { + // considering block locked if any quadword in the block is non-0xFF + volatile uint8_t *addr = + (__IO uint8_t *)(FLASH_OTP_BASE + block * FLASH_OTP_BLOCK_SIZE); + + for (uint8_t i = 0; i < FLASH_OTP_BLOCK_SIZE; i++) { + if (addr[i] != 0xFF) { + return sectrue; + } + } + return secfalse; +} diff --git a/core/embed/trezorhal/stm32u5/lowlevel.c b/core/embed/trezorhal/stm32u5/lowlevel.c index 8aa1e29b5db..f289368c19d 100644 --- a/core/embed/trezorhal/stm32u5/lowlevel.c +++ b/core/embed/trezorhal/stm32u5/lowlevel.c @@ -22,6 +22,7 @@ #include "lowlevel.h" #include "common.h" #include "flash.h" +#include "flash_otp.h" #include "model.h" #include TREZOR_BOARD @@ -84,6 +85,27 @@ SEC_AREA_2_PAGE_END << FLASH_SECWM1R1_SECWM1_PEND_Pos | 0xFF00FF00) #define FLASH_SECWM2R2_VALUE (0x7F007F00) +#define FLASH_STATUS_ALL_FLAGS \ + (FLASH_NSSR_PGSERR | FLASH_NSSR_PGAERR | FLASH_NSSR_WRPERR | FLASH_NSSR_EOP) + +static uint32_t flash_wait_and_clear_status_flags(void) { + while (FLASH->NSSR & FLASH_NSSR_BSY) + ; // wait for all previous flash operations to complete + + uint32_t result = + FLASH->NSSR & FLASH_STATUS_ALL_FLAGS; // get the current status flags + FLASH->NSSR |= FLASH_STATUS_ALL_FLAGS; // clear all status flags + +#if defined(__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) + while (FLASH->SECSR & FLASH_SECSR_BSY) + ; // wait for all previous flash operations to complete + result |= + FLASH->SECSR & FLASH_STATUS_ALL_FLAGS; // get the current status flags + FLASH->SECSR |= FLASH_STATUS_ALL_FLAGS; // clear all status flags +#endif + return result; +} + secbool flash_check_option_bytes(void) { flash_wait_and_clear_status_flags(); // check values stored in flash interface registers diff --git a/core/embed/trezorhal/stm32u5/secret.c b/core/embed/trezorhal/stm32u5/secret.c index aac77080661..db18379646b 100644 --- a/core/embed/trezorhal/stm32u5/secret.c +++ b/core/embed/trezorhal/stm32u5/secret.c @@ -1,4 +1,5 @@ #include "secret.h" +#include #include #include "common.h" #include "flash.h" diff --git a/core/embed/trezorhal/unix/flash.c b/core/embed/trezorhal/unix/flash.c index 6d279b57bc2..35fbe6176d8 100644 --- a/core/embed/trezorhal/unix/flash.c +++ b/core/embed/trezorhal/unix/flash.c @@ -80,11 +80,6 @@ static const uint32_t FLASH_SECTOR_TABLE[FLASH_SECTOR_COUNT + 1] = { static uint8_t *FLASH_BUFFER = NULL; static uint32_t FLASH_SIZE; -#define OTP_BLOCK_SIZE 32 -#define FLASH_SECTOR_OTP (FLASH_SECTOR_COUNT) - -static uint8_t OTP_BUFFER[OTP_BLOCK_SIZE * 64]; - static void flash_exit(void) { int r = munmap(FLASH_BUFFER, FLASH_SIZE); ensure(sectrue * (r == 0), "munmap failed"); @@ -123,9 +118,6 @@ void flash_init(void) { FLASH_BUFFER = (uint8_t *)map; - // fill OTP buffer with ones - memset(OTP_BUFFER, 0xFF, sizeof(OTP_BUFFER)); - atexit(flash_exit); } @@ -145,79 +137,44 @@ const void *flash_get_address(uint16_t sector, uint32_t offset, uint32_t size) { return FLASH_BUFFER + addr - FLASH_SECTOR_TABLE[0]; } -uint32_t flash_sector_size(uint16_t sector) { - if (sector >= FLASH_SECTOR_COUNT) { +uint32_t flash_sector_size(uint16_t first_sector, uint16_t sector_count) { + if (first_sector + sector_count > FLASH_SECTOR_COUNT) { return 0; } - return FLASH_SECTOR_TABLE[sector + 1] - FLASH_SECTOR_TABLE[sector]; + return FLASH_SECTOR_TABLE[first_sector + sector_count] - + FLASH_SECTOR_TABLE[first_sector]; } -secbool flash_area_erase_bulk(const flash_area_t *area, int count, - void (*progress)(int pos, int len)) { - ensure(flash_unlock_write(), NULL); - - int total_sectors = 0; - int done_sectors = 0; - for (int a = 0; a < count; a++) { - for (int i = 0; i < area[a].num_subareas; i++) { - total_sectors += area[a].subarea[i].num_sectors; - } - } - - if (progress) { - progress(0, total_sectors); - } +uint16_t flash_sector_find(uint16_t first_sector, uint32_t offset) { + uint16_t sector = first_sector; - for (int a = 0; a < count; a++) { - for (int s = 0; s < area[a].num_subareas; s++) { - for (int i = 0; i < area[a].subarea[s].num_sectors; i++) { - int sector = area[a].subarea[s].first_sector + i; + while (sector < FLASH_SECTOR_COUNT) { + uint32_t sector_size = + FLASH_SECTOR_TABLE[sector + 1] - FLASH_SECTOR_TABLE[sector]; - const uint32_t offset = - FLASH_SECTOR_TABLE[sector] - FLASH_SECTOR_TABLE[0]; - const uint32_t size = - FLASH_SECTOR_TABLE[sector + 1] - FLASH_SECTOR_TABLE[sector]; - memset(FLASH_BUFFER + offset, 0xFF, size); - - done_sectors++; - if (progress) { - progress(done_sectors, total_sectors); - } - } + if (offset < sector_size) { + break; } + offset -= sector_size; + sector++; } - ensure(flash_lock_write(), NULL); - return sectrue; -} -secbool flash_area_erase_partial(const flash_area_t *area, uint32_t offset, - uint32_t *bytes_erased) { - uint32_t sector_offset = 0; - *bytes_erased = 0; + return sector; +} - for (int s = 0; s < area->num_subareas; s++) { - for (int i = 0; i < area->subarea[s].num_sectors; i++) { - uint32_t sector_index = area->subarea[s].first_sector + i; - uint32_t sector_size = FLASH_SECTOR_TABLE[sector_index + 1] - - FLASH_SECTOR_TABLE[sector_index]; +secbool flash_sector_erase(uint16_t sector) { + if (sector >= FLASH_SECTOR_COUNT) { + return secfalse; + } - if (offset == sector_offset) { - uint8_t *flash = - (uint8_t *)flash_get_address(sector_index, 0, sector_size); - memset(flash, 0xFF, sector_size); - *bytes_erased = sector_size; - return sectrue; - } + const uint32_t offset = FLASH_SECTOR_TABLE[sector] - FLASH_SECTOR_TABLE[0]; - sector_offset += sector_size; - } - } + const uint32_t size = + FLASH_SECTOR_TABLE[sector + 1] - FLASH_SECTOR_TABLE[sector]; - if (offset == sector_offset) { - return sectrue; - } + memset(FLASH_BUFFER + offset, 0xFF, size); - return secfalse; + return sectrue; } secbool flash_write_byte(uint16_t sector, uint32_t offset, uint8_t data) { @@ -246,33 +203,3 @@ secbool flash_write_word(uint16_t sector, uint32_t offset, uint32_t data) { flash[0] = data; return sectrue; } - -secbool flash_otp_read(uint8_t block, uint8_t offset, uint8_t *data, - uint8_t datalen) { - if (offset + datalen > OTP_BLOCK_SIZE) { - return secfalse; - } - uint32_t offset_in_sector = block * OTP_BLOCK_SIZE + offset; - memcpy(data, OTP_BUFFER + offset_in_sector, datalen); - return sectrue; -} - -secbool flash_otp_write(uint8_t block, uint8_t offset, const uint8_t *data, - uint8_t datalen) { - if (offset + datalen > OTP_BLOCK_SIZE) { - return secfalse; - } - uint32_t offset_in_sector = block * OTP_BLOCK_SIZE + offset; - uint8_t *flash = OTP_BUFFER + offset_in_sector; - for (int i = 0; i < datalen; i++) { - if ((flash[i] & data[i]) != data[i]) { - return secfalse; // we cannot change zeroes to ones - } - flash[i] = data[i]; - } - return sectrue; -} - -secbool flash_otp_lock(uint8_t block) { return secfalse; } - -secbool flash_otp_is_locked(uint8_t block) { return secfalse; } diff --git a/core/embed/trezorhal/unix/flash_otp.c b/core/embed/trezorhal/unix/flash_otp.c new file mode 100644 index 00000000000..1369e092937 --- /dev/null +++ b/core/embed/trezorhal/unix/flash_otp.c @@ -0,0 +1,62 @@ +/* + * 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 + +#include "../flash_otp.h" + +#define OTP_BLOCK_SIZE 32 +#define FLASH_SECTOR_OTP (FLASH_SECTOR_COUNT) + +static uint8_t OTP_BUFFER[OTP_BLOCK_SIZE * 64]; + +void flash_otp_init(void) { + // fill OTP buffer with ones + memset(OTP_BUFFER, 0xFF, sizeof(OTP_BUFFER)); +} + +secbool flash_otp_read(uint8_t block, uint8_t offset, uint8_t *data, + uint8_t datalen) { + if (offset + datalen > OTP_BLOCK_SIZE) { + return secfalse; + } + uint32_t offset_in_sector = block * OTP_BLOCK_SIZE + offset; + memcpy(data, OTP_BUFFER + offset_in_sector, datalen); + return sectrue; +} + +secbool flash_otp_write(uint8_t block, uint8_t offset, const uint8_t *data, + uint8_t datalen) { + if (offset + datalen > OTP_BLOCK_SIZE) { + return secfalse; + } + uint32_t offset_in_sector = block * OTP_BLOCK_SIZE + offset; + uint8_t *flash = OTP_BUFFER + offset_in_sector; + for (int i = 0; i < datalen; i++) { + if ((flash[i] & data[i]) != data[i]) { + return secfalse; // we cannot change zeroes to ones + } + flash[i] = data[i]; + } + return sectrue; +} + +secbool flash_otp_lock(uint8_t block) { return secfalse; } + +secbool flash_otp_is_locked(uint8_t block) { return secfalse; } diff --git a/core/embed/unix/main.c b/core/embed/unix/main.c index a6c90fe414f..f7c6461ea8d 100644 --- a/core/embed/unix/main.c +++ b/core/embed/unix/main.c @@ -40,6 +40,7 @@ #include "extmod/misc.h" #include "extmod/vfs_posix.h" #include "flash.h" +#include "flash_otp.h" #include "genhdr/mpversion.h" #include "input.h" #include "py/builtin.h" @@ -482,6 +483,7 @@ MP_NOINLINE int main_(int argc, char **argv) { // Map trezor.flash to memory. flash_init(); + flash_otp_init(); #if MICROPY_ENABLE_GC char *heap = malloc(heap_size); diff --git a/core/site_scons/boards/stm32f4_common.py b/core/site_scons/boards/stm32f4_common.py index f6294295a0c..8265068c4b2 100644 --- a/core/site_scons/boards/stm32f4_common.py +++ b/core/site_scons/boards/stm32f4_common.py @@ -42,6 +42,7 @@ def stm32f4_common_files(env, defines, sources, paths): "embed/trezorhal/stm32f4/common.c", "embed/trezorhal/stm32f4/fault_handlers.c", "embed/trezorhal/stm32f4/flash.c", + "embed/trezorhal/stm32f4/flash_otp.c", "embed/trezorhal/stm32f4/lowlevel.c", "embed/trezorhal/stm32f4/mpu.c", "embed/trezorhal/stm32f4/platform.c", @@ -50,7 +51,6 @@ def stm32f4_common_files(env, defines, sources, paths): "embed/trezorhal/stm32f4/random_delays.c", "embed/trezorhal/stm32f4/rng.c", "embed/trezorhal/stm32f4/vectortable.s", - "vendor/trezor-storage/flash_common_f4.c", ] # boardloader needs separate assembler for some function unencumbered by various FW+bootloader hacks diff --git a/core/site_scons/boards/stm32u5_common.py b/core/site_scons/boards/stm32u5_common.py index 61c4184e4d2..f1594e3b2ec 100644 --- a/core/site_scons/boards/stm32u5_common.py +++ b/core/site_scons/boards/stm32u5_common.py @@ -52,6 +52,7 @@ def stm32u5_common_files(env, defines, sources, paths): "embed/trezorhal/stm32u5/common.c", "embed/trezorhal/stm32u5/fault_handlers.c", "embed/trezorhal/stm32u5/flash.c", + "embed/trezorhal/stm32u5/flash_otp.c", "embed/trezorhal/stm32u5/lowlevel.c", "embed/trezorhal/stm32u5/mpu.c", "embed/trezorhal/stm32u5/platform.c", diff --git a/legacy/Makefile b/legacy/Makefile index 7795fd21e6a..d77354a04e9 100644 --- a/legacy/Makefile +++ b/legacy/Makefile @@ -20,7 +20,7 @@ OBJS += usb_standard.o OBJS += util.o OBJS += webusb.o OBJS += winusb.o -OBJS += vendor/trezor-storage/flash_common_f4.o +OBJS += vendor/trezor-storage/flash_area.o libtrezor.a: $(OBJS) diff --git a/legacy/flash.c b/legacy/flash.c index 4f3f1447176..4c3ce7c0329 100644 --- a/legacy/flash.c +++ b/legacy/flash.c @@ -22,6 +22,7 @@ #include "common.h" #include "flash.h" +#include "flash_area.h" #include "memory.h" #include "supervise.h" @@ -88,11 +89,29 @@ const void *flash_get_address(uint16_t sector, uint32_t offset, uint32_t size) { return (const void *)FLASH_PTR(addr); } -uint32_t flash_sector_size(uint16_t sector) { - if (sector >= FLASH_SECTOR_COUNT) { +uint32_t flash_sector_size(uint16_t first_sector, uint16_t sector_count) { + if (first_sector + sector_count >= FLASH_SECTOR_COUNT) { return 0; } - return FLASH_SECTOR_TABLE[sector + 1] - FLASH_SECTOR_TABLE[sector]; + return FLASH_SECTOR_TABLE[first_sector + sector_count] - + FLASH_SECTOR_TABLE[first_sector]; +} + +uint16_t flash_sector_find(uint16_t first_sector, uint32_t offset) { + uint16_t sector = first_sector; + + while (sector < FLASH_SECTOR_COUNT) { + uint32_t sector_size = + FLASH_SECTOR_TABLE[sector + 1] - FLASH_SECTOR_TABLE[sector]; + + if (offset < sector_size) { + break; + } + offset -= sector_size; + sector++; + } + + return sector; } secbool flash_write_byte(uint16_t sector, uint32_t offset, uint8_t data) { @@ -139,42 +158,7 @@ secbool flash_write_word(uint16_t sector, uint32_t offset, uint32_t data) { return sectrue; } -secbool flash_area_erase_bulk(const flash_area_t *area, int count, - void (*progress)(int pos, int len)) { - ensure(flash_unlock_write(), NULL); - - int total_sectors = 0; - int done_sectors = 0; - for (int a = 0; a < count; a++) { - for (int i = 0; i < area[a].num_subareas; i++) { - total_sectors += area[a].subarea[i].num_sectors; - } - } - if (progress) { - progress(0, total_sectors); - } - - for (int a = 0; a < count; a++) { - for (int s = 0; s < area[a].num_subareas; s++) { - for (int i = 0; i < area[a].subarea[s].num_sectors; i++) { - int sector = area[a].subarea[s].first_sector + i; - svc_flash_erase_sector(sector); - // check whether the sector was really deleted (contains only 0xFF) - const uint32_t addr_start = FLASH_SECTOR_TABLE[sector], - addr_end = FLASH_SECTOR_TABLE[sector + 1]; - for (uint32_t addr = addr_start; addr < addr_end; addr += 4) { - if (*((const uint32_t *)FLASH_PTR(addr)) != 0xFFFFFFFF) { - ensure(flash_lock_write(), NULL); - return secfalse; - } - } - done_sectors++; - if (progress) { - progress(done_sectors, total_sectors); - } - } - } - } - ensure(flash_lock_write(), NULL); +secbool flash_sector_erase(uint16_t sector) { + svc_flash_erase_sector(sector); return sectrue; } diff --git a/legacy/flash.h b/legacy/flash.h index 5559b6d89b9..f082908138d 100644 --- a/legacy/flash.h +++ b/legacy/flash.h @@ -27,7 +27,7 @@ #define FLASH_BYTE_ACCESS 1 #define FLASH_SECTOR_COUNT 24 -#include "flash_common.h" +#include "flash_ll.h" // note: FLASH_SR_RDERR is STM32F42xxx and STM32F43xxx specific (STM32F427) // (reference RM0090 section 3.7.5) diff --git a/legacy/intermediate_fw/trezor.c b/legacy/intermediate_fw/trezor.c index 18d3e9a1292..8f0d9866c05 100644 --- a/legacy/intermediate_fw/trezor.c +++ b/legacy/intermediate_fw/trezor.c @@ -35,8 +35,6 @@ #include "timer.h" #include "util.h" -const void *flash_get_address(uint16_t sector, uint32_t offset, uint32_t size); - // legacy storage magic #define LEGACY_STORAGE_SECTOR 2 static const uint32_t META_MAGIC_V10 = 0x525a5254; // 'TRZR' diff --git a/legacy/memory.c b/legacy/memory.c index 079ba50413d..a2b16329429 100644 --- a/legacy/memory.c +++ b/legacy/memory.c @@ -28,8 +28,6 @@ #define FLASH_OPTION_BYTES_1 (*(const uint64_t *)0x1FFFC000) #define FLASH_OPTION_BYTES_2 (*(const uint64_t *)0x1FFFC008) -const void *flash_get_address(uint16_t sector, uint32_t offset, uint32_t size); - void memory_protect(void) { #if PRODUCTION #if BOOTLOADER_QA @@ -110,7 +108,7 @@ int memory_firmware_hash(const uint8_t *challenge, uint32_t challenge_size, } for (int i = FLASH_CODE_SECTOR_FIRST; i <= FLASH_CODE_SECTOR_LAST; i++) { - uint32_t size = flash_sector_size(i); + uint32_t size = flash_sector_size(i, 1); const void *data = flash_get_address(i, 0, size); if (data == NULL) { return 1; diff --git a/legacy/norcow_config.h b/legacy/norcow_config.h index 3a9545e19b0..d7416bb63bd 100644 --- a/legacy/norcow_config.h +++ b/legacy/norcow_config.h @@ -20,7 +20,7 @@ #ifndef __NORCOW_CONFIG_H__ #define __NORCOW_CONFIG_H__ -#include "flash.h" +#include "flash_area.h" #define NORCOW_SECTOR_COUNT 2 #define NORCOW_SECTOR_SIZE (16 * 1024) diff --git a/storage/flash_area.c b/storage/flash_area.c new file mode 100644 index 00000000000..bc1fb2d7887 --- /dev/null +++ b/storage/flash_area.c @@ -0,0 +1,243 @@ +/* + * 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 "flash_area.h" +#include + +uint32_t flash_area_get_size(const flash_area_t *area) { + uint32_t size = 0; + for (int i = 0; i < area->num_subareas; i++) { + size += flash_sector_size(area->subarea[i].first_sector, + area->subarea[i].num_sectors); + } + return size; +} + +uint16_t flash_area_total_sectors(const flash_area_t *area) { + uint16_t total = 0; + for (int i = 0; i < area->num_subareas; i++) { + total += area->subarea[i].num_sectors; + } + return total; +} + +static secbool get_sector_and_offset(const flash_area_t *area, uint32_t offset, + uint16_t *sector_out, + uint32_t *offset_out) { + for (int i = 0; i < area->num_subareas; i++) { + // Get the sub-area parameters + uint16_t first_sector = area->subarea[i].first_sector; + uint16_t num_sectors = area->subarea[i].num_sectors; + uint32_t subarea_size = flash_sector_size(first_sector, num_sectors); + // Does the requested offset start in the sub-area? + if (offset < subarea_size) { + uint16_t found_sector = flash_sector_find(first_sector, offset); + *sector_out = found_sector; + *offset_out = + offset - flash_sector_size(first_sector, found_sector - first_sector); + return sectrue; + } + offset -= subarea_size; + } + + return secfalse; +} + +const void *flash_area_get_address(const flash_area_t *area, uint32_t offset, + uint32_t size) { + for (int i = 0; i < area->num_subareas; i++) { + // Get sub-area parameters + uint16_t first_sector = area->subarea[i].first_sector; + uint16_t num_sectors = area->subarea[i].num_sectors; + uint32_t subarea_size = flash_sector_size(first_sector, num_sectors); + // Does the requested block start in the sub-area? + if (offset < subarea_size) { + // Does the requested block fit in the sub-area? + if (offset + size <= subarea_size) { + const uint8_t *ptr = + (const uint8_t *)flash_get_address(first_sector, 0, 0); + // We expect that all sectors/pages in the sub-area make + // a continuous block of adresses with the same security atributes + return ptr + offset; + } else { + return NULL; + } + } + offset -= subarea_size; + } + return NULL; +} + +#if defined FLASH_BYTE_ACCESS + +secbool flash_area_write_byte(const flash_area_t *area, uint32_t offset, + uint8_t data) { + uint16_t sector; + uint32_t sector_offset; + if (get_sector_and_offset(area, offset, §or, §or_offset) != sectrue) { + return secfalse; + } + return flash_write_byte(sector, sector_offset, data); +} + +secbool flash_area_write_word(const flash_area_t *area, uint32_t offset, + uint32_t data) { + uint16_t sector; + uint32_t sector_offset; + if (get_sector_and_offset(area, offset, §or, §or_offset) != sectrue) { + return secfalse; + } + return flash_write_word(sector, sector_offset, data); +} + +secbool flash_area_write_quadword(const flash_area_t *area, uint32_t offset, + const uint32_t *data) { + if (offset % 16 != 0) { + return secfalse; + } + for (int i = 0; i < 4; i++) { + if (sectrue != + flash_area_write_word(area, offset + i * sizeof(uint32_t), data[i])) { + return secfalse; + } + } + return sectrue; +} + +secbool flash_area_write_burst(const flash_area_t *area, uint32_t offset, + const uint32_t *data) { + if (offset % (8 * 16) != 0) { + return secfalse; + } + for (int i = 0; i < (8 * 4); i++) { + if (sectrue != + flash_area_write_word(area, offset + i * sizeof(uint32_t), data[i])) { + return secfalse; + } + } + return sectrue; +} + +#else // not defined FLASH_BYTE_ACCESS + +secbool flash_area_write_quadword(const flash_area_t *area, uint32_t offset, + const uint32_t *data) { + uint16_t sector; + uint32_t sector_offset; + if (get_sector_and_offset(area, offset, §or, §or_offset) != sectrue) { + return secfalse; + } + return flash_write_quadword(sector, sector_offset, data); +} + +secbool flash_area_write_burst(const flash_area_t *area, uint32_t offset, + const uint32_t *data) { + uint16_t sector; + uint32_t sector_offset; + if (get_sector_and_offset(area, offset, §or, §or_offset) != sectrue) { + return secfalse; + } + return flash_write_burst(sector, sector_offset, data); +} + +#endif // not defined FLASH_BYTE_ACCESS + +secbool flash_area_erase(const flash_area_t *area, + void (*progress)(int pos, int len)) { + return flash_area_erase_bulk(area, 1, progress); +} + +static secbool erase_sector(uint16_t sector) { + secbool result = secfalse; + + if (sectrue != flash_unlock_write()) { + return secfalse; + } + + result = flash_sector_erase(sector); + + if (sectrue != flash_lock_write()) { + return secfalse; + } + + return result; +} + +secbool flash_area_erase_bulk(const flash_area_t *area, int count, + void (*progress)(int pos, int len)) { + int total_sectors = 0; + int done_sectors = 0; + for (int a = 0; a < count; a++) { + for (int i = 0; i < area[a].num_subareas; i++) { + total_sectors += area[a].subarea[i].num_sectors; + } + } + if (progress) { + progress(0, total_sectors); + } + + for (int a = 0; a < count; a++) { + for (int s = 0; s < area[a].num_subareas; s++) { + for (int i = 0; i < area[a].subarea[s].num_sectors; i++) { + int sector = area[a].subarea[s].first_sector + i; + + if (sectrue != erase_sector(sector)) { + return secfalse; + } + + done_sectors++; + if (progress) { + progress(done_sectors, total_sectors); + } + } + } + } + + return sectrue; +} + +secbool flash_area_erase_partial(const flash_area_t *area, uint32_t offset, + uint32_t *bytes_erased) { + uint32_t sector_offset = 0; + *bytes_erased = 0; + + for (int s = 0; s < area->num_subareas; s++) { + for (int i = 0; i < area->subarea[s].num_sectors; i++) { + uint32_t sector = area->subarea[s].first_sector + i; + uint32_t sector_size = flash_sector_size(sector, 1); + + if (offset == sector_offset) { + if (sectrue != erase_sector(sector)) { + return secfalse; + } + + *bytes_erased = sector_size; + return sectrue; + } + + sector_offset += sector_size; + } + } + + if (offset == sector_offset) { + return sectrue; + } + + return secfalse; +} diff --git a/storage/flash_area.h b/storage/flash_area.h new file mode 100644 index 00000000000..02f8930fcb0 --- /dev/null +++ b/storage/flash_area.h @@ -0,0 +1,89 @@ +/* + * 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 . + */ + +#ifndef FLASH_AREA_H +#define FLASH_AREA_H + +#include +#include "secbool.h" + +#include "flash.h" + +/** + * Flash driver interface is designed to abstract away differences between + * various MCUs used in Trezor devices. + * + * Generally, flash memory is divided into sectors. On different MCUs, sectors + * may have different sizes, and therefore, different number of sectors are used + * for a given purpose. For example, on STM32F4, the sectors are relatively + * large so we use single sector for Storage. On STM32U5, the sectors are + * smaller, so we use multiple sectors for the Storage. Storage implementation + * should not care about this, and should use flash_area_t interface to access + * the flash memory. + * + * flash_area_t represents a location in flash memory. It may be contiguous, or + * it may be composed of multiple non-contiguous subareas. + * + * flash_subarea_t represents a contiguous area in flash memory, specified by + * first_sector and num_sectors. + */ + +typedef struct { + uint16_t first_sector; + uint16_t num_sectors; +} flash_subarea_t; + +typedef struct { + flash_subarea_t subarea[4]; + uint8_t num_subareas; +} flash_area_t; + +uint32_t flash_area_get_size(const flash_area_t *area); + +uint16_t flash_area_total_sectors(const flash_area_t *area); + +const void *flash_area_get_address(const flash_area_t *area, uint32_t offset, + uint32_t size); + +#if defined FLASH_BYTE_ACCESS +secbool __wur flash_area_write_byte(const flash_area_t *area, uint32_t offset, + uint8_t data); +secbool __wur flash_area_write_word(const flash_area_t *area, uint32_t offset, + uint32_t data); +#endif +secbool __wur flash_area_write_quadword(const flash_area_t *area, + uint32_t offset, const uint32_t *data); + +secbool __wur flash_area_write_burst(const flash_area_t *area, uint32_t offset, + const uint32_t *data); + +secbool __wur flash_area_erase(const flash_area_t *area, + void (*progress)(int pos, int len)); +secbool __wur flash_area_erase_bulk(const flash_area_t *area, int count, + void (*progress)(int pos, int len)); + +// Erases the single sector in the designated flash area +// The 'offset' parameter must indicate the relative sector offset within the +// flash area If 'offset' is outside the bounds of the flash area, +// 'bytes_erased' is set to 0 otherwise, 'bytes_erased' is set to the size of +// the erased sector +secbool __wur flash_area_erase_partial(const flash_area_t *area, + uint32_t offset, uint32_t *bytes_erased); + +#endif // FLASH_AREA_H diff --git a/storage/flash_common.h b/storage/flash_common.h deleted file mode 100644 index f40bae00fdd..00000000000 --- a/storage/flash_common.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef FLASH_COMMON_H -#define FLASH_COMMON_H - -#include -#include -#include "secbool.h" - -typedef struct { - uint16_t first_sector; - uint16_t num_sectors; -} flash_subarea_t; - -typedef struct { - flash_subarea_t subarea[4]; - uint8_t num_subareas; - bool secure_area; -} flash_area_t; - -void flash_init(void); - -secbool __wur flash_unlock_write(void); -secbool __wur flash_lock_write(void); - -uint32_t flash_sector_size(uint16_t sector); -uint16_t flash_total_sectors(const flash_area_t *area); -int32_t flash_get_sector_num(const flash_area_t *area, - uint32_t sector_inner_num); - -const void *flash_area_get_address(const flash_area_t *area, uint32_t offset, - uint32_t size); -uint32_t flash_area_get_size(const flash_area_t *area); - -secbool __wur flash_area_erase(const flash_area_t *area, - void (*progress)(int pos, int len)); -secbool __wur flash_area_erase_bulk(const flash_area_t *area, int count, - void (*progress)(int pos, int len)); - -#if defined FLASH_BYTE_ACCESS -secbool __wur flash_area_write_byte(const flash_area_t *area, uint32_t offset, - uint8_t data); -secbool __wur flash_area_write_word(const flash_area_t *area, uint32_t offset, - uint32_t data); -#endif -secbool __wur flash_area_write_quadword(const flash_area_t *area, - uint32_t offset, const uint32_t *data); - -secbool __wur flash_area_write_burst(const flash_area_t *area, uint32_t offset, - const uint32_t *data); - -#endif diff --git a/storage/flash_common_f4.c b/storage/flash_common_f4.c deleted file mode 100644 index 43e3b22e247..00000000000 --- a/storage/flash_common_f4.c +++ /dev/null @@ -1,150 +0,0 @@ -#include "flash.h" - -secbool flash_write_byte(uint16_t sector, uint32_t offset, uint8_t data); - -secbool flash_write_word(uint16_t sector, uint32_t offset, uint32_t data); - -const void *flash_get_address(uint16_t sector, uint32_t offset, uint32_t size); - -static uint32_t flash_subarea_get_size(const flash_subarea_t *subarea) { - uint32_t size = 0; - for (int s = 0; s < subarea->num_sectors; s++) { - size += flash_sector_size(subarea->first_sector + s); - } - return size; -} - -static secbool subarea_get_sector_and_offset(const flash_subarea_t *subarea, - uint32_t offset, - uint16_t *sector_out, - uint32_t *offset_out) { - uint32_t tmp_offset = offset; - uint16_t sector = subarea->first_sector; - - // in correct subarea - for (int s = 0; s < subarea->num_sectors; s++) { - const uint32_t sector_size = flash_sector_size(sector); - if (tmp_offset < sector_size) { - *sector_out = sector; - *offset_out = tmp_offset; - return sectrue; - } - tmp_offset -= sector_size; - sector++; - } - return secfalse; -} - -uint32_t flash_area_get_size(const flash_area_t *area) { - uint32_t size = 0; - for (int i = 0; i < area->num_subareas; i++) { - size += flash_subarea_get_size(&area->subarea[i]); - } - return size; -} - -uint16_t flash_total_sectors(const flash_area_t *area) { - uint16_t total = 0; - for (int i = 0; i < area->num_subareas; i++) { - total += area->subarea[i].num_sectors; - } - return total; -} - -int32_t flash_get_sector_num(const flash_area_t *area, - uint32_t sector_inner_num) { - uint16_t sector = 0; - uint16_t remaining = sector_inner_num; - for (int i = 0; i < area->num_subareas; i++) { - if (remaining < area->subarea[i].num_sectors) { - sector = area->subarea[i].first_sector + remaining; - return sector; - } else { - remaining -= area->subarea[i].num_sectors; - } - } - - return -1; -} - -static secbool get_sector_and_offset(const flash_area_t *area, uint32_t offset, - uint16_t *sector_out, - uint32_t *offset_out) { - uint32_t tmp_offset = offset; - for (int i = 0; i < area->num_subareas; i++) { - uint32_t sub_size = flash_subarea_get_size(&area->subarea[i]); - if (tmp_offset >= sub_size) { - tmp_offset -= sub_size; - continue; - } - - return subarea_get_sector_and_offset(&area->subarea[i], tmp_offset, - sector_out, offset_out); - } - return secfalse; -} - -const void *flash_area_get_address(const flash_area_t *area, uint32_t offset, - uint32_t size) { - uint16_t sector; - uint32_t sector_offset; - - if (!get_sector_and_offset(area, offset, §or, §or_offset)) { - return NULL; - } - - return flash_get_address(sector, sector_offset, size); -} - -secbool flash_area_erase(const flash_area_t *area, - void (*progress)(int pos, int len)) { - return flash_area_erase_bulk(area, 1, progress); -} - -secbool flash_area_write_byte(const flash_area_t *area, uint32_t offset, - uint8_t data) { - uint16_t sector; - uint32_t sector_offset; - if (get_sector_and_offset(area, offset, §or, §or_offset) != sectrue) { - return secfalse; - } - return flash_write_byte(sector, sector_offset, data); -} - -secbool flash_area_write_word(const flash_area_t *area, uint32_t offset, - uint32_t data) { - uint16_t sector; - uint32_t sector_offset; - if (get_sector_and_offset(area, offset, §or, §or_offset) != sectrue) { - return secfalse; - } - return flash_write_word(sector, sector_offset, data); -} - -secbool flash_area_write_quadword(const flash_area_t *area, uint32_t offset, - const uint32_t *data) { - if (offset % 16 != 0) { - return secfalse; - } - for (int i = 0; i < 4; i++) { - if (sectrue != - flash_area_write_word(area, offset + i * sizeof(uint32_t), data[i])) { - return secfalse; - } - } - return sectrue; -} - -secbool flash_area_write_burst(const flash_area_t *area, uint32_t offset, - const uint32_t *data) { - if (offset % (8 * 16) != 0) { - return secfalse; - } - for (int i = 0; i < (8 * 4); i++) { - if (sectrue != - flash_area_write_word(area, offset + i * sizeof(uint32_t), data[i])) { - return secfalse; - } - } - return sectrue; -} diff --git a/storage/flash_ll.h b/storage/flash_ll.h new file mode 100644 index 00000000000..af573df5075 --- /dev/null +++ b/storage/flash_ll.h @@ -0,0 +1,74 @@ +/* + * 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 . + */ + +#ifndef FLASH_LL_H_ +#define FLASH_LL_H_ + +#include +#include + +// Flash memory low-level API, providing abstraction for +// various flash archictures found on STM32 MCUs + +// The 'sector' parameter in this API can represent +// 1. Non-uniform sector number on STM32F4 +// 2. Uniform page number on STM32U5 + +// Returns the size of the a continuous area of sectors +// Returns 0 if any of the sectors is out of range +uint32_t flash_sector_size(uint16_t first_sector, uint16_t sector_count); + +// Returns number of the sector/page at specified byte 'offset' +// from the beginning of the 'first_sector' +uint16_t flash_sector_find(uint16_t first_sector, uint32_t offset); + +// Returns the physical address of a byte on specified 'offset' in the specified +// 'sector'. Checks if it's possible to access continues space of 'size' bytes +// Returns NULL i [offset, offset + size] is of out of the specified sector +const void *flash_get_address(uint16_t sector, uint32_t offset, uint32_t size); + +// Unlocks the flash memory for writes/erase operations +// Flash must be locked again as soon as possible +secbool __wur flash_unlock_write(void); + +// Locks the flash memory for writes/erase operations +secbool __wur flash_lock_write(void); + +#if defined FLASH_BYTE_ACCESS + +// Writes a single byte to the specified 'offset' inside a flash 'sector' +secbool __wur flash_write_byte(uint16_t sector, uint32_t offset, uint8_t data); + +// Writes a single 32-bit word to the specified 'offset' inside a flash 'sector' +secbool __wur flash_write_word(uint16_t sector, uint32_t offset, uint32_t data); + +#endif + +// Writes a 16-byte block to specified 'offset' inside a flash 'sector' +secbool __wur flash_write_quadword(uint16_t sector, uint32_t offset, + const uint32_t *data); + +// Writes a 16-byte block to specified 'offset' inside a flash 'sector' +secbool __wur flash_write_burst(uint16_t sector, uint32_t offset, + const uint32_t *data); + +// Erases a single sector/page of flash memory +secbool __wur flash_sector_erase(uint16_t sector); + +#endif // FLASH_LL_H diff --git a/storage/norcow.c b/storage/norcow.c index 8bb639518ab..287eed58af6 100644 --- a/storage/norcow.c +++ b/storage/norcow.c @@ -20,7 +20,7 @@ #include #include "common.h" -#include "flash.h" +#include "flash_area.h" #include "norcow.h" // NRC2 = 4e524332 diff --git a/storage/tests/c/Makefile b/storage/tests/c/Makefile index 984faf2d09c..dd22e8bbdaa 100644 --- a/storage/tests/c/Makefile +++ b/storage/tests/c/Makefile @@ -14,7 +14,7 @@ SRC = storage/tests/c/flash.c SRC += storage/tests/c/common.c SRC += storage/tests/c/random_delays.c SRC += storage/tests/c/test_layout.c -SRC += storage/flash_common_f4.c +SRC += storage/flash_area.c SRC += storage/storage.c SRC += storage/norcow.c SRC += crypto/pbkdf2.c diff --git a/storage/tests/c/flash.c b/storage/tests/c/flash.c index 7ca193f8d2a..0b2369fcceb 100644 --- a/storage/tests/c/flash.c +++ b/storage/tests/c/flash.c @@ -63,11 +63,29 @@ secbool flash_unlock_write(void) { return sectrue; } secbool flash_lock_write(void) { return sectrue; } -uint32_t flash_sector_size(uint16_t sector) { - if (sector >= FLASH_SECTOR_COUNT) { +uint32_t flash_sector_size(uint16_t first_sector, uint16_t sector_count) { + if (first_sector + sector_count >= FLASH_SECTOR_COUNT) { return 0; } - return FLASH_SECTOR_TABLE[sector + 1] - FLASH_SECTOR_TABLE[sector]; + return FLASH_SECTOR_TABLE[first_sector + sector_count] - + FLASH_SECTOR_TABLE[first_sector]; +} + +uint16_t flash_sector_find(uint16_t first_sector, uint32_t offset) { + uint16_t sector = first_sector; + + while (sector < FLASH_SECTOR_COUNT) { + uint32_t sector_size = + FLASH_SECTOR_TABLE[sector + 1] - FLASH_SECTOR_TABLE[sector]; + + if (offset < sector_size) { + break; + } + offset -= sector_size; + sector++; + } + + return sector; } const void *flash_get_address(uint16_t sector, uint32_t offset, uint32_t size) { @@ -82,67 +100,56 @@ const void *flash_get_address(uint16_t sector, uint32_t offset, uint32_t size) { return FLASH_BUFFER + addr - FLASH_SECTOR_TABLE[0]; } -secbool flash_area_erase_bulk(const flash_area_t *area, int count, - void (*progress)(int pos, int len)) { - ensure(flash_unlock_write(), NULL); +secbool flash_sector_erase(uint16_t sector) { + if (sector >= FLASH_SECTOR_COUNT) { + return secfalse; + } + const uint32_t offset = FLASH_SECTOR_TABLE[sector] - FLASH_SECTOR_TABLE[0]; + const uint32_t size = + FLASH_SECTOR_TABLE[sector + 1] - FLASH_SECTOR_TABLE[sector]; + memset(FLASH_BUFFER + offset, 0xFF, size); + return sectrue; +} - int total_sectors = 0; - int done_sectors = 0; - for (int a = 0; a < count; a++) { - for (int i = 0; i < area[a].num_subareas; i++) { - total_sectors += area[a].subarea[i].num_sectors; - } +static secbool flash_write(uint16_t sector, uint32_t offset, + const uint8_t *data, size_t data_size) { + // check proper alignment + if ((offset % data_size) != 0) { + return secfalse; } - if (progress) { - progress(0, total_sectors); + uint8_t *flash = (uint8_t *)flash_get_address(sector, offset, data_size); + + if (flash == NULL) { + return secfalse; } - for (int a = 0; a < count; a++) { - for (int s = 0; s < area[a].num_subareas; s++) { - for (int i = 0; i < area[a].subarea[s].num_sectors; i++) { - int sector = area[a].subarea[s].first_sector + i; - - const uint32_t offset = - FLASH_SECTOR_TABLE[sector] - FLASH_SECTOR_TABLE[0]; - const uint32_t size = - FLASH_SECTOR_TABLE[sector + 1] - FLASH_SECTOR_TABLE[sector]; - memset(FLASH_BUFFER + offset, 0xFF, size); - - done_sectors++; - if (progress) { - progress(done_sectors, total_sectors); - } - } + // check if not writing ones to zeroes + for (size_t i = 0; i < data_size; i++) { + if (data[i] != (data[i] & flash[i])) { + return secfalse; } } - ensure(flash_lock_write(), NULL); + + memcpy(flash, data, data_size); + return sectrue; } secbool flash_write_byte(uint16_t sector, uint32_t offset, uint8_t data) { - uint8_t *flash = (uint8_t *)flash_get_address(sector, offset, 1); - if (!flash) { - return secfalse; - } - if ((flash[0] & data) != data) { - return secfalse; // we cannot change zeroes to ones - } - flash[0] = data; - return sectrue; + return flash_write(sector, offset, (uint8_t *)&data, sizeof(uint8_t)); } secbool flash_write_word(uint16_t sector, uint32_t offset, uint32_t data) { - if (offset % 4) { // we write only at 4-byte boundary - return secfalse; - } - uint32_t *flash = (uint32_t *)flash_get_address(sector, offset, sizeof(data)); - if (!flash) { - return secfalse; - } - if ((flash[0] & data) != data) { - return secfalse; // we cannot change zeroes to ones - } - flash[0] = data; - return sectrue; + return flash_write(sector, offset, (uint8_t *)&data, sizeof(uint32_t)); +} + +secbool flash_write_quadword(uint16_t sector, uint32_t offset, + const uint32_t *data) { + return flash_write(sector, offset, (uint8_t *)data, 4 * sizeof(uint32_t)); +} + +secbool flash_write_burst(uint16_t sector, uint32_t offset, + const uint32_t *data) { + return flash_write(sector, offset, (uint8_t *)data, 32 * sizeof(uint32_t)); } diff --git a/storage/tests/c/flash.h b/storage/tests/c/flash.h index 132f85bcc81..38a2ea3845f 100644 --- a/storage/tests/c/flash.h +++ b/storage/tests/c/flash.h @@ -28,13 +28,6 @@ #define FLASH_BYTE_ACCESS 1 #endif -#include "flash_common.h" -#include "test_layout.h" - -uint32_t flash_sector_size(uint16_t sector); - -secbool flash_write_byte(uint16_t sector, uint32_t offset, uint8_t data); - -secbool flash_write_word(uint16_t sector, uint32_t offset, uint32_t data); +#include "flash_ll.h" #endif diff --git a/storage/tests/c/norcow_config.h b/storage/tests/c/norcow_config.h index ea72a111572..f3b22d089f7 100644 --- a/storage/tests/c/norcow_config.h +++ b/storage/tests/c/norcow_config.h @@ -21,6 +21,7 @@ #define __NORCOW_CONFIG_H__ #include "flash.h" +#include "test_layout.h" #define NORCOW_SECTOR_COUNT 2 #define NORCOW_SECTOR_SIZE (64 * 1024) diff --git a/storage/tests/c/test_layout.h b/storage/tests/c/test_layout.h index 1223f198fc4..62e7cd25eed 100644 --- a/storage/tests/c/test_layout.h +++ b/storage/tests/c/test_layout.h @@ -3,7 +3,7 @@ #define STORAGE_AREAS_COUNT 2 -#include "flash_common.h" +#include "flash_area.h" extern const flash_area_t STORAGE_AREAS[STORAGE_AREAS_COUNT];