From 440b1304c065c6e231c41fd8e513035fabf7c5c6 Mon Sep 17 00:00:00 2001 From: Andrew Kozlik Date: Wed, 27 Dec 2023 00:29:30 +0100 Subject: [PATCH] refactor(storage): Split implementations based on FLASH_BIT_ACCESS. [no changelog] --- core/embed/trezorhal/stm32f4/platform.h | 2 +- core/embed/trezorhal/unix/platform.h | 2 +- legacy/flash.h | 2 +- storage/flash_common.h | 2 +- storage/norcow.c | 415 +----------------------- storage/norcow_bitwise.h | 214 ++++++++++++ storage/norcow_blockwise.h | 283 ++++++++++++++++ storage/tests/c/flash.h | 2 +- 8 files changed, 518 insertions(+), 404 deletions(-) create mode 100644 storage/norcow_bitwise.h create mode 100644 storage/norcow_blockwise.h diff --git a/core/embed/trezorhal/stm32f4/platform.h b/core/embed/trezorhal/stm32f4/platform.h index d41af2d43d..6f8d323498 100644 --- a/core/embed/trezorhal/stm32f4/platform.h +++ b/core/embed/trezorhal/stm32f4/platform.h @@ -23,7 +23,7 @@ #include STM32_HAL_H #include -#define FLASH_BYTE_ACCESS 1 +#define FLASH_BIT_ACCESS 1 typedef enum { CLOCK_180_MHZ = 0, diff --git a/core/embed/trezorhal/unix/platform.h b/core/embed/trezorhal/unix/platform.h index 72e4ac225d..a8d548bd90 100644 --- a/core/embed/trezorhal/unix/platform.h +++ b/core/embed/trezorhal/unix/platform.h @@ -1,4 +1,4 @@ -#define FLASH_BYTE_ACCESS 1 +#define FLASH_BIT_ACCESS 1 void emulator_poll_events(void); diff --git a/legacy/flash.h b/legacy/flash.h index 5559b6d89b..dbd61b501d 100644 --- a/legacy/flash.h +++ b/legacy/flash.h @@ -24,7 +24,7 @@ #include #include "secbool.h" -#define FLASH_BYTE_ACCESS 1 +#define FLASH_BIT_ACCESS 1 #define FLASH_SECTOR_COUNT 24 #include "flash_common.h" diff --git a/storage/flash_common.h b/storage/flash_common.h index fb1daf16c8..0e7890b8f3 100644 --- a/storage/flash_common.h +++ b/storage/flash_common.h @@ -51,7 +51,7 @@ secbool __wur flash_area_erase(const flash_area_t *area, secbool __wur flash_area_erase_bulk(const flash_area_t *area, int count, void (*progress)(int pos, int len)); -#if defined FLASH_BYTE_ACCESS +#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, diff --git a/storage/norcow.c b/storage/norcow.c index 145f4a5e6f..0657f5207e 100644 --- a/storage/norcow.c +++ b/storage/norcow.c @@ -33,6 +33,10 @@ #define NORCOW_MAGIC_LEN sizeof(uint32_t) #define NORCOW_VERSION_LEN sizeof(uint32_t) +// The offset from the beginning of the sector where stored items start. +#define NORCOW_STORAGE_START \ + FLASH_ALIGN(NORCOW_HEADER_LEN + NORCOW_MAGIC_LEN + NORCOW_VERSION_LEN) + // The key value which is used to indicate that the entry is not set. #define NORCOW_KEY_FREE (0xFFFF) @@ -42,27 +46,6 @@ #define NORCOW_KEY_LEN 2 #define NORCOW_LEN_LEN 2 -#ifdef FLASH_BYTE_ACCESS -#define COUNTER_TAIL_WORDS 2 -#define NORCOW_MAX_PREFIX_LEN (FLASH_BLOCK_SIZE) -// The offset from the beginning of the sector where stored items start. -#define NORCOW_STORAGE_START \ - (NORCOW_HEADER_LEN + NORCOW_MAGIC_LEN + NORCOW_VERSION_LEN) - -#else -#define COUNTER_TAIL_WORDS 0 -// Small items are encoded more efficiently. -#define NORCOW_SMALL_ITEM_SIZE \ - (FLASH_BLOCK_SIZE - NORCOW_LEN_LEN - NORCOW_KEY_LEN) -// Larger items store first part of data with the same QW as key -#define NORCOW_VALID_FLAG 0xFE -#define NORCOW_VALID_FLAG_LEN 1 -#define NORCOW_DATA_OPT_SIZE (FLASH_BLOCK_SIZE - NORCOW_VALID_FLAG_LEN) -#define NORCOW_MAX_PREFIX_LEN (FLASH_BLOCK_SIZE + NORCOW_VALID_FLAG_LEN) -// The offset from the beginning of the sector where stored items start. -#define NORCOW_STORAGE_START (NORCOW_HEADER_LEN + FLASH_BLOCK_SIZE) -#endif - // The index of the active reading sector and writing sector. These should be // equal except when storage version upgrade or compaction is in progress. static uint8_t norcow_active_sector = 0; @@ -75,11 +58,15 @@ static uint32_t norcow_active_version = 0; static uint32_t norcow_free_offset = 0; static uint16_t norcow_write_buffer_flashed = 0; -#ifndef FLASH_BYTE_ACCESS -static flash_block_t norcow_write_buffer = {0}; -static uint16_t norcow_write_buffer_filled = 0; -static uint16_t norcow_write_buffer_filled_data = 0; -static int32_t norcow_write_buffer_key = -1; + +static const void *norcow_ptr(uint8_t sector, uint32_t offset, uint32_t size); +static secbool find_item(uint8_t sector, uint16_t key, const void **val, + uint16_t *len); + +#ifdef FLASH_BIT_ACCESS +#include "norcow_bitwise.h" +#else +#include "norcow_blockwise.h" #endif /* @@ -91,115 +78,6 @@ static const void *norcow_ptr(uint8_t sector, uint32_t offset, uint32_t size) { return flash_area_get_address(&STORAGE_AREAS[sector], offset, size); } -/* - * Writes data to given sector, starting from offset - */ -#ifdef FLASH_BYTE_ACCESS -static secbool write_item(uint8_t sector, uint32_t offset, uint16_t key, - const uint8_t *data, uint16_t len, uint32_t *pos) { - if (sector >= NORCOW_SECTOR_COUNT) { - return secfalse; - } - - if (offset + NORCOW_MAX_PREFIX_LEN + len > NORCOW_SECTOR_SIZE) { - return secfalse; - } - - uint32_t prefix = ((uint32_t)len << 16) | key; - - ensure(flash_unlock_write(), NULL); - - // write prefix - ensure(flash_area_write_word(&STORAGE_AREAS[sector], offset, prefix), NULL); - offset += NORCOW_MAX_PREFIX_LEN; - - if (data != NULL) { - // write data - for (uint16_t i = 0; i < len; i++, offset++) { - ensure(flash_area_write_byte(&STORAGE_AREAS[sector], offset, data[i]), - NULL); - } - } else { - offset += len; - } - - // pad with zeroes - for (; offset % FLASH_BLOCK_SIZE; offset++) { - ensure(flash_area_write_byte(&STORAGE_AREAS[sector], offset, 0x00), NULL); - } - - ensure(flash_lock_write(), NULL); - *pos = offset; - return sectrue; -} -#else -static secbool write_item(uint8_t sector, uint32_t offset, uint16_t key, - const uint8_t *data, uint16_t len, uint32_t *pos) { - if (sector >= NORCOW_SECTOR_COUNT) { - return secfalse; - } - - flash_block_t block = {len | ((uint32_t)key << 16)}; - if (len <= NORCOW_SMALL_ITEM_SIZE) { - // the whole item fits into one block, let's not waste space - if (offset + FLASH_BLOCK_SIZE > NORCOW_SECTOR_SIZE) { - return secfalse; - } - - if (len > 0) { - memcpy(&block[1], data, len); // write data - } - - ensure(flash_unlock_write(), NULL); - ensure(flash_area_write_block(&STORAGE_AREAS[sector], offset, block), NULL); - ensure(flash_lock_write(), NULL); - *pos = offset + FLASH_BLOCK_SIZE; - } else { - uint16_t len_adjusted = FLASH_ALIGN(len); - - if (offset + NORCOW_MAX_PREFIX_LEN + len_adjusted > NORCOW_SECTOR_SIZE) { - return secfalse; - } - - ensure(flash_unlock_write(), NULL); - - // write len - ensure(flash_area_write_block(&STORAGE_AREAS[sector], offset, block), NULL); - offset += FLASH_BLOCK_SIZE; - - *pos = FLASH_ALIGN(offset + NORCOW_VALID_FLAG_LEN + len); - if (data != NULL) { - // write key and first data part - uint16_t len_to_write = - len > NORCOW_DATA_OPT_SIZE ? NORCOW_DATA_OPT_SIZE : len; - memset(block, 0, sizeof(block)); - block[0] = NORCOW_VALID_FLAG; - memcpy(&(((uint8_t *)block)[NORCOW_VALID_FLAG_LEN]), data, len_to_write); - ensure(flash_area_write_block(&STORAGE_AREAS[sector], offset, block), - NULL); - offset += FLASH_BLOCK_SIZE; - data += len_to_write; - len -= len_to_write; - - while (len > 0) { - len_to_write = len > FLASH_BLOCK_SIZE ? FLASH_BLOCK_SIZE : len; - memset(block, 0, sizeof(block)); - memcpy(block, data, len_to_write); - ensure(flash_area_write_block(&STORAGE_AREAS[sector], offset, block), - NULL); - offset += FLASH_BLOCK_SIZE; - data += len_to_write; - len -= len_to_write; - } - memzero(block, sizeof(block)); - } - - ensure(flash_lock_write(), NULL); - } - return sectrue; -} -#endif - /* * Erases sector (and sets a magic) */ @@ -244,70 +122,6 @@ static void erase_sector(uint8_t sector, secbool set_magic) { } } -/* - * Reads one item starting from offset - */ -static secbool read_item(uint8_t sector, uint32_t offset, uint16_t *key, - const void **val, uint16_t *len, uint32_t *pos) { - *pos = offset; - -#ifdef FLASH_BYTE_ACCESS - const void *k = norcow_ptr(sector, *pos, NORCOW_KEY_LEN); - if (k == NULL) return secfalse; - *pos += NORCOW_KEY_LEN; - memcpy(key, k, sizeof(uint16_t)); - if (*key == NORCOW_KEY_FREE) { - return secfalse; - } - - const void *l = norcow_ptr(sector, *pos, NORCOW_LEN_LEN); - if (l == NULL) return secfalse; - *pos += NORCOW_LEN_LEN; - memcpy(len, l, sizeof(uint16_t)); -#else - const void *l = norcow_ptr(sector, *pos, NORCOW_LEN_LEN); - if (l == NULL) return secfalse; - memcpy(len, l, sizeof(uint16_t)); - - if (*len <= NORCOW_SMALL_ITEM_SIZE) { - *pos += NORCOW_LEN_LEN; - const void *k = norcow_ptr(sector, *pos, NORCOW_KEY_LEN); - if (k == NULL) return secfalse; - memcpy(key, k, sizeof(uint16_t)); - if (*key == NORCOW_KEY_FREE) { - return secfalse; - } - *pos += NORCOW_KEY_LEN; - } else { - *pos += NORCOW_LEN_LEN; - const void *k = norcow_ptr(sector, *pos, NORCOW_KEY_LEN); - if (k == NULL) return secfalse; - - *pos += (NORCOW_KEY_LEN + NORCOW_SMALL_ITEM_SIZE); - - const void *flg = norcow_ptr(sector, *pos, NORCOW_VALID_FLAG_LEN); - if (flg == NULL) return secfalse; - - *pos += NORCOW_VALID_FLAG_LEN; - if (*((const uint8_t *)flg) == 0) { - // Deleted item. - *key = NORCOW_KEY_DELETED; - } else { - memcpy(key, k, sizeof(uint16_t)); - if (*key == NORCOW_KEY_FREE) { - return secfalse; - } - } - } - -#endif - - *val = norcow_ptr(sector, *pos, *len); - if (*val == NULL) return secfalse; - *pos = FLASH_ALIGN(*pos + *len); - return sectrue; -} - /* * Finds the offset from the beginning of the sector where stored items start. */ @@ -534,35 +348,6 @@ secbool norcow_get_next(uint32_t *offset, uint16_t *key, const void **val, return secfalse; } -void norcow_delete_head(const flash_area_t *area, uint32_t len, - uint32_t *val_offset) { - ensure(flash_unlock_write(), NULL); -#ifdef FLASH_BYTE_ACCESS - // Update the prefix to indicate that the item has been deleted. - uint32_t prefix = (uint32_t)len << 16; - ensure( - flash_area_write_word(area, *val_offset - NORCOW_MAX_PREFIX_LEN, prefix), - NULL); -#else - // Move to the beginning of the block. - if (len <= NORCOW_SMALL_ITEM_SIZE) { - // Will delete the entire small item, setting the length to 0 - *val_offset -= NORCOW_LEN_LEN + NORCOW_KEY_LEN; - } else { - // Will update the flag to indicate that the old item has been deleted. - // Deletes a portion of old item data too. - *val_offset -= NORCOW_VALID_FLAG_LEN; - } - - flash_block_t block = {0}; - ensure(flash_area_write_block(area, *val_offset, block), NULL); - - // Move to the next block. - *val_offset += FLASH_BLOCK_SIZE; -#endif - ensure(flash_lock_write(), NULL); -} - /* * Sets the given key, returns status of the operation. If NULL is passed * as val, then norcow_set allocates a new key of size len. The value should @@ -591,23 +376,10 @@ secbool norcow_set_ex(uint16_t key, const void *val, uint16_t len, (const uint8_t *)ptr - (const uint8_t *)norcow_ptr(norcow_write_sector, 0, NORCOW_SECTOR_SIZE); // Try to update the entry if it already exists. -#ifdef FLASH_BYTE_ACCESS - if (val != NULL && len_old == len) { - secbool updated = sectrue; - ensure(flash_unlock_write(), NULL); - for (uint16_t i = 0; i < len; i++) { - if (sectrue != flash_area_write_byte(area, val_offset + i, - ((const uint8_t *)val)[i])) { - updated = secfalse; - break; - } - } - ensure(flash_lock_write(), NULL); - if (sectrue == updated) { - return sectrue; - } + if (sectrue == + flash_area_write_bytes(area, val_offset, len_old, val, len)) { + return sectrue; } -#endif } // Delete the old item. @@ -678,33 +450,6 @@ secbool norcow_delete(uint16_t key) { return sectrue; } -#ifdef FLASH_BYTE_ACCESS -/* - * Update a word in flash at the given pointer. The pointer must point - * into the NORCOW area. - */ -secbool norcow_update_word(uint16_t key, uint16_t offset, uint32_t value) { - const void *ptr = NULL; - uint16_t len = 0; - if (sectrue != find_item(norcow_write_sector, key, &ptr, &len)) { - return secfalse; - } - if (!FLASH_IS_ALIGNED(offset) || offset >= len) { - return secfalse; - } - uint32_t sector_offset = - (const uint8_t *)ptr - - (const uint8_t *)norcow_ptr(norcow_write_sector, 0, NORCOW_SECTOR_SIZE) + - offset; - ensure(flash_unlock_write(), NULL); - ensure(flash_area_write_word(&STORAGE_AREAS[norcow_write_sector], - sector_offset, value), - NULL); - ensure(flash_lock_write(), NULL); - return sectrue; -} -#endif - secbool norcow_set_counter(uint16_t key, uint32_t count) { // The count is stored as a 32-bit integer followed by a tail of "1" bits, // which is used as a tally. @@ -714,134 +459,6 @@ secbool norcow_set_counter(uint16_t key, uint32_t count) { return norcow_set(key, value, sizeof(value)); } -secbool norcow_next_counter(uint16_t key, uint32_t *count) { - uint16_t len = 0; - const uint32_t *val_stored = NULL; - if (sectrue != norcow_get(key, (const void **)&val_stored, &len)) { - *count = 0; - return norcow_set_counter(key, 0); - } - - if (len < sizeof(uint32_t) || len % sizeof(uint32_t) != 0) { - return secfalse; - } - uint16_t len_words = len / sizeof(uint32_t); - - uint16_t i = 1; - while (i < len_words && val_stored[i] == 0) { - ++i; - } - - *count = val_stored[0] + 1 + 32 * (i - 1); - if (*count < val_stored[0]) { - // Value overflow. - return secfalse; - } - - if (i < len_words) { - *count += hamming_weight(~val_stored[i]); - if (*count < val_stored[0]) { - // Value overflow. - return secfalse; - } -#ifdef FLASH_BYTE_ACCESS - return norcow_update_word(key, sizeof(uint32_t) * i, val_stored[i] >> 1); -#else - return norcow_set_counter(key, *count); -#endif - } else { - return norcow_set_counter(key, *count); - } -} - -/* - * Update the value of the given key starting at the given offset. - */ -secbool norcow_update_bytes(const uint16_t key, const uint8_t *data, - const uint16_t len) { - const void *ptr = NULL; - uint16_t allocated_len = 0; - if (sectrue != find_item(norcow_write_sector, key, &ptr, &allocated_len)) { - return secfalse; - } -#ifndef FLASH_BYTE_ACCESS - if (allocated_len <= NORCOW_SMALL_ITEM_SIZE) { - // small items are not updated in place - return secfalse; - } -#endif - if (norcow_write_buffer_flashed + len > allocated_len) { - return secfalse; - } - uint32_t sector_offset = - (const uint8_t *)ptr - - (const uint8_t *)norcow_ptr(norcow_write_sector, 0, NORCOW_SECTOR_SIZE); - - const flash_area_t *area = &STORAGE_AREAS[norcow_write_sector]; - ensure(flash_unlock_write(), NULL); -#ifdef FLASH_BYTE_ACCESS - sector_offset += norcow_write_buffer_flashed; - for (uint16_t i = 0; i < len; i++, sector_offset++) { - ensure(flash_area_write_byte(area, sector_offset, data[i]), NULL); - } - norcow_write_buffer_flashed += len; - if (norcow_write_buffer_flashed >= allocated_len) { - norcow_write_buffer_flashed = 0; - } -#else - if (norcow_write_buffer_key != key && norcow_write_buffer_key != -1) { - // some other update bytes is in process, abort - return secfalse; - } - - if (norcow_write_buffer_key == -1) { - memset(norcow_write_buffer, 0, sizeof(norcow_write_buffer)); - norcow_write_buffer_key = key; - norcow_write_buffer[0] = NORCOW_VALID_FLAG; - norcow_write_buffer_filled = NORCOW_VALID_FLAG_LEN; - norcow_write_buffer_filled_data = 0; - norcow_write_buffer_flashed = 0; - } - - uint16_t tmp_len = len; - uint16_t flash_offset = - sector_offset - NORCOW_VALID_FLAG_LEN + norcow_write_buffer_flashed; - while (tmp_len > 0) { - uint16_t buffer_space = FLASH_BLOCK_SIZE - norcow_write_buffer_filled; - uint16_t data_to_copy = (tmp_len > buffer_space ? buffer_space : tmp_len); - memcpy(&((uint8_t *)norcow_write_buffer)[norcow_write_buffer_filled], data, - data_to_copy); - data += data_to_copy; - norcow_write_buffer_filled += data_to_copy; - norcow_write_buffer_filled_data += data_to_copy; - tmp_len -= data_to_copy; - - if (norcow_write_buffer_filled == FLASH_BLOCK_SIZE || - (norcow_write_buffer_filled_data + norcow_write_buffer_flashed) == - allocated_len + NORCOW_VALID_FLAG_LEN) { - ensure(flash_area_write_block(area, flash_offset, norcow_write_buffer), - NULL); - ensure(flash_area_write_block(area, flash_offset, norcow_write_buffer), - NULL); - flash_offset += FLASH_BLOCK_SIZE; - norcow_write_buffer_filled = 0; - norcow_write_buffer_flashed += FLASH_BLOCK_SIZE; - memset(norcow_write_buffer, 0, sizeof(norcow_write_buffer)); - - if ((norcow_write_buffer_flashed) >= - allocated_len + NORCOW_VALID_FLAG_LEN) { - norcow_write_buffer_key = -1; - norcow_write_buffer_flashed = 0; - } - norcow_write_buffer_filled_data = 0; - } - } - -#endif - ensure(flash_lock_write(), NULL); - return sectrue; -} - /* * Complete storage version upgrade */ diff --git a/storage/norcow_bitwise.h b/storage/norcow_bitwise.h new file mode 100644 index 0000000000..9aa3850b6a --- /dev/null +++ b/storage/norcow_bitwise.h @@ -0,0 +1,214 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "flash_common.h" + +#define COUNTER_TAIL_WORDS 2 +#define NORCOW_MAX_PREFIX_LEN (NORCOW_KEY_LEN + NORCOW_LEN_LEN) + +static secbool write_item(uint8_t sector, uint32_t offset, uint16_t key, + const uint8_t *data, uint16_t len, uint32_t *pos) { + if (sector >= NORCOW_SECTOR_COUNT) { + return secfalse; + } + + if (offset + NORCOW_MAX_PREFIX_LEN + len > NORCOW_SECTOR_SIZE) { + return secfalse; + } + + uint32_t prefix = ((uint32_t)len << 16) | key; + + ensure(flash_unlock_write(), NULL); + + // write prefix + ensure(flash_area_write_word(&STORAGE_AREAS[sector], offset, prefix), NULL); + offset += sizeof(prefix); + + if (data != NULL) { + // write data + for (uint16_t i = 0; i < len; i++, offset++) { + ensure(flash_area_write_byte(&STORAGE_AREAS[sector], offset, data[i]), + NULL); + } + } else { + offset += len; + } + + // pad with zeroes + for (; offset % FLASH_BLOCK_SIZE; offset++) { + ensure(flash_area_write_byte(&STORAGE_AREAS[sector], offset, 0x00), NULL); + } + + ensure(flash_lock_write(), NULL); + *pos = offset; + return sectrue; +} + +/* + * Reads one item starting from offset + */ +static secbool read_item(uint8_t sector, uint32_t offset, uint16_t *key, + const void **val, uint16_t *len, uint32_t *pos) { + *pos = offset; + const void *k = norcow_ptr(sector, *pos, NORCOW_KEY_LEN); + if (k == NULL) return secfalse; + *pos += NORCOW_KEY_LEN; + memcpy(key, k, sizeof(uint16_t)); + if (*key == NORCOW_KEY_FREE) { + return secfalse; + } + + const void *l = norcow_ptr(sector, *pos, NORCOW_LEN_LEN); + if (l == NULL) return secfalse; + *pos += NORCOW_LEN_LEN; + memcpy(len, l, sizeof(uint16_t)); + + *val = norcow_ptr(sector, *pos, *len); + if (*val == NULL) return secfalse; + *pos = FLASH_ALIGN(*pos + *len); + return sectrue; +} + +void norcow_delete_head(const flash_area_t *area, uint16_t len, + uint32_t *val_offset) { + ensure(flash_unlock_write(), NULL); + // Update the prefix to indicate that the item has been deleted. + uint32_t prefix = (uint32_t)len << 16; + ensure(flash_area_write_word(area, *val_offset - sizeof(prefix), prefix), + NULL); + ensure(flash_lock_write(), NULL); +} + +/* + * Tries to update a part of flash memory with a given value. + */ +static secbool flash_area_write_bytes(const flash_area_t *area, uint32_t offset, + uint16_t dest_len, const void *val, + uint16_t len) { + if (val == NULL || dest_len != len) { + return secfalse; + } + + secbool updated = sectrue; + ensure(flash_unlock_write(), NULL); + for (uint16_t i = 0; i < len; i++) { + if (sectrue != + flash_area_write_byte(area, offset + i, ((const uint8_t *)val)[i])) { + updated = secfalse; + break; + } + } + ensure(flash_lock_write(), NULL); + return updated; +} + +/* + * Update a word in flash at the given pointer. The pointer must point + * into the NORCOW area. + */ +secbool norcow_update_word(uint16_t key, uint16_t offset, uint32_t value) { + const void *ptr = NULL; + uint16_t len = 0; + if (sectrue != find_item(norcow_write_sector, key, &ptr, &len)) { + return secfalse; + } + if (!FLASH_IS_ALIGNED(offset) || offset >= len) { + return secfalse; + } + uint32_t sector_offset = + (const uint8_t *)ptr - + (const uint8_t *)norcow_ptr(norcow_write_sector, 0, NORCOW_SECTOR_SIZE) + + offset; + ensure(flash_unlock_write(), NULL); + ensure(flash_area_write_word(&STORAGE_AREAS[norcow_write_sector], + sector_offset, value), + NULL); + ensure(flash_lock_write(), NULL); + return sectrue; +} + +secbool norcow_next_counter(uint16_t key, uint32_t *count) { + uint16_t len = 0; + const uint32_t *val_stored = NULL; + if (sectrue != norcow_get(key, (const void **)&val_stored, &len)) { + *count = 0; + return norcow_set_counter(key, 0); + } + + if (len < sizeof(uint32_t) || len % sizeof(uint32_t) != 0) { + return secfalse; + } + uint16_t len_words = len / sizeof(uint32_t); + + uint16_t i = 1; + while (i < len_words && val_stored[i] == 0) { + ++i; + } + + *count = val_stored[0] + 1 + 32 * (i - 1); + if (*count < val_stored[0]) { + // Value overflow. + return secfalse; + } + + if (i < len_words) { + *count += hamming_weight(~val_stored[i]); + if (*count < val_stored[0]) { + // Value overflow. + return secfalse; + } + return norcow_update_word(key, sizeof(uint32_t) * i, val_stored[i] >> 1); + } else { + return norcow_set_counter(key, *count); + } +} + +/* + * Update the value of the given key starting at the given offset. + */ +secbool norcow_update_bytes(const uint16_t key, const uint8_t *data, + const uint16_t len) { + const void *ptr = NULL; + uint16_t allocated_len = 0; + if (sectrue != find_item(norcow_write_sector, key, &ptr, &allocated_len)) { + return secfalse; + } + + if (norcow_write_buffer_flashed + len > allocated_len) { + return secfalse; + } + uint32_t sector_offset = + (const uint8_t *)ptr - + (const uint8_t *)norcow_ptr(norcow_write_sector, 0, NORCOW_SECTOR_SIZE); + + const flash_area_t *area = &STORAGE_AREAS[norcow_write_sector]; + ensure(flash_unlock_write(), NULL); + + sector_offset += norcow_write_buffer_flashed; + for (uint16_t i = 0; i < len; i++, sector_offset++) { + ensure(flash_area_write_byte(area, sector_offset, data[i]), NULL); + } + norcow_write_buffer_flashed += len; + if (norcow_write_buffer_flashed >= allocated_len) { + norcow_write_buffer_flashed = 0; + } + + ensure(flash_lock_write(), NULL); + return sectrue; +} diff --git a/storage/norcow_blockwise.h b/storage/norcow_blockwise.h new file mode 100644 index 0000000000..cc9a74f2c7 --- /dev/null +++ b/storage/norcow_blockwise.h @@ -0,0 +1,283 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "flash_common.h" + +#define COUNTER_TAIL_WORDS 0 +// Small items are encoded more efficiently. +#define NORCOW_SMALL_ITEM_SIZE \ + (FLASH_BLOCK_SIZE - NORCOW_LEN_LEN - NORCOW_KEY_LEN) +#define NORCOW_VALID_FLAG 0xFE +#define NORCOW_VALID_FLAG_LEN 1 +#define NORCOW_DATA_OPT_SIZE (FLASH_BLOCK_SIZE - NORCOW_VALID_FLAG_LEN) +#define NORCOW_MAX_PREFIX_LEN (FLASH_BLOCK_SIZE + NORCOW_VALID_FLAG_LEN) + +static flash_block_t norcow_write_buffer = {0}; +static uint16_t norcow_write_buffer_filled = 0; +static uint16_t norcow_write_buffer_filled_data = 0; +static int32_t norcow_write_buffer_key = -1; + +/* + * Writes data to given sector, starting from offset + */ +static secbool write_item(uint8_t sector, uint32_t offset, uint16_t key, + const uint8_t *data, uint16_t len, uint32_t *pos) { + if (sector >= NORCOW_SECTOR_COUNT) { + return secfalse; + } + + flash_block_t block = {len | ((uint32_t)key << 16)}; + if (len <= NORCOW_SMALL_ITEM_SIZE) { + // the whole item fits into one block, let's not waste space + if (offset + FLASH_BLOCK_SIZE > NORCOW_SECTOR_SIZE) { + return secfalse; + } + + if (len > 0) { + memcpy(&block[1], data, len); // write data + } + + ensure(flash_unlock_write(), NULL); + ensure(flash_area_write_block(&STORAGE_AREAS[sector], offset, block), NULL); + ensure(flash_lock_write(), NULL); + *pos = offset + FLASH_BLOCK_SIZE; + } else { + uint16_t len_adjusted = FLASH_ALIGN(len); + + if (offset + NORCOW_MAX_PREFIX_LEN + len_adjusted > NORCOW_SECTOR_SIZE) { + return secfalse; + } + + ensure(flash_unlock_write(), NULL); + + // write len + ensure(flash_area_write_block(&STORAGE_AREAS[sector], offset, block), NULL); + offset += FLASH_BLOCK_SIZE; + + *pos = FLASH_ALIGN(offset + NORCOW_VALID_FLAG_LEN + len); + if (data != NULL) { + // write key and first data part + uint16_t len_to_write = + len > NORCOW_DATA_OPT_SIZE ? NORCOW_DATA_OPT_SIZE : len; + memset(block, 0, sizeof(block)); + block[0] = NORCOW_VALID_FLAG; + memcpy(&(((uint8_t *)block)[NORCOW_VALID_FLAG_LEN]), data, len_to_write); + ensure(flash_area_write_block(&STORAGE_AREAS[sector], offset, block), + NULL); + offset += FLASH_BLOCK_SIZE; + data += len_to_write; + len -= len_to_write; + + while (len > 0) { + len_to_write = len > FLASH_BLOCK_SIZE ? FLASH_BLOCK_SIZE : len; + memset(block, 0, sizeof(block)); + memcpy(block, data, len_to_write); + ensure(flash_area_write_block(&STORAGE_AREAS[sector], offset, block), + NULL); + offset += FLASH_BLOCK_SIZE; + data += len_to_write; + len -= len_to_write; + } + memzero(block, sizeof(block)); + } + + ensure(flash_lock_write(), NULL); + } + return sectrue; +} + +/* + * Reads one item starting from offset + */ +static secbool read_item(uint8_t sector, uint32_t offset, uint16_t *key, + const void **val, uint16_t *len, uint32_t *pos) { + *pos = offset; + + const void *l = norcow_ptr(sector, *pos, NORCOW_LEN_LEN); + if (l == NULL) return secfalse; + memcpy(len, l, sizeof(uint16_t)); + + *pos += NORCOW_LEN_LEN; + const void *k = norcow_ptr(sector, *pos, NORCOW_KEY_LEN); + if (k == NULL) { + return secfalse; + } + + if (*len <= NORCOW_SMALL_ITEM_SIZE) { + memcpy(key, k, sizeof(uint16_t)); + if (*key == NORCOW_KEY_FREE) { + return secfalse; + } + *pos += NORCOW_KEY_LEN; + } else { + *pos += (NORCOW_KEY_LEN + NORCOW_SMALL_ITEM_SIZE); + + const void *flg = norcow_ptr(sector, *pos, NORCOW_VALID_FLAG_LEN); + if (flg == NULL) { + return secfalse; + } + + *pos += NORCOW_VALID_FLAG_LEN; + if (*((const uint8_t *)flg) == 0) { + // Deleted item. + *key = NORCOW_KEY_DELETED; + } else { + memcpy(key, k, sizeof(uint16_t)); + if (*key == NORCOW_KEY_FREE) { + return secfalse; + } + } + } + + *val = norcow_ptr(sector, *pos, *len); + if (*val == NULL) return secfalse; + *pos = FLASH_ALIGN(*pos + *len); + return sectrue; +} + +void norcow_delete_head(const flash_area_t *area, uint32_t len, + uint32_t *val_offset) { + ensure(flash_unlock_write(), NULL); + // Move to the beginning of the block. + if (len <= NORCOW_SMALL_ITEM_SIZE) { + // Will delete the entire small item, setting the length to 0 + *val_offset -= NORCOW_LEN_LEN + NORCOW_KEY_LEN; + } else { + // Will update the flag to indicate that the old item has been deleted. + // Deletes a portion of old item data too. + *val_offset -= NORCOW_VALID_FLAG_LEN; + } + + flash_block_t block = {0}; + ensure(flash_area_write_block(area, *val_offset, block), NULL); + + // Move to the next block. + *val_offset += FLASH_BLOCK_SIZE; + ensure(flash_lock_write(), NULL); +} + +static secbool flash_area_write_bytes(const flash_area_t *area, uint32_t offset, + uint16_t dest_len, const void *val, + uint16_t len) { + (void)area; + (void)offset; + (void)dest_len; + (void)val; + (void)len; + return secfalse; +} + +secbool norcow_next_counter(uint16_t key, uint32_t *count) { + uint16_t len = 0; + const uint32_t *val_stored = NULL; + if (sectrue != norcow_get(key, (const void **)&val_stored, &len)) { + *count = 0; + return norcow_set_counter(key, 0); + } + + if (len != sizeof(uint32_t)) { + return secfalse; + } + + *count = *val_stored + 1; + if (*count < *val_stored) { + // Value overflow. + return secfalse; + } + + return norcow_set_counter(key, *count); +} + +/* + * Update the value of the given key starting at the given offset. + */ +secbool norcow_update_bytes(const uint16_t key, const uint8_t *data, + const uint16_t len) { + const void *ptr = NULL; + uint16_t allocated_len = 0; + if (sectrue != find_item(norcow_write_sector, key, &ptr, &allocated_len)) { + return secfalse; + } + + if (allocated_len <= NORCOW_SMALL_ITEM_SIZE) { + // small items are not updated in place + return secfalse; + } + + if (norcow_write_buffer_flashed + len > allocated_len) { + return secfalse; + } + uint32_t sector_offset = + (const uint8_t *)ptr - + (const uint8_t *)norcow_ptr(norcow_write_sector, 0, NORCOW_SECTOR_SIZE); + + const flash_area_t *area = &STORAGE_AREAS[norcow_write_sector]; + ensure(flash_unlock_write(), NULL); + + if (norcow_write_buffer_key != key && norcow_write_buffer_key != -1) { + // some other update bytes is in process, abort + return secfalse; + } + + if (norcow_write_buffer_key == -1) { + memset(norcow_write_buffer, 0, sizeof(norcow_write_buffer)); + norcow_write_buffer_key = key; + norcow_write_buffer[0] = NORCOW_VALID_FLAG; + norcow_write_buffer_filled = NORCOW_VALID_FLAG_LEN; + norcow_write_buffer_filled_data = 0; + norcow_write_buffer_flashed = 0; + } + + uint16_t tmp_len = len; + uint16_t flash_offset = + sector_offset - NORCOW_VALID_FLAG_LEN + norcow_write_buffer_flashed; + while (tmp_len > 0) { + uint16_t buffer_space = FLASH_BLOCK_SIZE - norcow_write_buffer_filled; + uint16_t data_to_copy = (tmp_len > buffer_space ? buffer_space : tmp_len); + memcpy(&((uint8_t *)norcow_write_buffer)[norcow_write_buffer_filled], data, + data_to_copy); + data += data_to_copy; + norcow_write_buffer_filled += data_to_copy; + norcow_write_buffer_filled_data += data_to_copy; + tmp_len -= data_to_copy; + + if (norcow_write_buffer_filled == FLASH_BLOCK_SIZE || + (norcow_write_buffer_filled_data + norcow_write_buffer_flashed) == + allocated_len + NORCOW_VALID_FLAG_LEN) { + ensure(flash_area_write_block(area, flash_offset, norcow_write_buffer), + NULL); + ensure(flash_area_write_block(area, flash_offset, norcow_write_buffer), + NULL); + flash_offset += FLASH_BLOCK_SIZE; + norcow_write_buffer_filled = 0; + norcow_write_buffer_flashed += FLASH_BLOCK_SIZE; + memset(norcow_write_buffer, 0, sizeof(norcow_write_buffer)); + + if ((norcow_write_buffer_flashed) >= + allocated_len + NORCOW_VALID_FLAG_LEN) { + norcow_write_buffer_key = -1; + norcow_write_buffer_flashed = 0; + } + norcow_write_buffer_filled_data = 0; + } + } + + ensure(flash_lock_write(), NULL); + return sectrue; +} diff --git a/storage/tests/c/flash.h b/storage/tests/c/flash.h index 132f85bcc8..9b27f7c5d9 100644 --- a/storage/tests/c/flash.h +++ b/storage/tests/c/flash.h @@ -25,7 +25,7 @@ #include "secbool.h" #ifndef FLASH_QUADWORD -#define FLASH_BYTE_ACCESS 1 +#define FLASH_BIT_ACCESS 1 #endif #include "flash_common.h"