parent
271bed8bf6
commit
4cf781abb2
@ -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
|
@ -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));
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
@ -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; }
|
@ -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;
|
||||||
|
}
|
@ -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;
|
|
||||||
}
|
|
@ -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
|
Loading…
Reference in new issue