From 36b55483a8f1beab5be84a769afa9bb35c488002 Mon Sep 17 00:00:00 2001 From: sys64738 Date: Tue, 24 Aug 2021 01:22:24 +0200 Subject: [PATCH] fix compilation, more stuff --- CMakeLists.txt | 2 +- bsp/rp2040/bsp-info.h | 4 +- bsp/rp2040/bsp-storage.h | 72 +++++++++++++++++++--- src/main.c | 4 +- src/storage.c | 41 ++++++++----- src/storage.h | 6 +- src/storage_internal.h | 4 +- src/storage_save.c | 129 ++++++++++++++++++++++++++++++++------- 8 files changed, 209 insertions(+), 53 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6517772..d67157a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -110,7 +110,7 @@ target_include_directories(${PROJECT} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/bsp/default/ ) -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Werror=implicit-function-declaration -Werror=return-type") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Werror=implicit-function-declaration -Werror=return-type -Werror=maybe-uninitialized") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra") add_custom_target(fix_db ALL WORKING_DIRECTORY ${OUTPUT_DIR} diff --git a/bsp/rp2040/bsp-info.h b/bsp/rp2040/bsp-info.h index 2120d37..811d14e 100644 --- a/bsp/rp2040/bsp-info.h +++ b/bsp/rp2040/bsp-info.h @@ -23,8 +23,8 @@ #define CFG_TUD_VENDOR 1 /* don't access storage for RAM-only builds */ -#ifndef PICO_NO_FLASH -#define BOARD_HAS_STORAGE +#if !PICO_NO_FLASH +#define DBOARD_HAS_STORAGE #endif #endif diff --git a/bsp/rp2040/bsp-storage.h b/bsp/rp2040/bsp-storage.h index 983544e..23ce8e8 100644 --- a/bsp/rp2040/bsp-storage.h +++ b/bsp/rp2040/bsp-storage.h @@ -7,16 +7,22 @@ #include #include -#ifndef PICO_NO_FLASH +#if !PICO_NO_FLASH -#include +#include + +#include +#include +#include #include +#include #ifndef PICO_FLASH_SIZE_BYTES -#warning "PICO_FLASH_SIZE_BYTES not defined, defaulting to 2 megabytes. This WILL break if your flash is smaller!" -#define PICO_FLASH_SIZE_BYTES (2*1024*1024) +#error "PICO_FLASH_SIZE_BYTES not defined" #endif +#define STORAGE_RAM_FUNC(x) __no_inline_not_in_flash_func(x) + static inline size_t storage_get_program_size(void) { extern uint8_t __flash_binary_start, __flash_binary_end; @@ -41,16 +47,68 @@ static inline void storage_read(void* dest, size_t offset, size_t size) { // * SSI DMA: blocking & fast, code needs to run from RAM. a bit unwieldy memcpy(dest, (uint8_t*)(XIP_BASE+offset), size); } -static inline bool storage_erasewrite(size_t offset, const void* src, size_t size) { +static bool STORAGE_RAM_FUNC(storage_erasewrite)(size_t offset, const void* src, size_t size) { // bad alignment => give an error in advance if (offset & (FLASH_SECTOR_SIZE - 1)) return false; if (size & (FLASH_SECTOR_SIZE - 1)) return false; + // memcmp pre: if no changes, don't flash + for (size_t i = 0; i < size; ++i) { + if (*(uint8_t*)(XIP_BASE + offset + i) != ((const uint8_t*)src)[i]) { + goto do_flash; + } + + asm volatile("":::"memory"); // make sure the compiler won't turn this into a memcmp call + } + + return true; // all equal, nothing to do, all is fine + +do_flash: + // these functions are RAM-only-safe flash_range_erase(offset, size); + // TODO: only program ranges written to? flash_range_program(offset, src, size); - // maybe not a bad idea either - return !memcmp(src, (uint8_t*)(XIP_BASE+offset), size); + // now do a memcmp, kinda badly but we can't call newlib memcmp + + for (size_t i = 0; i < size; ++i) { + if (*(uint8_t*)(XIP_BASE + offset + i) != ((const uint8_t*)src)[i]) { + return false; // welp + } + + asm volatile("":::"memory"); // make sure the compiler won't turn this into a memcmp call + } + + return true; +} + +static void* STORAGE_RAM_FUNC(storage_extra_ram_get_base)(void) { + return (void*)XIP_SRAM_BASE; +} + +typedef uint32_t storage_extra_ram_temp_t; + +static storage_extra_ram_temp_t STORAGE_RAM_FUNC(storage_extra_ram_enable)(void) { + uint32_t flags = save_and_disable_interrupts(); + + xip_ctrl_hw->flush = 1; // flush XIP stuff + // wait until flush is done + while (!(xip_ctrl_hw->stat & XIP_STAT_FLUSH_RDY)) + asm volatile("nop":::"memory"); + + xip_ctrl_hw->ctrl &= ~(io_rw_32)XIP_CTRL_EN_BITS; // disable XIP cache -> can now use it as SRAM + + // interrupts may access flash + //restore_interrupts(flags); + + return flags; +} +static void STORAGE_RAM_FUNC(storage_extra_ram_disable)(storage_extra_ram_temp_t flags) { + //uint32_t flags = save_and_disable_interrupts(); + + xip_ctrl_hw->ctrl |= XIP_CTRL_EN_BITS; // reenable XIP cache + + restore_interrupts(flags); } #endif diff --git a/src/main.c b/src/main.c index 35a8b2f..1951908 100644 --- a/src/main.c +++ b/src/main.c @@ -32,9 +32,9 @@ int main() { vndcfg_thread = co_derive(vndcfg_stack, sizeof vndcfg_stack, vndcfg_thread_fn); thread_enter(vndcfg_thread); - storage_init(); // sets mode_next_id + int startupmode = storage_init(); - modes_init(mode_next_id); + modes_init(startupmode); if (mode_current) mode_current->enter(); tusb_init(); diff --git a/src/storage.c b/src/storage.c index 993e85b..931cb46 100644 --- a/src/storage.c +++ b/src/storage.c @@ -5,8 +5,8 @@ #include "mode.h" #include "storage.h" -#ifndef BOARD_HAS_STORAGE -void storage_init(void) { } +#ifndef DBOARD_HAS_STORAGE +int storage_init(void) { return -1; } void storage_flush_data(void) { } struct mode_info storage_mode_get_size(int _) { (void)_; return (struct mode_info){ .size = 0, .version = 0 }; @@ -16,11 +16,21 @@ void storage_mode_read(int _, void* __) { (void)_; (void)__; } #include "storage_internal.h" +uint32_t str_hash_djb2_digest(uint32_t hash, const void* data, size_t len) { + const uint8_t* d = data; + + for (size_t i = 0; i < len; ++i) hash += hash * 33 + d[i]; + + return hash; +} + bool header_valid = false; struct storage_header header_tmp; -uint8_t data_tmp[1024 - sizeof(struct storage_header)]; +uint8_t data_tmp[256]; uint16_t mode_bad = 0; +static int in_init = 0; + static void storage_init_defaults(void) { memcpy(header_tmp.magic, STORAGE_MAGIC, STORAGE_MAGIC_LEN); header_tmp.fwversion = STORAGE_VER; @@ -34,8 +44,8 @@ static void storage_init_defaults(void) { header_valid = true; } -void storage_init(void) { - mode_next_id = -1; // by default, boot to default mode +int storage_init(void) { + ++in_init; mode_bad = 0; storage_read(&header_tmp, STORAGE_SIZE - sizeof(struct storage_header), @@ -44,14 +54,16 @@ void storage_init(void) { bool bad = false; if (memcmp(header_tmp.magic, STORAGE_MAGIC, STORAGE_MAGIC_LEN)) { storage_init_defaults(); - storage_flush_data(); - return; + if (in_init == 1) storage_flush_data(); + --in_init; + return -1; } if (header_tmp.fwversion != STORAGE_VER) { // TODO: migrate... if there were any older versions header_valid = false; - return; + --in_init; + return -1; } if (header_tmp.nmodes >= 16) bad = true; @@ -64,13 +76,14 @@ void storage_init(void) { if (bad) { storage_init_defaults(); - storage_flush_data(); - return; + if (in_init == 1) storage_flush_data(); + --in_init; + return -1; } - mode_next_id = header_tmp.curmode; - header_valid = true; + --in_init; + return header_tmp.curmode; } struct mode_info storage_mode_get_info(int mode) { @@ -89,7 +102,7 @@ struct mode_info storage_mode_get_info(int mode) { uint32_t mdoffset = md.offsetandmode & ((1<<28)-1); if (mdmode != mode) continue; - if (~mdsize == 0 || ~md.version == 0 || ~offsetandmode == 0) + if (mdsize == 0xffff || md.version == 0xffff || md.offsetandmode == 0xffffffffu) continue; // empty (wut?) // found it! @@ -141,7 +154,7 @@ void storage_mode_read(int mode, void* dst) { uint32_t mdoffset = md.offsetandmode & ((1<<28)-1); if (mdmode != mode) continue; - if (~mdsize == 0 || ~md.version == 0 || ~offsetandmode == 0) + if (mdsize == 0xffff || md.version == 0xffff || md.offsetandmode == 0xffffffffu) continue; // empty (wut?) // found it! diff --git a/src/storage.h b/src/storage.h index 07a9caa..e77a06a 100644 --- a/src/storage.h +++ b/src/storage.h @@ -19,8 +19,8 @@ enum mode_storage_class { // mode callbacks used by the storage subsystem struct mode_storage { enum mode_storage_class stclass; - uint32_t (*get_size)(void); - void (*get_data)(void* dst); + uint16_t (*get_size)(void); + void (*get_data)(void* dst, size_t offset, size_t maxsize); bool (*is_dirty)(void); // if data was changed since last mode_read/get_data call }; @@ -37,7 +37,7 @@ void storage_mode_read(int mode, void* dst); // global functions // reads all data, creates table if needed -void storage_init(void); +int storage_init(void); // flush edits if anything has been edited void storage_flush_data(void); diff --git a/src/storage_internal.h b/src/storage_internal.h index 3b28681..5c32348 100644 --- a/src/storage_internal.h +++ b/src/storage_internal.h @@ -8,7 +8,7 @@ inline static uint32_t str_hash_djb2_init(void) { } uint32_t str_hash_djb2_digest(uint32_t hash, const void* data, size_t len); inline static uint32_t str_hash_djb2(const void* data, size_t len) { - return str_hash_djb2_digestt(str_hash_djb2_init(), data, len); + return str_hash_djb2_digest(str_hash_djb2_init(), data, len); } /* @@ -62,7 +62,7 @@ __attribute__((__packed__)) struct storage_header { extern bool header_valid; extern struct storage_header header_tmp; -extern uint8_t data_tmp[1024 - sizeof(struct storage_header)]; +extern uint8_t data_tmp[256]; extern uint16_t mode_bad; #endif diff --git a/src/storage_save.c b/src/storage_save.c index ff94809..2abe21b 100644 --- a/src/storage_save.c +++ b/src/storage_save.c @@ -5,22 +5,116 @@ #include "mode.h" #include "storage.h" -#ifdef BOARD_HAS_STORAGE +#ifdef DBOARD_HAS_STORAGE #include "storage_internal.h" -static void storage_write_data(void) { +static bool storage_mode_has(int i) { + if (mode_list[i]->storage.stclass == mode_storage_none) return false; + if (mode_list[i]->storage.get_size == NULL) return false; + if (mode_list[i]->storage.get_data == NULL) return false; + if (mode_list[i]->storage.is_dirty == NULL) return false; + + return true; +} + +static struct mode_storage msto[16]; + +static size_t storage_allocate_new(void) { + static const size_t stclass_sz[] = { 0, 32, 128, 512, 0xffffffff }; + + memcpy(header_tmp.magic, STORAGE_MAGIC, STORAGE_MAGIC_LEN); + memset(header_tmp.reserved, 0xff, sizeof(header_tmp.reserved)); + memset(header_tmp.mode_data_table, 0xff, sizeof(header_tmp.mode_data_table)); + + header_tmp.fwversion = STORAGE_VER; + header_tmp.curmode = mode_current_id; + header_tmp.nmodes = 0; + + size_t current_page = STORAGE_SIZE - STORAGE_ERASEWRITE_ALIGN, + current_page_end = STORAGE_SIZE - sizeof(struct storage_header); + size_t current_wrhead = current_page; + size_t npages = 1; + + for (enum mode_storage_class stcls = mode_storage_32b; stcls <= mode_storage_big; ++stcls) { + for (int mode = 1; mode < 16; ++mode) { + if (mode_list[mode] == NULL || !storage_mode_has(mode)) continue; + if (mode_list[mode]->storage.stclass != stcls) continue; + + // too big for the class? don't write the data, then + uint16_t dsize = mode_list[mode]->storage.get_size(); + if (dsize > stclass_sz[stcls]) continue; + + if (current_wrhead + dsize > current_page_end) { // welp + current_page_end = current_page; + current_page -= STORAGE_ERASEWRITE_ALIGN; + current_wrhead = current_page; + ++npages; + + if (current_page < storage_get_program_offset() + storage_get_program_size()) + return 0; // welp, out of space + } + + struct mode_data* md = &header_tmp.mode_data_table[header_tmp.nmodes]; + md->version = mode_list[mode]->version; + md->datasize = dsize; + md->offsetandmode = current_wrhead | ((uint32_t)mode << 28); + msto[header_tmp.nmodes] = mode_list[mode]->storage; // copy to RAM because mode_list is in rodata! + + current_wrhead += stclass_sz[stcls]; + + uint32_t hash = str_hash_djb2_init(); + + for (size_t i = 0; i < dsize; i += sizeof(data_tmp)) { + size_t tohash = sizeof(data_tmp); + if (dsize - i < tohash) tohash = dsize - i; + + mode_list[mode]->storage.get_data(data_tmp, i, tohash); + + hash = str_hash_djb2_digest(hash, data_tmp, tohash); + } + + md->data_djb2 = hash; + ++header_tmp.nmodes; + } + } + + header_tmp.table_djb2 = str_hash_djb2(header_tmp.mode_data_table, + sizeof(header_tmp.mode_data_table)); + + return npages; +} + +static void STORAGE_RAM_FUNC(storage_serialize_xip)(size_t pageid, + size_t pagestart, size_t pageend, void* dest) { + (void)pageid; (void)pagestart; (void)pageend; (void)dest; + // BIG TODO - // * try to allocate every mode data thing, smaller stuff (cf. mode storage - // class) at higher addrs, bigger ones at lower ones. can use get_size(), - // but maybe keep data aligned - // * for each flash page, copy data into a big buffer. maybe use the XIP - // cache because the running mode may be already using all data. all - // get_data() functions and the bsp write function need to run from RAM, - // though - // * then write it out - // * maybe we could only write pages that changed? or maybe the bsp code - // can take care of this +} + +static void STORAGE_RAM_FUNC(storage_write_data)(void) { + size_t npages = storage_allocate_new(); + if (npages == 0) { + storage_init(); + return; // TODO: error, somehow + } + + storage_extra_ram_temp_t ramtmp = storage_extra_ram_enable(); + + size_t current_page = STORAGE_SIZE - STORAGE_ERASEWRITE_ALIGN, + current_page_end = STORAGE_SIZE - sizeof(struct storage_header); + for (size_t page = 0; page < npages; ++page) { + storage_serialize_xip(page, current_page, current_page_end, + storage_extra_ram_get_base()); + + storage_erasewrite(current_page, storage_extra_ram_get_base(), + STORAGE_ERASEWRITE_ALIGN); + + current_page_end = current_page; + current_page -= STORAGE_ERASEWRITE_ALIGN; + } + + storage_extra_ram_disable(ramtmp); // also TODO: // * call storage_flush_data on vnd cfg command @@ -29,15 +123,6 @@ static void storage_write_data(void) { // * try to save when unplugging??? } -static bool storage_mode_has(int mode) { - if (mode_list[i]->storage.stclass == mode_storage_none) return false; - if (mode_list[i]->get_size == NULL) return false; - if (mode_list[i]->get_data == NULL) return false; - if (mode_list[i]->is_dirty == NULL) return false; - - return true; -} - void storage_flush_data(void) { if (mode_bad != 0 || mode_current_id != header_tmp.curmode) { storage_write_data(); @@ -45,7 +130,7 @@ void storage_flush_data(void) { if (mode_list[i] == NULL || !storage_mode_has(i)) continue; if (mode_list[i]->storage.is_dirty()) { - storage_write_data(i); + storage_write_data(); return; } }