From 32cf4f36d295ae98f6f0609331a6804155124205 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Thu, 30 Mar 2017 20:50:50 +0200 Subject: [PATCH] modtrezorconfig: norcow works for stm --- micropython/extmod/modtrezorconfig/Makefile | 15 + micropython/extmod/modtrezorconfig/norcow.c | 344 +++++++++++++++++- micropython/extmod/modtrezorconfig/norcow.h | 35 +- .../extmod/modtrezorconfig/norcow_config.h | 6 + .../extmod/modtrezorconfig/norcow_test.c | 45 +++ 5 files changed, 443 insertions(+), 2 deletions(-) create mode 100644 micropython/extmod/modtrezorconfig/Makefile mode change 120000 => 100644 micropython/extmod/modtrezorconfig/norcow.c mode change 120000 => 100644 micropython/extmod/modtrezorconfig/norcow.h create mode 100644 micropython/extmod/modtrezorconfig/norcow_test.c diff --git a/micropython/extmod/modtrezorconfig/Makefile b/micropython/extmod/modtrezorconfig/Makefile new file mode 100644 index 0000000000..8da1a6d3ae --- /dev/null +++ b/micropython/extmod/modtrezorconfig/Makefile @@ -0,0 +1,15 @@ +NAME=norcow_test +CC=gcc +CFLAGS=-Wall -Wextra -Werror -DUNIX +LDFLAGS= +LIBS= +OBJ=$(NAME).o norcow.o + +$(NAME): $(OBJ) + $(CC) $(LDFLAGS) $(LIBS) $(OBJ) -o $(NAME) + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +clean: + rm -f $(NAME) $(OBJ) diff --git a/micropython/extmod/modtrezorconfig/norcow.c b/micropython/extmod/modtrezorconfig/norcow.c deleted file mode 120000 index 0c15ca2f96..0000000000 --- a/micropython/extmod/modtrezorconfig/norcow.c +++ /dev/null @@ -1 +0,0 @@ -../../../vendor/norcow/norcow.c \ No newline at end of file diff --git a/micropython/extmod/modtrezorconfig/norcow.c b/micropython/extmod/modtrezorconfig/norcow.c new file mode 100644 index 0000000000..8b62d8fda1 --- /dev/null +++ b/micropython/extmod/modtrezorconfig/norcow.c @@ -0,0 +1,343 @@ +#include "norcow.h" +#include "norcow_config.h" + +#include + +#ifdef NORCOW_UNIX +#ifndef NORCOW_FILE +#error Undefined NORCOW_FILE +#endif +#include +static uint8_t norcow_buffer[NORCOW_SECTOR_COUNT * NORCOW_SECTOR_SIZE]; +#endif + +#ifdef NORCOW_STM +#ifndef NORCOW_START_SECTOR +#error Undefined NORCOW_START_SECTOR +#endif +#ifndef NORCOW_START_ADDRESS +#error Undefined NORCOW_START_ADDRESS +#endif +#include STM32_HAL_H +#endif + +static uint8_t norcow_active_sector = 0; +static uint32_t norcow_active_offset = 0; + +/* + * Synchronizes in-memory storage with file on disk (UNIX only) + */ +#ifdef NORCOW_UNIX +static void norcow_sync(void) +{ + FILE *f = fopen(NORCOW_FILE, "wb"); + if (f) { + fwrite(norcow_buffer, sizeof(norcow_buffer), 1, f); + fclose(f); + } +} +#endif + +/* + * Erases sector + */ +static bool norcow_erase(uint8_t sector) +{ + if (sector >= NORCOW_SECTOR_COUNT) { + return false; + } +#ifdef NORCOW_UNIX + memset(norcow_buffer + sector * NORCOW_SECTOR_SIZE, 0xFF, NORCOW_SECTOR_SIZE); + norcow_sync(); + return true; +#endif +#ifdef NORCOW_STM + HAL_FLASH_Unlock(); + FLASH_EraseInitTypeDef EraseInitStruct; + __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | + FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR); + EraseInitStruct.TypeErase = TYPEERASE_SECTORS; + EraseInitStruct.VoltageRange = VOLTAGE_RANGE_3; // voltage range needs to be 2.7V to 3.6V + EraseInitStruct.NbSectors = 1; + uint32_t SectorError = 0; + EraseInitStruct.Sector = NORCOW_START_SECTOR + sector; + HAL_StatusTypeDef r; + r = HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError); + HAL_FLASH_Lock(); + return r == HAL_OK; +#endif + return false; +} + +/* + * Returns pointer to sector, starting with offset + * Fails when there is not enough space for data of given size + */ +static const void *norcow_ptr(uint8_t sector, uint32_t offset, uint32_t size) +{ + if (sector >= NORCOW_SECTOR_COUNT) { + return NULL; + } + if (offset + size > NORCOW_SECTOR_SIZE) { + return NULL; + } +#ifdef NORCOW_UNIX + return (const void *)(norcow_buffer + sector * NORCOW_SECTOR_SIZE + offset); +#endif +#ifdef NORCOW_STM + return (const void *)(NORCOW_START_ADDRESS + sector * NORCOW_SECTOR_SIZE + offset); +#endif + return NULL; +} + +/* + * Writes data to given sector, starting from offset + */ +static bool norcow_write(uint8_t sector, uint32_t offset, uint32_t prefix, const uint8_t *data, uint32_t len) +{ + if (offset % 4) { // we write only at 4-byte boundary + return false; + } + const uint8_t *ptr = (const uint8_t *)norcow_ptr(sector, offset, sizeof(uint32_t) + len); + if (!ptr) { + return false; + } +#ifdef NORCOW_UNIX + // check whether we are about just change 1s to 0s + // and bailout if not + if ((*(uint32_t *)ptr & prefix) != prefix) { + return false; + } + for (uint32_t i = 0; i < len; i++) { + if ((ptr[sizeof(uint32_t) + i] & data[i]) != data[i]) { + return false; + } + } + memcpy((void *)ptr, &prefix, sizeof(uint32_t)); + memcpy((void *)(ptr + sizeof(uint32_t)), data, len); + norcow_sync(); + return true; +#endif +#ifdef NORCOW_STM + HAL_FLASH_Unlock(); + HAL_FLASH_Program(TYPEPROGRAM_WORD, NORCOW_START_ADDRESS + sector * NORCOW_SECTOR_SIZE + offset, prefix); + for (int i = 0; i < (len + 3) / sizeof(uint32_t); i++) { + const uint32_t *d = (const uint32_t *)(data + i * sizeof(uint32_t)); + HAL_FLASH_Program(TYPEPROGRAM_WORD, NORCOW_START_ADDRESS + sector * NORCOW_SECTOR_SIZE + offset + (1 + i) * sizeof(uint32_t), *d); + } + HAL_FLASH_Lock(); + return true; +#endif + return false; +} + +/* + * Aligns position to 4-byte boundary + */ +static inline void align4(uint32_t *pos) +{ + if (*pos & 3) { + *pos = (*pos + 3) & ~3; + } +} + +/* + * Reads one item starting from offset + */ +static bool read_item(uint8_t sector, uint32_t offset, uint16_t *key, const void **val, uint32_t *len, uint32_t *pos) +{ + *pos = offset; + + const void *k = norcow_ptr(sector, *pos, 2); + if (k == NULL) return false; + *pos += 2; + memcpy(key, k, sizeof(uint16_t)); + if (*key == 0xFFFF) { + return false; + } + + const void *l = norcow_ptr(sector, *pos, 2); + if (l == NULL) return false; + *pos += 2; + memcpy(len, l, sizeof(uint16_t)); + + *val = norcow_ptr(sector, *pos, *len); + if (*val == NULL) return false; + *pos += *len; + align4(pos); + return true; +} + +/* + * Writes one item starting from offset + */ +static bool write_item(uint8_t sector, uint32_t offset, uint16_t key, const void *val, uint32_t len, uint32_t *pos) +{ + uint32_t prefix = (len << 16) | key; + *pos = offset + sizeof(uint32_t) + len; + align4(pos); + return norcow_write(sector, offset, prefix, val, len); +} + +/* + * Finds item in given sector + */ +static bool find_item(uint8_t sector, uint16_t key, const void **val, uint32_t *len) +{ + *val = 0; + *len = 0; + uint32_t offset = 0; + for (;;) { + uint16_t k; + const void *v; + uint32_t l, pos; + bool r = read_item(sector, offset, &k, &v, &l, &pos); + if (!r) break; + if (key == k) { + *val = v; + *len = l; + } + offset = pos; + } + return (*val); +} + +/* + * Finds first unused offset in given sector + */ +static uint32_t find_free_offset(uint8_t sector) +{ + uint32_t offset = 0; + for (;;) { + uint16_t key; + const void *val; + uint32_t len, pos; + bool r = read_item(sector, offset, &key, &val, &len, &pos); + if (!r) break; + offset = pos; + } + return offset; +} + +/* + * Compacts active sector and sets new active sector + */ +static void compact() +{ + uint8_t norcow_next_sector = (norcow_active_sector + 1) % NORCOW_SECTOR_COUNT; + + uint32_t offset = 0, offsetw = 0; + + for (;;) { + // read item + uint16_t k; + const void *v; + uint32_t l, pos; + bool r = read_item(norcow_active_sector, offset, &k, &v, &l, &pos); + if (!r) break; + offset = pos; + + // check if not already saved + const void *v2; + uint32_t l2; + r = find_item(norcow_next_sector, k, &v2, &l2); + if (r) { + continue; + } + + // scan for latest instance + uint32_t offsetr = offset; + for (;;) { + uint16_t k2; + uint32_t posr; + r = read_item(norcow_active_sector, offsetr, &k2, &v2, &l2, &posr); + if (!r) break; + if (k == k2) { + v = v2; + l = l2; + } + offsetr = posr; + } + + // copy the last item + uint32_t posw; + r = write_item(norcow_next_sector, offsetw, k, v, l, &posw); + if (!r) { } // TODO: error + offsetw = posw; + } + + norcow_erase(norcow_active_sector); + norcow_active_sector = norcow_next_sector; + norcow_active_offset = find_free_offset(norcow_active_sector); +} + +/* + * Initializes storage + */ +bool norcow_init(void) +{ +#ifdef NORCOW_UNIX + memset(norcow_buffer, 0xFF, sizeof(norcow_buffer)); + FILE *f = fopen(NORCOW_FILE, "rb"); + if (f) { + size_t r = fread(norcow_buffer, sizeof(norcow_buffer), 1, f); + fclose(f); + printf("%d\n", (int)r); + if (r != 1) { + memset(norcow_buffer, 0xFF, sizeof(norcow_buffer)); + } + } +#endif + // detect active sector (inactive sectors are empty = start with 0xFF) + for (uint8_t i = 0; i < NORCOW_SECTOR_COUNT; i++) { + const uint8_t *b = norcow_ptr(i, 0, 1); + if (b != NULL && *b != 0xFF) { + norcow_active_sector = i; + break; + } + } + norcow_active_offset = find_free_offset(norcow_active_sector); + return true; +} + +/* + * Wipe the storage + */ +bool norcow_wipe(void) +{ + for (uint8_t i = 0; i < NORCOW_SECTOR_COUNT; i++) { + if (!norcow_erase(i)) { + return false; + } + } + norcow_active_sector = 0; + norcow_active_offset = 0; + return true; +} + +/* + * Looks for the given key, returns status of the operation + */ +bool norcow_get(uint16_t key, const void **val, uint32_t *len) +{ + return find_item(norcow_active_sector, key, val, len); +} + +/* + * Sets the given key, returns status of the operation + */ +bool norcow_set(uint16_t key, const void *val, uint32_t len) +{ + // check whether there is enough free space + // and compact if full + if (norcow_active_offset + sizeof(uint32_t) + len > NORCOW_SECTOR_SIZE) { + compact(); + } + // write item + uint32_t pos; + bool r = write_item(norcow_active_sector, norcow_active_offset, key, val, len, &pos); + if (r) { + norcow_active_offset = pos; + } + return r; +} diff --git a/micropython/extmod/modtrezorconfig/norcow.h b/micropython/extmod/modtrezorconfig/norcow.h deleted file mode 120000 index f33ecb1577..0000000000 --- a/micropython/extmod/modtrezorconfig/norcow.h +++ /dev/null @@ -1 +0,0 @@ -../../../vendor/norcow/norcow.h \ No newline at end of file diff --git a/micropython/extmod/modtrezorconfig/norcow.h b/micropython/extmod/modtrezorconfig/norcow.h new file mode 100644 index 0000000000..50f7d1c223 --- /dev/null +++ b/micropython/extmod/modtrezorconfig/norcow.h @@ -0,0 +1,34 @@ +#ifndef __NORCOW_H__ +#define __NORCOW_H__ + +#include +#include + +/* + * Storage parameters: + */ + +#define NORCOW_SECTOR_COUNT 2 +#define NORCOW_SECTOR_SIZE (16*1024) + +/* + * Initialize storage + */ +bool norcow_init(void); + +/* + * Wipe the storage + */ +bool norcow_wipe(void); + +/* + * Looks for the given key, returns status of the operation + */ +bool norcow_get(uint16_t key, const void **val, uint32_t *len); + +/* + * Sets the given key, returns status of the operation + */ +bool norcow_set(uint16_t key, const void *val, uint32_t len); + +#endif diff --git a/micropython/extmod/modtrezorconfig/norcow_config.h b/micropython/extmod/modtrezorconfig/norcow_config.h index 74e19ffc45..99738d61bf 100644 --- a/micropython/extmod/modtrezorconfig/norcow_config.h +++ b/micropython/extmod/modtrezorconfig/norcow_config.h @@ -2,3 +2,9 @@ #define NORCOW_UNIX 1 #define NORCOW_FILE "/var/tmp/trezor.config" #endif + +#ifdef STM32_HAL_H +#define NORCOW_STM 1 +#define NORCOW_START_SECTOR 2 +#define NORCOW_START_ADDRESS 0x08008000 +#endif diff --git a/micropython/extmod/modtrezorconfig/norcow_test.c b/micropython/extmod/modtrezorconfig/norcow_test.c new file mode 100644 index 0000000000..a64d83aad7 --- /dev/null +++ b/micropython/extmod/modtrezorconfig/norcow_test.c @@ -0,0 +1,45 @@ +#include +#include +#include +#include +#include + +#include "norcow.h" + +#define MAXVALLEN 1024 + +uint8_t val[MAXVALLEN], *v; +uint16_t key; +uint32_t vallen, vlen; + +int main() +{ + srand(time(0)); + + norcow_init(); + + bool r; + for (int i = 0; i < 10000; i++) { + + vallen = rand() % (MAXVALLEN + 1); + for (uint32_t j = 0; j < vallen; j++) { + val[j] = rand() & 0xFF; + } + + key = 0x1234 + (rand() % 32); + + printf("#%d key=0x%04x size=%d\n", i, key, vallen); + + r = norcow_set(key, val, vallen); + if (!r) { + printf("Write failed (full storage)\n"); + continue; + } + + r = norcow_get(key, (const void **)&v, &vlen); + assert(r == 1); + + assert(vlen == vallen); + assert(0 == memcmp(val, v, vallen)); + } +}