chore(core, legacy, storage): refactor flash drivers

[no changelog]
pull/3474/head
cepetr 6 months ago committed by TychoVrahe
parent 271bed8bf6
commit 4cf781abb2

@ -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

@ -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

@ -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

@ -91,13 +91,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',
@ -131,6 +127,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',

@ -58,6 +58,7 @@ SOURCE_MOD += [
'vendor/trezor-storage/norcow.c',
'vendor/trezor-storage/storage.c',
'vendor/trezor-storage/storage_utils.c',
'vendor/trezor-storage/flash_area.c',
]
# modtrezorcrypto

@ -71,6 +71,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

@ -65,6 +65,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

@ -59,13 +59,9 @@ SOURCE_MOD += [
'vendor/trezor-storage/norcow.c',
'vendor/trezor-storage/storage.c',
'vendor/trezor-storage/storage_utils.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 += [
@ -383,6 +379,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',

@ -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

@ -26,6 +26,7 @@
#include "display_utils.h"
#include "fault_handlers.h"
#include "flash.h"
#include "flash_otp.h"
#include "image.h"
#include "lowlevel.h"
#include "messages.pb.h"

@ -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"

@ -17,7 +17,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "flash.h"
#include "flash_otp.h"
#include "embed/extmod/trezorobj.h"

@ -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;

@ -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

@ -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,

@ -29,6 +29,7 @@
#include "display_utils.h"
#include "fault_handlers.h"
#include "flash.h"
#include "flash_otp.h"
#include "i2c.h"
#include "model.h"
#include "mpu.h"

@ -22,50 +22,12 @@
#include <stdint.h>
#include <stdlib.h>
#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

@ -0,0 +1,18 @@
#ifndef TREZORHAL_FLASH_OTP_H
#define TREZORHAL_FLASH_OTP_H
#include <common.h>
#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

@ -29,7 +29,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"

@ -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, &sector_error) != HAL_OK) {
ensure(flash_lock_write(), NULL);
return secfalse;
}
uint32_t sector_error;
ensure(flash_lock_write(), NULL);
if (HAL_FLASHEx_Erase(&EraseInitStruct, &sector_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) {
@ -252,51 +198,3 @@ secbool flash_write_block(uint16_t sector, uint32_t offset,
const flash_block_t block) {
return flash_write_word(sector, offset, block[0]);
}
#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));
}

@ -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 <http://www.gnu.org/licenses/>.
*/
#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));
}

@ -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

@ -28,7 +28,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"

@ -19,6 +19,7 @@
#include STM32_HAL_H
#include <stdbool.h>
#include <string.h>
#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, &sector_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;
}
@ -435,132 +207,6 @@ 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;
}
secbool flash_write_block(uint16_t sector, uint32_t offset,
const flash_block_t block) {
return flash_write_quadword(sector, offset, block);

@ -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 <http://www.gnu.org/licenses/>.
*/
#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;
}

@ -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

@ -1,4 +1,5 @@
#include "secret.h"
#include <stdbool.h>
#include <string.h>
#include "common.h"
#include "flash.h"

@ -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);
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;
}
}
if (progress) {
progress(0, total_sectors);
}
while (sector < FLASH_SECTOR_COUNT) {
uint32_t sector_size =
FLASH_SECTOR_TABLE[sector + 1] - FLASH_SECTOR_TABLE[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;
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;
return sector;
}
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) {
uint8_t *flash =
(uint8_t *)flash_get_address(sector_index, 0, sector_size);
memset(flash, 0xFF, sector_size);
*bytes_erased = sector_size;
return sectrue;
}
sector_offset += sector_size;
}
secbool flash_sector_erase(uint16_t sector) {
if (sector >= FLASH_SECTOR_COUNT) {
return secfalse;
}
if (offset == sector_offset) {
return sectrue;
}
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 secfalse;
return sectrue;
}
secbool flash_write_byte(uint16_t sector, uint32_t offset, uint8_t data) {
@ -261,33 +218,3 @@ secbool flash_write_block(uint16_t sector, uint32_t offset,
}
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; }

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#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; }

@ -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);

@ -44,6 +44,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",
@ -53,7 +54,6 @@ def stm32f4_common_files(env, defines, sources, paths):
"embed/trezorhal/stm32f4/rng.c",
"embed/trezorhal/stm32f4/vectortable.s",
"embed/trezorhal/stm32f4/translations.c",
"vendor/trezor-storage/flash_common_f4.c",
]
# boardloader needs separate assembler for some function unencumbered by various FW+bootloader hacks

@ -53,6 +53,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",

@ -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)

@ -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) {
@ -144,42 +163,7 @@ secbool flash_write_block(uint16_t sector, uint32_t offset,
return flash_write_word(sector, offset, block[0]);
}
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;
}

@ -26,7 +26,7 @@
#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)

@ -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'

@ -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;

@ -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)

@ -0,0 +1,244 @@
/*
* This file is part of the Trezor project, https://trezor.io/
*
* Copyright (c) SatoshiLabs
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "flash_area.h"
#include <stddef.h>
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_BIT_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, &sector, &sector_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, &sector, &sector_offset) != sectrue) {
return secfalse;
}
return flash_write_word(sector, sector_offset, data);
}
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_BIT_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, &sector, &sector_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, &sector, &sector_offset) != sectrue) {
return secfalse;
}
return flash_write_burst(sector, sector_offset, data);
}
#endif // not defined FLASH_BIT_ACCESS
secbool flash_area_write_block(const flash_area_t *area, uint32_t offset,
const flash_block_t block) {
if (!FLASH_IS_ALIGNED(offset)) {
return secfalse;
}
uint16_t sector;
uint32_t sector_offset;
if (sectrue != get_sector_and_offset(area, offset, &sector, &sector_offset)) {
return secfalse;
}
return flash_write_block(sector, sector_offset, block);
}
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;
}

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef FLASH_AREA_H
#define FLASH_AREA_H
#include <stdint.h>
#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_BIT_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_write_block(const flash_area_t *area, uint32_t offset,
const flash_block_t block);
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

@ -1,67 +0,0 @@
#ifndef FLASH_COMMON_H
#define FLASH_COMMON_H
#include <stdbool.h>
#include <stdint.h>
#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;
#define FLASH_BLOCK_SIZE (sizeof(uint32_t) * FLASH_BLOCK_WORDS)
typedef uint32_t flash_block_t[FLASH_BLOCK_WORDS];
#if FLASH_BLOCK_WORDS == 1
#define FLASH_ALIGN(X) (((X) + 3) & ~3)
#define FLASH_IS_ALIGNED(X) (((X)&3) == 0)
#elif FLASH_BLOCK_WORDS == 4
#define FLASH_ALIGN(X) (((X) + 0xF) & ~0xF)
#define FLASH_IS_ALIGNED(X) (((X)&0xF) == 0)
#else
#error Unsupported number of FLASH_BLOCK_WORDS.
#endif
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_BIT_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_block(const flash_area_t *area, uint32_t offset,
const flash_block_t block);
secbool flash_write_block(uint16_t sector, uint32_t offset,
const flash_block_t block);
secbool __wur flash_area_write_burst(const flash_area_t *area, uint32_t offset,
const uint32_t *data);
#endif

@ -1,153 +0,0 @@
#include "flash_common.h"
#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, &sector, &sector_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, &sector, &sector_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, &sector, &sector_offset) != sectrue) {
return secfalse;
}
return flash_write_word(sector, sector_offset, data);
}
secbool flash_area_write_block(const flash_area_t *area, uint32_t offset,
const flash_block_t block) {
if (!FLASH_IS_ALIGNED(offset)) {
return secfalse;
}
uint16_t sector;
uint32_t sector_offset;
if (sectrue != get_sector_and_offset(area, offset, &sector, &sector_offset)) {
return secfalse;
}
return flash_write_block(sector, sector_offset, block);
}
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;
}

@ -0,0 +1,93 @@
/*
* This file is part of the Trezor project, https://trezor.io/
*
* Copyright (c) SatoshiLabs
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef FLASH_LL_H_
#define FLASH_LL_H_
#include <secbool.h>
#include <stdint.h>
// 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
#define FLASH_BLOCK_SIZE (sizeof(uint32_t) * FLASH_BLOCK_WORDS)
typedef uint32_t flash_block_t[FLASH_BLOCK_WORDS];
#if FLASH_BLOCK_WORDS == 1
#define FLASH_ALIGN(X) (((X) + 3) & ~3)
#define FLASH_IS_ALIGNED(X) (((X)&3) == 0)
#elif FLASH_BLOCK_WORDS == 4
#define FLASH_ALIGN(X) (((X) + 0xF) & ~0xF)
#define FLASH_IS_ALIGNED(X) (((X)&0xF) == 0)
#else
#error Unsupported number of FLASH_BLOCK_WORDS.
#endif
// 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_BIT_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 128-byte burst 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);
// Writes a block to specified 'offset' inside a flash 'sector'
// Block represents a natural unit of the given flash memory
secbool flash_write_block(uint16_t sector, uint32_t offset,
const flash_block_t block);
#endif // FLASH_LL_H

@ -20,7 +20,7 @@
#include <string.h>
#include "common.h"
#include "flash.h"
#include "flash_area.h"
#include "memzero.h"
#include "norcow.h"
#include "storage_utils.h"

@ -17,7 +17,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "flash_common.h"
#include "flash_area.h"
#define COUNTER_TAIL_WORDS 2
#define NORCOW_MAX_PREFIX_LEN (NORCOW_KEY_LEN + NORCOW_LEN_LEN)

@ -18,7 +18,7 @@
*/
#include <stdbool.h>
#include "flash_common.h"
#include "flash_area.h"
#define COUNTER_TAIL_WORDS 0
// Small items are encoded more efficiently.

@ -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/storage_utils.c
SRC += storage/norcow.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,69 +100,58 @@ 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));
}
secbool flash_write_block(uint16_t sector, uint32_t offset,

@ -22,15 +22,7 @@
#include <stdint.h>
#include <stdlib.h>
#include "flash_ll.h"
#include "secbool.h"
#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);
#endif

@ -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)

@ -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];

Loading…
Cancel
Save