mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-28 09:08:07 +00:00
chore(core, legacy, storage): refactor flash drivers
[no changelog]
This commit is contained in:
parent
271bed8bf6
commit
4cf781abb2
@ -50,6 +50,7 @@ SOURCE_MOD += [
|
|||||||
'vendor/trezor-crypto/ed25519-donna/modm-donna-32bit.c',
|
'vendor/trezor-crypto/ed25519-donna/modm-donna-32bit.c',
|
||||||
'vendor/trezor-crypto/memzero.c',
|
'vendor/trezor-crypto/memzero.c',
|
||||||
'vendor/trezor-crypto/sha2.c',
|
'vendor/trezor-crypto/sha2.c',
|
||||||
|
'vendor/trezor-storage/flash_area.c',
|
||||||
]
|
]
|
||||||
|
|
||||||
# modtrezorui
|
# modtrezorui
|
||||||
|
@ -71,6 +71,7 @@ SOURCE_MOD += [
|
|||||||
'vendor/trezor-crypto/memzero.c',
|
'vendor/trezor-crypto/memzero.c',
|
||||||
'vendor/trezor-crypto/rand.c',
|
'vendor/trezor-crypto/rand.c',
|
||||||
'vendor/trezor-crypto/sha2.c',
|
'vendor/trezor-crypto/sha2.c',
|
||||||
|
'vendor/trezor-storage/flash_area.c',
|
||||||
]
|
]
|
||||||
|
|
||||||
# modtrezorui
|
# modtrezorui
|
||||||
|
@ -66,6 +66,7 @@ SOURCE_MOD += [
|
|||||||
'vendor/trezor-crypto/memzero.c',
|
'vendor/trezor-crypto/memzero.c',
|
||||||
'vendor/trezor-crypto/rand.c',
|
'vendor/trezor-crypto/rand.c',
|
||||||
'vendor/trezor-crypto/sha2.c',
|
'vendor/trezor-crypto/sha2.c',
|
||||||
|
'vendor/trezor-storage/flash_area.c',
|
||||||
]
|
]
|
||||||
|
|
||||||
# modtrezorui
|
# modtrezorui
|
||||||
|
@ -91,11 +91,7 @@ SOURCE_MOD += [
|
|||||||
'vendor/micropython/lib/uzlib/adler32.c',
|
'vendor/micropython/lib/uzlib/adler32.c',
|
||||||
'vendor/micropython/lib/uzlib/crc32.c',
|
'vendor/micropython/lib/uzlib/crc32.c',
|
||||||
'vendor/micropython/lib/uzlib/tinflate.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', ):
|
if TREZOR_MODEL in ('1', ):
|
||||||
@ -131,6 +127,7 @@ SOURCE_TREZORHAL = [
|
|||||||
'embed/trezorhal/unix/display-unix.c',
|
'embed/trezorhal/unix/display-unix.c',
|
||||||
'embed/trezorhal/unix/fault_handlers.c',
|
'embed/trezorhal/unix/fault_handlers.c',
|
||||||
'embed/trezorhal/unix/flash.c',
|
'embed/trezorhal/unix/flash.c',
|
||||||
|
'embed/trezorhal/unix/flash_otp.c',
|
||||||
'embed/trezorhal/unix/common.c',
|
'embed/trezorhal/unix/common.c',
|
||||||
'embed/trezorhal/unix/touch/touch.c',
|
'embed/trezorhal/unix/touch/touch.c',
|
||||||
'embed/trezorhal/unix/rng.c',
|
'embed/trezorhal/unix/rng.c',
|
||||||
|
@ -58,6 +58,7 @@ SOURCE_MOD += [
|
|||||||
'vendor/trezor-storage/norcow.c',
|
'vendor/trezor-storage/norcow.c',
|
||||||
'vendor/trezor-storage/storage.c',
|
'vendor/trezor-storage/storage.c',
|
||||||
'vendor/trezor-storage/storage_utils.c',
|
'vendor/trezor-storage/storage_utils.c',
|
||||||
|
'vendor/trezor-storage/flash_area.c',
|
||||||
]
|
]
|
||||||
|
|
||||||
# modtrezorcrypto
|
# modtrezorcrypto
|
||||||
|
@ -71,6 +71,7 @@ SOURCE_MOD += [
|
|||||||
'vendor/trezor-crypto/secp256k1.c',
|
'vendor/trezor-crypto/secp256k1.c',
|
||||||
'vendor/trezor-crypto/sha2.c',
|
'vendor/trezor-crypto/sha2.c',
|
||||||
'vendor/trezor-crypto/tls_prf.c',
|
'vendor/trezor-crypto/tls_prf.c',
|
||||||
|
'vendor/trezor-storage/flash_area.c',
|
||||||
]
|
]
|
||||||
|
|
||||||
# modtrezorui
|
# modtrezorui
|
||||||
|
@ -65,6 +65,7 @@ SOURCE_MOD += [
|
|||||||
'vendor/micropython/lib/uzlib/adler32.c',
|
'vendor/micropython/lib/uzlib/adler32.c',
|
||||||
'vendor/micropython/lib/uzlib/crc32.c',
|
'vendor/micropython/lib/uzlib/crc32.c',
|
||||||
'vendor/micropython/lib/uzlib/tinflate.c',
|
'vendor/micropython/lib/uzlib/tinflate.c',
|
||||||
|
'vendor/trezor-storage/flash_area.c',
|
||||||
]
|
]
|
||||||
|
|
||||||
# fonts
|
# fonts
|
||||||
|
@ -59,11 +59,7 @@ SOURCE_MOD += [
|
|||||||
'vendor/trezor-storage/norcow.c',
|
'vendor/trezor-storage/norcow.c',
|
||||||
'vendor/trezor-storage/storage.c',
|
'vendor/trezor-storage/storage.c',
|
||||||
'vendor/trezor-storage/storage_utils.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
|
# modtrezorcrypto
|
||||||
@ -383,6 +379,7 @@ SOURCE_UNIX = [
|
|||||||
'embed/trezorhal/unix/common.c',
|
'embed/trezorhal/unix/common.c',
|
||||||
'embed/trezorhal/unix/display-unix.c',
|
'embed/trezorhal/unix/display-unix.c',
|
||||||
'embed/trezorhal/unix/flash.c',
|
'embed/trezorhal/unix/flash.c',
|
||||||
|
'embed/trezorhal/unix/flash_otp.c',
|
||||||
'embed/trezorhal/unix/random_delays.c',
|
'embed/trezorhal/unix/random_delays.c',
|
||||||
'embed/trezorhal/unix/rng.c',
|
'embed/trezorhal/unix/rng.c',
|
||||||
'embed/trezorhal/unix/usb.c',
|
'embed/trezorhal/unix/usb.c',
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "display.h"
|
#include "display.h"
|
||||||
#include "flash.h"
|
#include "flash.h"
|
||||||
|
#include "flash_otp.h"
|
||||||
#include "model.h"
|
#include "model.h"
|
||||||
#include "rust_ui.h"
|
#include "rust_ui.h"
|
||||||
#ifdef USE_OPTIGA
|
#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) {
|
__attribute__((noreturn)) int main(int argc, char **argv) {
|
||||||
flash_init();
|
flash_init();
|
||||||
|
flash_otp_init();
|
||||||
|
|
||||||
FIRMWARE_START = (uint8_t *)flash_area_get_address(&FIRMWARE_AREA, 0, 0);
|
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
|
// simulate non-empty storage so that we know whether it was erased or not
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
#include "display_utils.h"
|
#include "display_utils.h"
|
||||||
#include "fault_handlers.h"
|
#include "fault_handlers.h"
|
||||||
#include "flash.h"
|
#include "flash.h"
|
||||||
|
#include "flash_otp.h"
|
||||||
#include "image.h"
|
#include "image.h"
|
||||||
#include "lowlevel.h"
|
#include "lowlevel.h"
|
||||||
#include "messages.pb.h"
|
#include "messages.pb.h"
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "display.h"
|
#include "display.h"
|
||||||
#include "flash.h"
|
#include "flash.h"
|
||||||
|
#include "flash_otp.h"
|
||||||
#include "image.h"
|
#include "image.h"
|
||||||
#include "mini_printf.h"
|
#include "mini_printf.h"
|
||||||
#include "mpu.h"
|
#include "mpu.h"
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "flash.h"
|
#include "flash_otp.h"
|
||||||
|
|
||||||
#include "embed/extmod/trezorobj.h"
|
#include "embed/extmod/trezorobj.h"
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#include "unit_variant.h"
|
#include "unit_variant.h"
|
||||||
#include "flash.h"
|
#include "flash_otp.h"
|
||||||
#include "model.h"
|
#include "model.h"
|
||||||
|
|
||||||
static uint8_t unit_variant_color = 0;
|
static uint8_t unit_variant_color = 0;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#ifndef LAYOUT_COMMON_H
|
#ifndef LAYOUT_COMMON_H
|
||||||
#define LAYOUT_COMMON_H
|
#define LAYOUT_COMMON_H
|
||||||
|
|
||||||
#include "flash.h"
|
#include "flash_area.h"
|
||||||
|
|
||||||
// OTP blocks allocation
|
// OTP blocks allocation
|
||||||
#define FLASH_OTP_BLOCK_BATCH 0
|
#define FLASH_OTP_BLOCK_BATCH 0
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
const flash_area_t STORAGE_AREAS[STORAGE_AREAS_COUNT] = {
|
const flash_area_t STORAGE_AREAS[STORAGE_AREAS_COUNT] = {
|
||||||
{
|
{
|
||||||
.num_subareas = 1,
|
.num_subareas = 1,
|
||||||
.secure_area = true,
|
|
||||||
.subarea[0] =
|
.subarea[0] =
|
||||||
{
|
{
|
||||||
.first_sector = 0x18,
|
.first_sector = 0x18,
|
||||||
@ -13,7 +12,6 @@ const flash_area_t STORAGE_AREAS[STORAGE_AREAS_COUNT] = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
.num_subareas = 1,
|
.num_subareas = 1,
|
||||||
.secure_area = true,
|
|
||||||
.subarea[0] =
|
.subarea[0] =
|
||||||
{
|
{
|
||||||
.first_sector = 0x20,
|
.first_sector = 0x20,
|
||||||
@ -24,7 +22,6 @@ const flash_area_t STORAGE_AREAS[STORAGE_AREAS_COUNT] = {
|
|||||||
|
|
||||||
const flash_area_t BOARDLOADER_AREA = {
|
const flash_area_t BOARDLOADER_AREA = {
|
||||||
.num_subareas = 1,
|
.num_subareas = 1,
|
||||||
.secure_area = true,
|
|
||||||
.subarea[0] =
|
.subarea[0] =
|
||||||
{
|
{
|
||||||
.first_sector = 1,
|
.first_sector = 1,
|
||||||
@ -34,7 +31,6 @@ const flash_area_t BOARDLOADER_AREA = {
|
|||||||
|
|
||||||
const flash_area_t BOOTLOADER_AREA = {
|
const flash_area_t BOOTLOADER_AREA = {
|
||||||
.num_subareas = 1,
|
.num_subareas = 1,
|
||||||
.secure_area = true,
|
|
||||||
.subarea[0] =
|
.subarea[0] =
|
||||||
{
|
{
|
||||||
.first_sector = 0x08,
|
.first_sector = 0x08,
|
||||||
@ -44,7 +40,6 @@ const flash_area_t BOOTLOADER_AREA = {
|
|||||||
|
|
||||||
const flash_area_t FIRMWARE_AREA = {
|
const flash_area_t FIRMWARE_AREA = {
|
||||||
.num_subareas = 1,
|
.num_subareas = 1,
|
||||||
.secure_area = true,
|
|
||||||
.subarea[0] =
|
.subarea[0] =
|
||||||
{
|
{
|
||||||
.first_sector = 0x28,
|
.first_sector = 0x28,
|
||||||
@ -54,7 +49,6 @@ const flash_area_t FIRMWARE_AREA = {
|
|||||||
|
|
||||||
const flash_area_t SECRET_AREA = {
|
const flash_area_t SECRET_AREA = {
|
||||||
.num_subareas = 1,
|
.num_subareas = 1,
|
||||||
.secure_area = true,
|
|
||||||
.subarea[0] =
|
.subarea[0] =
|
||||||
{
|
{
|
||||||
.first_sector = 0,
|
.first_sector = 0,
|
||||||
@ -64,7 +58,6 @@ const flash_area_t SECRET_AREA = {
|
|||||||
|
|
||||||
const flash_area_t BHK_AREA = {
|
const flash_area_t BHK_AREA = {
|
||||||
.num_subareas = 1,
|
.num_subareas = 1,
|
||||||
.secure_area = true,
|
|
||||||
.subarea[0] =
|
.subarea[0] =
|
||||||
{
|
{
|
||||||
.first_sector = 1,
|
.first_sector = 1,
|
||||||
@ -74,7 +67,6 @@ const flash_area_t BHK_AREA = {
|
|||||||
|
|
||||||
const flash_area_t WIPE_AREA = {
|
const flash_area_t WIPE_AREA = {
|
||||||
.num_subareas = 1,
|
.num_subareas = 1,
|
||||||
.secure_area = true,
|
|
||||||
.subarea[0] =
|
.subarea[0] =
|
||||||
{
|
{
|
||||||
.first_sector = 0x18,
|
.first_sector = 0x18,
|
||||||
@ -84,7 +76,6 @@ const flash_area_t WIPE_AREA = {
|
|||||||
|
|
||||||
const flash_area_t ALL_WIPE_AREA = {
|
const flash_area_t ALL_WIPE_AREA = {
|
||||||
.num_subareas = 1,
|
.num_subareas = 1,
|
||||||
.secure_area = true,
|
|
||||||
.subarea[0] =
|
.subarea[0] =
|
||||||
{
|
{
|
||||||
.first_sector = 0x08,
|
.first_sector = 0x08,
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include "display_utils.h"
|
#include "display_utils.h"
|
||||||
#include "fault_handlers.h"
|
#include "fault_handlers.h"
|
||||||
#include "flash.h"
|
#include "flash.h"
|
||||||
|
#include "flash_otp.h"
|
||||||
#include "i2c.h"
|
#include "i2c.h"
|
||||||
#include "model.h"
|
#include "model.h"
|
||||||
#include "mpu.h"
|
#include "mpu.h"
|
||||||
|
@ -22,50 +22,12 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
#include "secbool.h"
|
#include "secbool.h"
|
||||||
|
|
||||||
#define FLASH_OTP_NUM_BLOCKS 16
|
#include "flash_ll.h"
|
||||||
#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"
|
|
||||||
|
|
||||||
void flash_init(void);
|
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
|
#endif // TREZORHAL_FLASH_H
|
||||||
|
18
core/embed/trezorhal/flash_otp.h
Normal file
18
core/embed/trezorhal/flash_otp.h
Normal file
@ -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
|
#ifdef FANCY_FATAL_ERROR
|
||||||
#include "rust_ui.h"
|
#include "rust_ui.h"
|
||||||
#endif
|
#endif
|
||||||
#include "flash.h"
|
#include "flash_otp.h"
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
#include "rand.h"
|
#include "rand.h"
|
||||||
#include "supervise.h"
|
#include "supervise.h"
|
||||||
|
@ -78,13 +78,41 @@ static const uint32_t FLASH_SECTOR_TABLE[FLASH_SECTOR_COUNT + 1] = {
|
|||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
uint32_t flash_wait_and_clear_status_flags(void) {
|
const void *flash_get_address(uint16_t sector, uint32_t offset, uint32_t size) {
|
||||||
while (FLASH->SR & FLASH_SR_BSY)
|
if (sector >= FLASH_SECTOR_COUNT) {
|
||||||
; // wait for all previous flash operations to complete
|
return NULL;
|
||||||
const uint32_t result =
|
}
|
||||||
FLASH->SR & FLASH_STATUS_ALL_FLAGS; // get the current status flags
|
const uint32_t addr = FLASH_SECTOR_TABLE[sector] + offset;
|
||||||
FLASH->SR |= FLASH_STATUS_ALL_FLAGS; // clear all status flags
|
const uint32_t next = FLASH_SECTOR_TABLE[sector + 1];
|
||||||
return result;
|
if (addr + size > next) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return (const void *)addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
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[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_unlock_write(void) {
|
secbool flash_unlock_write(void) {
|
||||||
@ -98,118 +126,36 @@ secbool flash_lock_write(void) {
|
|||||||
return sectrue;
|
return sectrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const void *flash_get_address(uint16_t sector, uint32_t offset, uint32_t size) {
|
secbool flash_sector_erase(uint16_t sector) {
|
||||||
if (sector >= FLASH_SECTOR_COUNT) {
|
if (sector >= FLASH_SECTOR_COUNT) {
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
const uint32_t addr = FLASH_SECTOR_TABLE[sector] + offset;
|
|
||||||
const uint32_t next = FLASH_SECTOR_TABLE[sector + 1];
|
|
||||||
if (addr + size > next) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
return (const void *)addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t flash_sector_size(uint16_t sector) {
|
|
||||||
if (sector >= FLASH_SECTOR_COUNT) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return FLASH_SECTOR_TABLE[sector + 1] - FLASH_SECTOR_TABLE[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;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
EraseInitStruct.Sector = sector;
|
|
||||||
uint32_t SectorError;
|
|
||||||
if (HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError) != HAL_OK) {
|
|
||||||
ensure(flash_lock_write(), NULL);
|
|
||||||
return secfalse;
|
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);
|
|
||||||
return sectrue;
|
|
||||||
}
|
|
||||||
|
|
||||||
secbool flash_area_erase_partial(const flash_area_t *area, uint32_t offset,
|
FLASH_EraseInitTypeDef EraseInitStruct = {
|
||||||
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);
|
|
||||||
|
|
||||||
FLASH_EraseInitTypeDef erase_init = {
|
|
||||||
.TypeErase = FLASH_TYPEERASE_SECTORS,
|
.TypeErase = FLASH_TYPEERASE_SECTORS,
|
||||||
.VoltageRange = FLASH_VOLTAGE_RANGE_3,
|
.VoltageRange = FLASH_VOLTAGE_RANGE_3,
|
||||||
.Sector = sector_index,
|
.Sector = sector,
|
||||||
.NbSectors = 1};
|
.NbSectors = 1,
|
||||||
|
};
|
||||||
|
|
||||||
uint32_t sector_error;
|
uint32_t sector_error;
|
||||||
|
|
||||||
if (HAL_FLASHEx_Erase(&erase_init, §or_error) != HAL_OK) {
|
if (HAL_FLASHEx_Erase(&EraseInitStruct, §or_error) != HAL_OK) {
|
||||||
ensure(flash_lock_write(), NULL);
|
|
||||||
return secfalse;
|
return secfalse;
|
||||||
}
|
}
|
||||||
|
|
||||||
ensure(flash_lock_write(), NULL);
|
// check whether the sector was really deleted (contains only 0xFF)
|
||||||
|
uint32_t addr_start = FLASH_SECTOR_TABLE[sector];
|
||||||
*bytes_erased = sector_size;
|
uint32_t addr_end = FLASH_SECTOR_TABLE[sector + 1];
|
||||||
return sectrue;
|
|
||||||
}
|
|
||||||
|
|
||||||
sector_offset += sector_size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (offset == sector_offset) {
|
|
||||||
return sectrue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
for (uint32_t addr = addr_start; addr < addr_end; addr += 4) {
|
||||||
|
if (*((const uint32_t *)addr) != 0xFFFFFFFF) {
|
||||||
return secfalse;
|
return secfalse;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sectrue;
|
||||||
|
}
|
||||||
|
|
||||||
secbool flash_write_byte(uint16_t sector, uint32_t offset, uint8_t data) {
|
secbool flash_write_byte(uint16_t sector, uint32_t offset, uint8_t data) {
|
||||||
uint32_t address = (uint32_t)flash_get_address(sector, offset, 1);
|
uint32_t address = (uint32_t)flash_get_address(sector, offset, 1);
|
||||||
@ -252,51 +198,3 @@ secbool flash_write_block(uint16_t sector, uint32_t offset,
|
|||||||
const flash_block_t block) {
|
const flash_block_t block) {
|
||||||
return flash_write_word(sector, offset, block[0]);
|
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));
|
|
||||||
}
|
|
||||||
|
76
core/embed/trezorhal/stm32f4/flash_otp.c
Normal file
76
core/embed/trezorhal/stm32f4/flash_otp.c
Normal file
@ -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 STM32_HAL_H
|
||||||
|
|
||||||
#include "lowlevel.h"
|
#include "lowlevel.h"
|
||||||
#include "flash.h"
|
#include "flash_otp.h"
|
||||||
|
|
||||||
#pragma GCC optimize( \
|
#pragma GCC optimize( \
|
||||||
"no-stack-protector") // applies to all functions in this file
|
"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_BANK1_WRP (*(volatile uint16_t* const)0x1FFFC008U)
|
||||||
#define OPTION_BYTES_BANK2_WRP (*(volatile uint16_t* const)0x1FFEC008U)
|
#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) {
|
secbool flash_check_option_bytes(void) {
|
||||||
flash_wait_and_clear_status_flags();
|
flash_wait_and_clear_status_flags();
|
||||||
// check values stored in flash interface registers
|
// check values stored in flash interface registers
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
#ifdef FANCY_FATAL_ERROR
|
#ifdef FANCY_FATAL_ERROR
|
||||||
#include "rust_ui.h"
|
#include "rust_ui.h"
|
||||||
#endif
|
#endif
|
||||||
#include "flash.h"
|
#include "flash_otp.h"
|
||||||
#include "model.h"
|
#include "model.h"
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
#include "rand.h"
|
#include "rand.h"
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include STM32_HAL_H
|
#include STM32_HAL_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
@ -33,22 +34,37 @@
|
|||||||
#define FLASH_STATUS_ALL_FLAGS \
|
#define FLASH_STATUS_ALL_FLAGS \
|
||||||
(FLASH_NSSR_PGSERR | FLASH_NSSR_PGAERR | FLASH_NSSR_WRPERR | FLASH_NSSR_EOP)
|
(FLASH_NSSR_PGSERR | FLASH_NSSR_PGAERR | FLASH_NSSR_WRPERR | FLASH_NSSR_EOP)
|
||||||
|
|
||||||
uint32_t flash_wait_and_clear_status_flags(void) {
|
static bool flash_sector_is_secure(uint32_t sector) {
|
||||||
while (FLASH->NSSR & FLASH_NSSR_BSY)
|
// We always return true since the entire flash memory is currently secure -
|
||||||
; // wait for all previous flash operations to complete
|
// partially through option bytes and partially through FLASH controller
|
||||||
|
// settings
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t result =
|
const void *flash_get_address(uint16_t sector, uint32_t offset, uint32_t size) {
|
||||||
FLASH->NSSR & FLASH_STATUS_ALL_FLAGS; // get the current status flags
|
if (sector >= FLASH_SECTOR_COUNT) {
|
||||||
FLASH->NSSR |= FLASH_STATUS_ALL_FLAGS; // clear all status flags
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U)
|
if (offset + size > FLASH_PAGE_SIZE) {
|
||||||
while (FLASH->SECSR & FLASH_SECSR_BSY)
|
return NULL;
|
||||||
; // wait for all previous flash operations to complete
|
}
|
||||||
result |=
|
|
||||||
FLASH->SECSR & FLASH_STATUS_ALL_FLAGS; // get the current status flags
|
uint32_t base_addr = flash_sector_is_secure(sector) ? FLASH_SEC_START_ADDRESS
|
||||||
FLASH->SECSR |= FLASH_STATUS_ALL_FLAGS; // clear all status flags
|
: FLASH_START_ADDRESS;
|
||||||
#endif
|
|
||||||
return result;
|
return (const void *)(base_addr + FLASH_PAGE_SIZE * sector + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
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_PAGE_SIZE * sector_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t flash_sector_find(uint16_t first_sector, uint32_t offset) {
|
||||||
|
return first_sector + offset / FLASH_PAGE_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
secbool flash_unlock_write(void) {
|
secbool flash_unlock_write(void) {
|
||||||
@ -65,297 +81,53 @@ secbool flash_lock_write(void) {
|
|||||||
return sectrue;
|
return sectrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const void *flash_get_address(uint16_t sector, uint32_t offset, uint32_t size,
|
secbool flash_sector_erase(uint16_t sector) {
|
||||||
bool secure_area) {
|
|
||||||
if (sector >= FLASH_SECTOR_COUNT) {
|
if (sector >= FLASH_SECTOR_COUNT) {
|
||||||
return NULL;
|
return secfalse;
|
||||||
}
|
|
||||||
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;
|
FLASH_EraseInitTypeDef EraseInitStruct = {
|
||||||
const uint32_t next = base_addr + (FLASH_PAGE_SIZE * (sector + 1));
|
.TypeErase = FLASH_TYPEERASE_PAGES_NS,
|
||||||
if (addr + size > next) {
|
.Banks = FLASH_BANK_1,
|
||||||
return NULL;
|
.Page = sector,
|
||||||
}
|
.NbPages = 1,
|
||||||
return (const void *)addr;
|
};
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t flash_sector_size(uint16_t sector) {
|
if (sector >= 256) {
|
||||||
if (sector >= FLASH_SECTOR_COUNT) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return FLASH_PAGE_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t flash_subarea_get_size(const flash_subarea_t *sub) {
|
|
||||||
return FLASH_PAGE_SIZE * sub->num_sectors;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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_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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (progress) {
|
|
||||||
progress(0, total_pages);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
EraseInitStruct.Banks = FLASH_BANK_2;
|
||||||
page_idx = page - 256;
|
EraseInitStruct.Page = sector - 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) {
|
if (flash_sector_is_secure(sector)) {
|
||||||
EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES_NS;
|
|
||||||
secure = false;
|
|
||||||
} else {
|
|
||||||
EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;
|
EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;
|
||||||
}
|
}
|
||||||
|
|
||||||
EraseInitStruct.Page = page_idx;
|
uint32_t sector_error = 0;
|
||||||
uint32_t SectorError;
|
|
||||||
if (HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError) != HAL_OK) {
|
if (HAL_FLASHEx_Erase(&EraseInitStruct, §or_error) != HAL_OK) {
|
||||||
ensure(flash_lock_write(), NULL);
|
|
||||||
return secfalse;
|
return secfalse;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check whether the sector was really deleted (contains only 0xFF)
|
// check whether the sector was really deleted (contains only 0xFF)
|
||||||
const uint32_t addr_start =
|
const uint32_t *sector_start =
|
||||||
(uint32_t)flash_get_address(page, 0, 0, secure);
|
(const uint32_t *)flash_get_address(sector, 0, 0);
|
||||||
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) {
|
const uint32_t *sector_end =
|
||||||
if (*((const uint32_t *)addr) != 0xFFFFFFFF) {
|
sector_start + flash_sector_size(sector, 1) / sizeof(uint32_t);
|
||||||
ensure(flash_lock_write(), NULL);
|
|
||||||
|
for (const uint32_t *addr = sector_start; addr < sector_end; addr++) {
|
||||||
|
if (*addr != 0xFFFFFFFF) {
|
||||||
return secfalse;
|
return secfalse;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
done_pages++;
|
|
||||||
if (progress && done_pages % 16 == 0) {
|
|
||||||
progress(done_pages, total_pages);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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 (progress) {
|
|
||||||
progress(0, total_pages);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ensure(flash_lock_write(), NULL);
|
|
||||||
*bytes_erased = FLASH_PAGE_SIZE;
|
|
||||||
return sectrue;
|
|
||||||
}
|
|
||||||
ensure(flash_lock_write(), NULL);
|
|
||||||
return sectrue;
|
return sectrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
secbool flash_write_quadword(uint16_t sector, uint32_t offset,
|
secbool flash_write_quadword(uint16_t sector, uint32_t offset,
|
||||||
const uint32_t *data, bool secure_area) {
|
const uint32_t *data) {
|
||||||
uint32_t address = (uint32_t)flash_get_address(
|
uint32_t address =
|
||||||
sector, offset, 4 * sizeof(uint32_t), secure_area);
|
(uint32_t)flash_get_address(sector, offset, 4 * sizeof(uint32_t));
|
||||||
if (address == 0) {
|
if (address == 0) {
|
||||||
return secfalse;
|
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,
|
secbool flash_write_burst(uint16_t sector, uint32_t offset,
|
||||||
const uint32_t *data, bool secure_area) {
|
const uint32_t *data) {
|
||||||
uint32_t address = (uint32_t)flash_get_address(
|
uint32_t address =
|
||||||
sector, offset, 8 * 4 * sizeof(uint32_t), secure_area);
|
(uint32_t)flash_get_address(sector, offset, 8 * 4 * sizeof(uint32_t));
|
||||||
if (address == 0) {
|
if (address == 0) {
|
||||||
return secfalse;
|
return secfalse;
|
||||||
}
|
}
|
||||||
@ -435,132 +207,6 @@ secbool flash_write_burst(uint16_t sector, uint32_t offset,
|
|||||||
return sectrue;
|
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,
|
secbool flash_write_block(uint16_t sector, uint32_t offset,
|
||||||
const flash_block_t block) {
|
const flash_block_t block) {
|
||||||
return flash_write_quadword(sector, offset, block);
|
return flash_write_quadword(sector, offset, block);
|
||||||
|
92
core/embed/trezorhal/stm32u5/flash_otp.c
Normal file
92
core/embed/trezorhal/stm32u5/flash_otp.c
Normal file
@ -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 "lowlevel.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "flash.h"
|
#include "flash.h"
|
||||||
|
#include "flash_otp.h"
|
||||||
#include "model.h"
|
#include "model.h"
|
||||||
#include TREZOR_BOARD
|
#include TREZOR_BOARD
|
||||||
|
|
||||||
@ -84,6 +85,27 @@
|
|||||||
SEC_AREA_2_PAGE_END << FLASH_SECWM1R1_SECWM1_PEND_Pos | 0xFF00FF00)
|
SEC_AREA_2_PAGE_END << FLASH_SECWM1R1_SECWM1_PEND_Pos | 0xFF00FF00)
|
||||||
#define FLASH_SECWM2R2_VALUE (0x7F007F00)
|
#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) {
|
secbool flash_check_option_bytes(void) {
|
||||||
flash_wait_and_clear_status_flags();
|
flash_wait_and_clear_status_flags();
|
||||||
// check values stored in flash interface registers
|
// check values stored in flash interface registers
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include "secret.h"
|
#include "secret.h"
|
||||||
|
#include <stdbool.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "flash.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 uint8_t *FLASH_BUFFER = NULL;
|
||||||
static uint32_t FLASH_SIZE;
|
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) {
|
static void flash_exit(void) {
|
||||||
int r = munmap(FLASH_BUFFER, FLASH_SIZE);
|
int r = munmap(FLASH_BUFFER, FLASH_SIZE);
|
||||||
ensure(sectrue * (r == 0), "munmap failed");
|
ensure(sectrue * (r == 0), "munmap failed");
|
||||||
@ -123,9 +118,6 @@ void flash_init(void) {
|
|||||||
|
|
||||||
FLASH_BUFFER = (uint8_t *)map;
|
FLASH_BUFFER = (uint8_t *)map;
|
||||||
|
|
||||||
// fill OTP buffer with ones
|
|
||||||
memset(OTP_BUFFER, 0xFF, sizeof(OTP_BUFFER));
|
|
||||||
|
|
||||||
atexit(flash_exit);
|
atexit(flash_exit);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,81 +137,46 @@ const void *flash_get_address(uint16_t sector, uint32_t offset, uint32_t size) {
|
|||||||
return FLASH_BUFFER + addr - FLASH_SECTOR_TABLE[0];
|
return FLASH_BUFFER + addr - FLASH_SECTOR_TABLE[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t flash_sector_size(uint16_t sector) {
|
uint32_t flash_sector_size(uint16_t first_sector, uint16_t sector_count) {
|
||||||
if (sector >= FLASH_SECTOR_COUNT) {
|
if (first_sector + sector_count > FLASH_SECTOR_COUNT) {
|
||||||
return 0;
|
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,
|
uint16_t flash_sector_find(uint16_t first_sector, uint32_t offset) {
|
||||||
void (*progress)(int pos, int len)) {
|
uint16_t sector = first_sector;
|
||||||
ensure(flash_unlock_write(), NULL);
|
|
||||||
|
|
||||||
int total_sectors = 0;
|
while (sector < FLASH_SECTOR_COUNT) {
|
||||||
int done_sectors = 0;
|
uint32_t sector_size =
|
||||||
for (int a = 0; a < count; a++) {
|
FLASH_SECTOR_TABLE[sector + 1] - FLASH_SECTOR_TABLE[sector];
|
||||||
for (int i = 0; i < area[a].num_subareas; i++) {
|
|
||||||
total_sectors += area[a].subarea[i].num_sectors;
|
if (offset < sector_size) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
offset -= sector_size;
|
||||||
|
sector++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (progress) {
|
return sector;
|
||||||
progress(0, total_sectors);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int a = 0; a < count; a++) {
|
secbool flash_sector_erase(uint16_t sector) {
|
||||||
for (int s = 0; s < area[a].num_subareas; s++) {
|
if (sector >= FLASH_SECTOR_COUNT) {
|
||||||
for (int i = 0; i < area[a].subarea[s].num_sectors; i++) {
|
return secfalse;
|
||||||
int sector = area[a].subarea[s].first_sector + i;
|
}
|
||||||
|
|
||||||
|
const uint32_t offset = FLASH_SECTOR_TABLE[sector] - FLASH_SECTOR_TABLE[0];
|
||||||
|
|
||||||
const uint32_t offset =
|
|
||||||
FLASH_SECTOR_TABLE[sector] - FLASH_SECTOR_TABLE[0];
|
|
||||||
const uint32_t size =
|
const uint32_t size =
|
||||||
FLASH_SECTOR_TABLE[sector + 1] - FLASH_SECTOR_TABLE[sector];
|
FLASH_SECTOR_TABLE[sector + 1] - FLASH_SECTOR_TABLE[sector];
|
||||||
|
|
||||||
memset(FLASH_BUFFER + offset, 0xFF, size);
|
memset(FLASH_BUFFER + offset, 0xFF, size);
|
||||||
|
|
||||||
done_sectors++;
|
|
||||||
if (progress) {
|
|
||||||
progress(done_sectors, total_sectors);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ensure(flash_lock_write(), NULL);
|
|
||||||
return sectrue;
|
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) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (offset == sector_offset) {
|
|
||||||
return sectrue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return secfalse;
|
|
||||||
}
|
|
||||||
|
|
||||||
secbool flash_write_byte(uint16_t sector, uint32_t offset, uint8_t data) {
|
secbool flash_write_byte(uint16_t sector, uint32_t offset, uint8_t data) {
|
||||||
uint8_t *flash = (uint8_t *)flash_get_address(sector, offset, 1);
|
uint8_t *flash = (uint8_t *)flash_get_address(sector, offset, 1);
|
||||||
if (!flash) {
|
if (!flash) {
|
||||||
@ -261,33 +218,3 @@ secbool flash_write_block(uint16_t sector, uint32_t offset,
|
|||||||
}
|
}
|
||||||
return sectrue;
|
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; }
|
|
||||||
|
62
core/embed/trezorhal/unix/flash_otp.c
Normal file
62
core/embed/trezorhal/unix/flash_otp.c
Normal file
@ -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/misc.h"
|
||||||
#include "extmod/vfs_posix.h"
|
#include "extmod/vfs_posix.h"
|
||||||
#include "flash.h"
|
#include "flash.h"
|
||||||
|
#include "flash_otp.h"
|
||||||
#include "genhdr/mpversion.h"
|
#include "genhdr/mpversion.h"
|
||||||
#include "input.h"
|
#include "input.h"
|
||||||
#include "py/builtin.h"
|
#include "py/builtin.h"
|
||||||
@ -482,6 +483,7 @@ MP_NOINLINE int main_(int argc, char **argv) {
|
|||||||
|
|
||||||
// Map trezor.flash to memory.
|
// Map trezor.flash to memory.
|
||||||
flash_init();
|
flash_init();
|
||||||
|
flash_otp_init();
|
||||||
|
|
||||||
#if MICROPY_ENABLE_GC
|
#if MICROPY_ENABLE_GC
|
||||||
char *heap = malloc(heap_size);
|
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/common.c",
|
||||||
"embed/trezorhal/stm32f4/fault_handlers.c",
|
"embed/trezorhal/stm32f4/fault_handlers.c",
|
||||||
"embed/trezorhal/stm32f4/flash.c",
|
"embed/trezorhal/stm32f4/flash.c",
|
||||||
|
"embed/trezorhal/stm32f4/flash_otp.c",
|
||||||
"embed/trezorhal/stm32f4/lowlevel.c",
|
"embed/trezorhal/stm32f4/lowlevel.c",
|
||||||
"embed/trezorhal/stm32f4/mpu.c",
|
"embed/trezorhal/stm32f4/mpu.c",
|
||||||
"embed/trezorhal/stm32f4/platform.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/rng.c",
|
||||||
"embed/trezorhal/stm32f4/vectortable.s",
|
"embed/trezorhal/stm32f4/vectortable.s",
|
||||||
"embed/trezorhal/stm32f4/translations.c",
|
"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
|
# 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/common.c",
|
||||||
"embed/trezorhal/stm32u5/fault_handlers.c",
|
"embed/trezorhal/stm32u5/fault_handlers.c",
|
||||||
"embed/trezorhal/stm32u5/flash.c",
|
"embed/trezorhal/stm32u5/flash.c",
|
||||||
|
"embed/trezorhal/stm32u5/flash_otp.c",
|
||||||
"embed/trezorhal/stm32u5/lowlevel.c",
|
"embed/trezorhal/stm32u5/lowlevel.c",
|
||||||
"embed/trezorhal/stm32u5/mpu.c",
|
"embed/trezorhal/stm32u5/mpu.c",
|
||||||
"embed/trezorhal/stm32u5/platform.c",
|
"embed/trezorhal/stm32u5/platform.c",
|
||||||
|
@ -20,7 +20,7 @@ OBJS += usb_standard.o
|
|||||||
OBJS += util.o
|
OBJS += util.o
|
||||||
OBJS += webusb.o
|
OBJS += webusb.o
|
||||||
OBJS += winusb.o
|
OBJS += winusb.o
|
||||||
OBJS += vendor/trezor-storage/flash_common_f4.o
|
OBJS += vendor/trezor-storage/flash_area.o
|
||||||
|
|
||||||
libtrezor.a: $(OBJS)
|
libtrezor.a: $(OBJS)
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "flash.h"
|
#include "flash.h"
|
||||||
|
#include "flash_area.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "supervise.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);
|
return (const void *)FLASH_PTR(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t flash_sector_size(uint16_t sector) {
|
uint32_t flash_sector_size(uint16_t first_sector, uint16_t sector_count) {
|
||||||
if (sector >= FLASH_SECTOR_COUNT) {
|
if (first_sector + sector_count >= FLASH_SECTOR_COUNT) {
|
||||||
return 0;
|
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) {
|
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]);
|
return flash_write_word(sector, offset, block[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
secbool flash_area_erase_bulk(const flash_area_t *area, int count,
|
secbool flash_sector_erase(uint16_t sector) {
|
||||||
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);
|
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);
|
|
||||||
return sectrue;
|
return sectrue;
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
|
|
||||||
#define FLASH_SECTOR_COUNT 24
|
#define FLASH_SECTOR_COUNT 24
|
||||||
|
|
||||||
#include "flash_common.h"
|
#include "flash_ll.h"
|
||||||
|
|
||||||
// note: FLASH_SR_RDERR is STM32F42xxx and STM32F43xxx specific (STM32F427)
|
// note: FLASH_SR_RDERR is STM32F42xxx and STM32F43xxx specific (STM32F427)
|
||||||
// (reference RM0090 section 3.7.5)
|
// (reference RM0090 section 3.7.5)
|
||||||
|
@ -35,8 +35,6 @@
|
|||||||
#include "timer.h"
|
#include "timer.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
const void *flash_get_address(uint16_t sector, uint32_t offset, uint32_t size);
|
|
||||||
|
|
||||||
// legacy storage magic
|
// legacy storage magic
|
||||||
#define LEGACY_STORAGE_SECTOR 2
|
#define LEGACY_STORAGE_SECTOR 2
|
||||||
static const uint32_t META_MAGIC_V10 = 0x525a5254; // 'TRZR'
|
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_1 (*(const uint64_t *)0x1FFFC000)
|
||||||
#define FLASH_OPTION_BYTES_2 (*(const uint64_t *)0x1FFFC008)
|
#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) {
|
void memory_protect(void) {
|
||||||
#if PRODUCTION
|
#if PRODUCTION
|
||||||
#if BOOTLOADER_QA
|
#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++) {
|
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);
|
const void *data = flash_get_address(i, 0, size);
|
||||||
if (data == NULL) {
|
if (data == NULL) {
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
#ifndef __NORCOW_CONFIG_H__
|
#ifndef __NORCOW_CONFIG_H__
|
||||||
#define __NORCOW_CONFIG_H__
|
#define __NORCOW_CONFIG_H__
|
||||||
|
|
||||||
#include "flash.h"
|
#include "flash_area.h"
|
||||||
|
|
||||||
#define NORCOW_SECTOR_COUNT 2
|
#define NORCOW_SECTOR_COUNT 2
|
||||||
#define NORCOW_SECTOR_SIZE (16 * 1024)
|
#define NORCOW_SECTOR_SIZE (16 * 1024)
|
||||||
|
244
storage/flash_area.c
Normal file
244
storage/flash_area.c
Normal file
@ -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, §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_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, §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_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, §or, §or_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;
|
||||||
|
}
|
92
storage/flash_area.h
Normal file
92
storage/flash_area.h
Normal file
@ -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, §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_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, §or, §or_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;
|
|
||||||
}
|
|
93
storage/flash_ll.h
Normal file
93
storage/flash_ll.h
Normal file
@ -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 <string.h>
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "flash.h"
|
#include "flash_area.h"
|
||||||
#include "memzero.h"
|
#include "memzero.h"
|
||||||
#include "norcow.h"
|
#include "norcow.h"
|
||||||
#include "storage_utils.h"
|
#include "storage_utils.h"
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* 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 COUNTER_TAIL_WORDS 2
|
||||||
#define NORCOW_MAX_PREFIX_LEN (NORCOW_KEY_LEN + NORCOW_LEN_LEN)
|
#define NORCOW_MAX_PREFIX_LEN (NORCOW_KEY_LEN + NORCOW_LEN_LEN)
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include "flash_common.h"
|
#include "flash_area.h"
|
||||||
|
|
||||||
#define COUNTER_TAIL_WORDS 0
|
#define COUNTER_TAIL_WORDS 0
|
||||||
// Small items are encoded more efficiently.
|
// 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/common.c
|
||||||
SRC += storage/tests/c/random_delays.c
|
SRC += storage/tests/c/random_delays.c
|
||||||
SRC += storage/tests/c/test_layout.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.c
|
||||||
SRC += storage/storage_utils.c
|
SRC += storage/storage_utils.c
|
||||||
SRC += storage/norcow.c
|
SRC += storage/norcow.c
|
||||||
|
@ -63,11 +63,29 @@ secbool flash_unlock_write(void) { return sectrue; }
|
|||||||
|
|
||||||
secbool flash_lock_write(void) { return sectrue; }
|
secbool flash_lock_write(void) { return sectrue; }
|
||||||
|
|
||||||
uint32_t flash_sector_size(uint16_t sector) {
|
uint32_t flash_sector_size(uint16_t first_sector, uint16_t sector_count) {
|
||||||
if (sector >= FLASH_SECTOR_COUNT) {
|
if (first_sector + sector_count >= FLASH_SECTOR_COUNT) {
|
||||||
return 0;
|
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) {
|
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];
|
return FLASH_BUFFER + addr - FLASH_SECTOR_TABLE[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
secbool flash_area_erase_bulk(const flash_area_t *area, int count,
|
secbool flash_sector_erase(uint16_t sector) {
|
||||||
void (*progress)(int pos, int len)) {
|
if (sector >= FLASH_SECTOR_COUNT) {
|
||||||
ensure(flash_unlock_write(), NULL);
|
return secfalse;
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
const uint32_t offset = FLASH_SECTOR_TABLE[sector] - FLASH_SECTOR_TABLE[0];
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
const uint32_t offset =
|
|
||||||
FLASH_SECTOR_TABLE[sector] - FLASH_SECTOR_TABLE[0];
|
|
||||||
const uint32_t size =
|
const uint32_t size =
|
||||||
FLASH_SECTOR_TABLE[sector + 1] - FLASH_SECTOR_TABLE[sector];
|
FLASH_SECTOR_TABLE[sector + 1] - FLASH_SECTOR_TABLE[sector];
|
||||||
memset(FLASH_BUFFER + offset, 0xFF, size);
|
memset(FLASH_BUFFER + offset, 0xFF, size);
|
||||||
|
return sectrue;
|
||||||
|
}
|
||||||
|
|
||||||
done_sectors++;
|
static secbool flash_write(uint16_t sector, uint32_t offset,
|
||||||
if (progress) {
|
const uint8_t *data, size_t data_size) {
|
||||||
progress(done_sectors, total_sectors);
|
// check proper alignment
|
||||||
|
if ((offset % data_size) != 0) {
|
||||||
|
return secfalse;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *flash = (uint8_t *)flash_get_address(sector, offset, data_size);
|
||||||
|
|
||||||
|
if (flash == NULL) {
|
||||||
|
return secfalse;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
memcpy(flash, data, data_size);
|
||||||
ensure(flash_lock_write(), NULL);
|
|
||||||
return sectrue;
|
return sectrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
secbool flash_write_byte(uint16_t sector, uint32_t offset, uint8_t data) {
|
secbool flash_write_byte(uint16_t sector, uint32_t offset, uint8_t data) {
|
||||||
uint8_t *flash = (uint8_t *)flash_get_address(sector, offset, 1);
|
return flash_write(sector, offset, (uint8_t *)&data, sizeof(uint8_t));
|
||||||
if (!flash) {
|
|
||||||
return secfalse;
|
|
||||||
}
|
|
||||||
if ((flash[0] & data) != data) {
|
|
||||||
return secfalse; // we cannot change zeroes to ones
|
|
||||||
}
|
|
||||||
flash[0] = data;
|
|
||||||
return sectrue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
secbool flash_write_word(uint16_t sector, uint32_t offset, uint32_t data) {
|
secbool flash_write_word(uint16_t sector, uint32_t offset, uint32_t data) {
|
||||||
if (offset % 4) { // we write only at 4-byte boundary
|
return flash_write(sector, offset, (uint8_t *)&data, sizeof(uint32_t));
|
||||||
return secfalse;
|
|
||||||
}
|
}
|
||||||
uint32_t *flash = (uint32_t *)flash_get_address(sector, offset, sizeof(data));
|
|
||||||
if (!flash) {
|
secbool flash_write_quadword(uint16_t sector, uint32_t offset,
|
||||||
return secfalse;
|
const uint32_t *data) {
|
||||||
|
return flash_write(sector, offset, (uint8_t *)data, 4 * sizeof(uint32_t));
|
||||||
}
|
}
|
||||||
if ((flash[0] & data) != data) {
|
|
||||||
return secfalse; // we cannot change zeroes to ones
|
secbool flash_write_burst(uint16_t sector, uint32_t offset,
|
||||||
}
|
const uint32_t *data) {
|
||||||
flash[0] = data;
|
return flash_write(sector, offset, (uint8_t *)data, 32 * sizeof(uint32_t));
|
||||||
return sectrue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
secbool flash_write_block(uint16_t sector, uint32_t offset,
|
secbool flash_write_block(uint16_t sector, uint32_t offset,
|
||||||
|
@ -22,15 +22,7 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include "flash_ll.h"
|
||||||
#include "secbool.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
|
#endif
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#define __NORCOW_CONFIG_H__
|
#define __NORCOW_CONFIG_H__
|
||||||
|
|
||||||
#include "flash.h"
|
#include "flash.h"
|
||||||
|
#include "test_layout.h"
|
||||||
|
|
||||||
#define NORCOW_SECTOR_COUNT 2
|
#define NORCOW_SECTOR_COUNT 2
|
||||||
#define NORCOW_SECTOR_SIZE (64 * 1024)
|
#define NORCOW_SECTOR_SIZE (64 * 1024)
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#define STORAGE_AREAS_COUNT 2
|
#define STORAGE_AREAS_COUNT 2
|
||||||
|
|
||||||
#include "flash_common.h"
|
#include "flash_area.h"
|
||||||
|
|
||||||
extern const flash_area_t STORAGE_AREAS[STORAGE_AREAS_COUNT];
|
extern const flash_area_t STORAGE_AREAS[STORAGE_AREAS_COUNT];
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user