From 9fc5f4468994065c96b16cb093b861b1dd108dc3 Mon Sep 17 00:00:00 2001 From: surenyi Date: Wed, 23 Oct 2024 14:59:21 +0800 Subject: [PATCH] add ubootenv api Signed-off-by: surenyi --- Makefile | 4 +- bspApi.h | 5 + bspEnv.c | 161 +++++++++ bspStubs.c | 27 +- config.h | 5 +- hwconf.c | 2 +- sysLib.c | 5 +- ubootenv/crc32.c | 113 +++++++ ubootenv/libuboot.h | 145 ++++++++ ubootenv/uboot_env.c | 699 +++++++++++++++++++++++++++++++++++++++ ubootenv/uboot_private.h | 190 +++++++++++ vxbLfsLib.c | 6 +- 12 files changed, 1342 insertions(+), 20 deletions(-) create mode 100644 bspEnv.c create mode 100644 ubootenv/crc32.c create mode 100644 ubootenv/libuboot.h create mode 100644 ubootenv/uboot_env.c create mode 100644 ubootenv/uboot_private.h diff --git a/Makefile b/Makefile index 312ed5a..fecc97b 100644 --- a/Makefile +++ b/Makefile @@ -20,8 +20,8 @@ RELEASE += bootrom_uncmp.hex MACH_EXTRA += vxbArmGenIntCtlrV3.o vxbArmv7GenTimer.o vxbArmv7AuxTimer.o \ vxbFtPcie.o vxbAhciStorage.o vxbFtGmacEnd.o vxbFtcan.o vxbPci.o \ vxbFtSdCtrl.o vxbFtI2c.o vxbYt8521Phy.o genericPhy.o vxbFtQspi.o\ - vxbSp25SpiFlash.o vxbFtGpio.o vxbFtSpi.o vxbSm2130SpiDev.o \ - bspStubs.o vxbLfsLib.o + vxbSp25SpiFlash.o vxbFtGpio.o vxbFtSpi.o vxbSm2130SpiDev.o vxbLfsLib.o \ + bspStubs.o bspEnv.o \ ifneq ($(findstring bootrom,$(MAKECMDGOALS)),bootrom) LIB_EXTRA = lib/libFtX100dcdrv.a diff --git a/bspApi.h b/bspApi.h index 4683c25..1a3f0aa 100644 --- a/bspApi.h +++ b/bspApi.h @@ -31,6 +31,11 @@ struct sm2130_xfer_mem { typedef struct sm2130_xfer_mem SM2130_MEM_XFER; /* }}} */ +/* functions */ +int envInit(); +int bspLoadUserApp(); +int updateVxWorks(const char *file, int pos); + #ifdef __cplusplus } #endif diff --git a/bspEnv.c b/bspEnv.c new file mode 100644 index 0000000..54611a4 --- /dev/null +++ b/bspEnv.c @@ -0,0 +1,161 @@ +#include +#include +#include +#include +#include "ubootenv/crc32.c" +#include "ubootenv/uboot_env.c" +#include "config.h" + +extern int lfsLowRawWrite(unsigned long offset, const char *buffer, int size); + +static int env_read(unsigned int offset, void *buf, size_t len) +{ + memcpy(buf, (void *)(offset), len); + return 0; +} + +static int env_write(unsigned int offset, const void *buf, size_t size) +{ + lfsLowRawWrite(offset, buf, size); + return 0; +} + +/* clang-format off */ +static struct uboot_env_device __bsp_uenv = { + "qspiflash", + QSPI_ENV_OFFSET, + 0x10000, + 0x1000, + 0x10, + env_read, + env_write, +}; +/* clang-format on */ +static struct uboot_ctx *__ctx = NULL; +static SEM_ID __ctx_lock = NULL; + +int uenvInit() +{ + int err = 0; + + if (__ctx == NULL) { + if (!__ctx_lock) { + __ctx_lock = semBCreate(SEM_Q_FIFO, SEM_FULL); + } + semTake(__ctx_lock, WAIT_FOREVER); + if (__ctx == NULL) { + err = libuboot_initialize(&__ctx, &__bsp_uenv, 1); + if (err) { + semGive(__ctx_lock); + goto skip; + } + if (libuboot_open(__ctx)) { + libuboot_exit(__ctx); + err = -101; + __ctx = NULL; + } + } + semGive(__ctx_lock); + } + +skip: + return (err); +} + +int uenvget(const char *varname, char *buf, size_t len) +{ + const char *val = NULL; + int r = -1; + + if (__ctx) { + semTake(__ctx_lock, WAIT_FOREVER); + val = libuboot_get_env(__ctx, varname); + if (val) { + strncpy(buf, val, len); + r = 0; + } + semGive(__ctx_lock); + } + return r; +} + +unsigned int uenvGetUint(const char *varname) +{ + char value[64] = {0}; + if (uenvget(varname, value, sizeof value) == 0) { + return strtoul(value, NULL, 16); + } + return (unsigned int)(-1); +} + +int setenv(const char *varname, const char *value) +{ + int err = -1; + + if (__ctx) { + semTake(__ctx_lock, WAIT_FOREVER); + err = libuboot_set_env(__ctx, varname, value); + semGive(__ctx_lock); + } + return (err); +} + +int saveenv() +{ + int err = -1; + + if (__ctx) { + semTake(__ctx_lock, WAIT_FOREVER); + err = libuboot_env_store(__ctx); + semGive(__ctx_lock); + } + return (err); +} + +static void *envNext(void *next) +{ + if (__ctx) { + return libuboot_iterator(__ctx, next); + } + return NULL; +} + +static const char *envGetName(void *entry) +{ + return libuboot_getname(entry); +} + +static const char *envGetValue(void *entry) +{ + return libuboot_getvalue(entry); +} + +int printenv(const char *name) +{ + void *entry; + + if (!__ctx) { + return -1; + } + + semTake(__ctx_lock, WAIT_FOREVER); + if (name) { + const char *val; + val = libuboot_get_env(__ctx, name); + if (val) { + printf("%s\r\n", val); + } else { + printf("%s: not found\r\n", name); + } + semGive(__ctx_lock); + return 0; + } + entry = envNext(NULL); + + while (entry) { + printf("%18s: %s\n", envGetName(entry), envGetValue(entry)); + entry = envNext(entry); + } + semGive(__ctx_lock); + return 0; +} diff --git a/bspStubs.c b/bspStubs.c index 8f54cce..def98dd 100644 --- a/bspStubs.c +++ b/bspStubs.c @@ -1,14 +1,17 @@ #include #include #include +#include #include #include #include +#include #include #include #include - +#include #include "config.h" +#include "ubootenv/libuboot.h" #include "vxbFtGpio.h" #if defined(INCLUDE_IPFTPS) #include @@ -21,7 +24,9 @@ /* externs */ extern int lfsLowRawWrite(unsigned long offset, const char *buffer, int size); -extern void gpioIsrSetTest (UINT32 gpio, UINT32 pin); +extern unsigned int uenvGetUint(const char *varname); +extern int uenvInit(); + int bspLoadUserApp() { SEGMENT_ID seg; @@ -62,6 +67,7 @@ int bspLoadUserApp() #if defined(__DCC__) void usrAppInit(void) { + uenvInit(); bspLoadUserApp(); } #endif @@ -77,7 +83,13 @@ int updateVxWorks(const char *file, int pos) printf("Can't stat %s\r\n", file); return ERROR; } - + if (pos <= 0) { + pos = (int)uenvGetUint("krnaddr"); + } + if (pos <= QSPI_PROTECT_SIZE) { + printf("Unknown flash offset (0x%x)\r\n", pos); + return -1; + } size = (stbuf.st_size + 0x10000 - 1) / 0x10000; size *= 0x10000; ptr = malloc(size); @@ -95,7 +107,7 @@ int updateVxWorks(const char *file, int pos) return ERROR; } printf("Reading: %d bytes\r\n", stbuf.st_size); - size = read(fd, ptr, stbuf.st_size); + size = read(fd, (void *)ptr, stbuf.st_size); close(fd); if (size != stbuf.st_size) { @@ -105,11 +117,8 @@ int updateVxWorks(const char *file, int pos) } size = (stbuf.st_size + 0x100 - 1) / 0x100; size *= 0x100; - printf("Write to flash\r\n"); - if (pos <= 0) { - pos = VXWORKS_POS; - } - status = lfsLowRawWrite(pos, ptr, size); + printf("Write to flash offset: 0x%x, len: %d\r\n", pos, stbuf.st_size); + status = lfsLowRawWrite(pos, (void *)ptr, size); free(ptr); printf("%s\r\n", status == OK ? "Success" : "Failed"); return status; diff --git a/config.h b/config.h index 0837d0a..d486aad 100644 --- a/config.h +++ b/config.h @@ -191,7 +191,8 @@ extern "C" { #define SPI_FLASH_SECTOR_NUM ((SPI_FLASH_SIZE) / (SPI_FLASH_SECTOR_SIZE)) #define SPI_BOOTROM_SIZE (0x400000) #define SPI_KERNEL_SIZE (0x600000) - +#define QSPI_PROTECT_SIZE (0x2A0000) +#define QSPI_ENV_OFFSET (0x2f0000) #if defined(INCLUDE_TFFS) #define INCLUDE_TFFS_MOUNT #define INCLUDE_TFFS_SHOW @@ -292,8 +293,6 @@ extern "C" { #define INCLUDE_ISR_OBJECTS -#define USER_APPL_INIT (loadUserApp()) - #ifdef __cplusplus } #endif diff --git a/hwconf.c b/hwconf.c index 63c4ac5..780e1e5 100644 --- a/hwconf.c +++ b/hwconf.c @@ -592,7 +592,7 @@ const struct hcfDevice hcfDeviceList[] = { { "ftGpio", 1, VXB_BUSID_PLB, 0, gpioDev1Num, gpioDev1Resources }, #endif #ifdef DRV_FS_LITTLEFS -{ "lfs", 0, VXB_BUSID_PLB, 0, lfsResNum, lfsResources}, +{ "littlefs", 0, VXB_BUSID_PLB, 0, lfsResNum, lfsResources}, #endif { "SM2130", 0, VXB_BUSID_SPI, 0, sm2130ResNum, sm2130Resources}, }; diff --git a/sysLib.c b/sysLib.c index 6baf35a..213296b 100644 --- a/sysLib.c +++ b/sysLib.c @@ -205,12 +205,12 @@ PHYS_MEM_DESC sysPhysMemDesc[] = { #ifdef DRV_FTQSPI { /* Qspi BootRom */ - SPI_FLASH_BASE_ADRS, SPI_FLASH_BASE_ADRS, SPI_BOOTROM_SIZE, + SPI_FLASH_BASE_ADRS, SPI_FLASH_BASE_ADRS, QSPI_PROTECT_SIZE, MMU_ATTR_VALID_MSK | MMU_ATTR_PROT_MSK | MMU_ATTR_DEVICE_SHARED_MSK, MMU_ATTR_VALID | MMU_ATTR_SUP_RO | MMU_ATTR_DEVICE_SHARED }, { /* Qspi Flash */ - SPI_FLASH_BASE_ADRS + SPI_BOOTROM_SIZE, SPI_FLASH_BASE_ADRS + SPI_BOOTROM_SIZE, SPI_FLASH_SIZE - SPI_BOOTROM_SIZE, + SPI_FLASH_BASE_ADRS + QSPI_PROTECT_SIZE, SPI_FLASH_BASE_ADRS + QSPI_PROTECT_SIZE, SPI_FLASH_SIZE - QSPI_PROTECT_SIZE, MMU_ATTR_VALID_MSK | MMU_ATTR_PROT_MSK | MMU_ATTR_DEVICE_SHARED_MSK, MMU_ATTR_VALID | MMU_ATTR_SUP_RW | MMU_ATTR_DEVICE_SHARED }, #endif @@ -580,7 +580,6 @@ void sysHwInit(void) reg &= ~((0x3 << 12) | (0x3 << 8) | 0x3); reg |= (1 << 12) | ( 1 << 8) | 1; writel(reg, PIN_DEMUX_BASE + REG200); - #ifdef DRV_X100DC ftX100DcDevicePciRegister(); #endif diff --git a/ubootenv/crc32.c b/ubootenv/crc32.c new file mode 100644 index 0000000..766b68f --- /dev/null +++ b/ubootenv/crc32.c @@ -0,0 +1,113 @@ +#include +#include +static const uint32_t crc_table[256] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, + 0x076dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L, + 0x0edb8832L, 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L, + 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 0x90bf1d91L, + 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, + 0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, + 0x14015c4fL, 0x63066cd9L, 0xfa0f3d63L, 0x8d080df5L, + 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 0xa2677172L, + 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, + 0x32d86ce3L, 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, + 0x26d930acL, 0x51de003aL, 0xc8d75180L, 0xbfd06116L, + 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 0xb8bda50fL, + 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, + 0x76dc4190L, 0x01db7106L, 0x98d220bcL, 0xefd5102aL, + 0x71b18589L, 0x06b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L, + 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 0xe10e9818L, + 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, + 0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, + 0x65b0d9c6L, 0x12b7e950L, 0x8bbeb8eaL, 0xfcb9887cL, + 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L, + 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, + 0x4369e96aL, 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, + 0x44042d73L, 0x33031de5L, 0xaa0a4c5fL, 0xdd0d7cc9L, + 0x5005713cL, 0x270241aaL, 0xbe0b1010L, 0xc90c2086L, + 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, + 0x59b33d17L, 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, + 0xedb88320L, 0x9abfb3b6L, 0x03b6e20cL, 0x74b1d29aL, + 0xead54739L, 0x9dd277afL, 0x04db2615L, 0x73dc1683L, + 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, + 0xf00f9344L, 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, + 0xf762575dL, 0x806567cbL, 0x196c3671L, 0x6e6b06e7L, + 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 0x67dd4accL, + 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, + 0xd1bb67f1L, 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, + 0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L, 0x41047a60L, + 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 0x4669be79L, + 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, + 0xc5ba3bbeL, 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, + 0xc2d7ffa7L, 0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL, + 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 0x026d930aL, + 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, + 0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, + 0x86d3d2d4L, 0xf1d4e242L, 0x68ddb3f8L, 0x1fda836eL, + 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 0x18b74777L, + 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, + 0xa00ae278L, 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, + 0xa7672661L, 0xd06016f7L, 0x4969474dL, 0x3e6e77dbL, + 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 0x37d83bf0L, + 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, + 0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL, + 0xb3667a2eL, 0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L, + 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 0x2d02ef8dL +}; + +#define DO_CRC(x) crc = tab[(crc ^ (x)) & 255] ^ (crc >> 8) + +static uint32_t crc32_no_comp(uint32_t crc, const uint8_t *buf, size_t len) +{ + const uint32_t *tab = crc_table; + const uint32_t *b =(const uint32_t *)buf; + size_t rem_len; + + /* Align it */ + if (((long)b) & 3 && len) { + uint8_t *p = (uint8_t *)b; + do { + DO_CRC(*p++); + } while ((--len) && ((long)p)&3); + b = (uint32_t *)p; + } + + rem_len = len & 3; + len = len >> 2; + for (--b; len; --len) { + /* load data 32 bits wide, xor data 32 bits wide. */ + crc ^= *++b; /* use pre increment for speed */ + DO_CRC(0); + DO_CRC(0); + DO_CRC(0); + DO_CRC(0); + } + len = rem_len; + /* And the last few bytes */ + if (len) { + uint8_t *p = (uint8_t *)(b + 1) - 1; + do { + DO_CRC(*++p); /* use pre increment for speed */ + } while (--len); + } + return crc; +} +#undef DO_CRC + +uint32_t crc32 (uint32_t crc, const uint8_t *p, size_t len) +{ + return crc32_no_comp(crc ^ 0xffffffffL, p, len) ^ 0xffffffffL; +} + diff --git a/ubootenv/libuboot.h b/ubootenv/libuboot.h new file mode 100644 index 0000000..a05e086 --- /dev/null +++ b/ubootenv/libuboot.h @@ -0,0 +1,145 @@ +#ifndef __LIBUBOOT_H___ +#define __LIBUBOOT_H___ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +struct uboot_ctx; + +#define DEVNAME_MAX_LENGTH 256 + +/** + * Configuration passed in initialization + * + */ +struct uboot_env_device { + /** path to device or file where env is stored */ + char *devname; + /** Start offset inside device path */ + long long int offset; + /** size of environment */ + size_t envsize; + /** Size of sector */ + size_t sectorsize; + /** Number of sectors for each environment */ + unsigned long envsectors; + + /* operators */ + int (*read)(unsigned int offset, void *buf, size_t len); + int (*write)(unsigned int offset, const void *buf, size_t len); + int (*erase)(unsigned int offset, size_t len); +}; + +/** @brief Read U-Boot environment configuration from structure + * + * @param[in] ctx libuboot context + * @param[in] envdevs array of two uboot_env_device + * @return 0 in case of success, else negative value + */ +int libuboot_configure(struct uboot_ctx *ctx, + struct uboot_env_device *envdevs, int copies); + + +/** @brief Flush environment to the storage + * + * Write the environment back to the storage and handle + * redundant devices. + * + * @param[in] ctx libuboot context + * @return 0 in case of success, else negative value + */ +int libuboot_env_store(struct uboot_ctx *ctx); + +/** @brief Initialize the library + * + * Initialize the library and get the context structure + * + * @param[out] *ctx struct uboot_ctx **out allocated structure + * @param[in] struct uboot_env_device *envdevs + * @return 0 in case of success, else negative value + */ +int libuboot_initialize(struct uboot_ctx **out, + struct uboot_env_device *envdevs, int copies); + +/** @brief Release all resources and exit the library + * + * @param[in] ctx libuboot context + */ +void libuboot_exit(struct uboot_ctx *ctx); + +/** @brief Load an environment + * + * @param[in] ctx libuboot context + * @return 0 in case of success + */ +int libuboot_open(struct uboot_ctx *ctx); + +/** @brief Release an environment + * + * Release allocated recource for the environment, but + * maintain the context. This allows to call + * libuboot_open() again. + * + * @param[in] ctx libuboot context + * @return 0 in case of success + */ +void libuboot_close(struct uboot_ctx *ctx); + +/** @brief Set a variable + * + * Set a variable. It creates a new variable if not present in + * the database, changes it or drops if value is NULL + * + * @param[in] ctx libuboot context + * @param[in] variable name + * @param[in] value. In case this is NULL, the variable is dropped + * @return 0 in case of success + */ +int libuboot_set_env(struct uboot_ctx *ctx, const char *varname, const char *value); + +/** @brief Get a variable + * + * Return value of a variable as string or NULL if + * variable is not present in the database. + * The returned string must be freed by the caller when not + * used anymore. + * + * @param[in] ctx libuboot context + * @param[in] variable name + * @return value in case of success, NULL in case of error + */ +char *libuboot_get_env(struct uboot_ctx *ctx, const char *varname); + +/** @brief Iterator + * + * Return a pointer to an entry in the database + * Used to iterate all variables in the database. + * + * @param[in] ctx libuboot context + * @param[in] next + * @return pointer to next entry or NULL + */ +void *libuboot_iterator(struct uboot_ctx *ctx, void *next); + +/** @brief Accessor to get variable name from DB entry + * + * @param[in] entry element in the database + * @return pointer to name or NULL + */ +const char *libuboot_getname(void *entry); + +/** @brief Accessor to get variable value from DB entry + * + * @param[in] entry element in the database + * @return pointer to name or NULL + */ +const char *libuboot_getvalue(void *entry); + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/ubootenv/uboot_env.c b/ubootenv/uboot_env.c new file mode 100644 index 0000000..c552359 --- /dev/null +++ b/ubootenv/uboot_env.c @@ -0,0 +1,699 @@ +/* vim: set ts=4 sw=4 et ci: */ +/** + * @file uboot_env.c + * + * @brief This is the implementation of libubootenv library + * + * Changes: + * + * 2021/12/28: port to P7 use spiFlash API by surenyi926 + * 2024/10/23: port to KM02 project + */ + +#define _GNU_SOURCE + +#include "uboot_private.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#define LIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = LIST_FIRST((head)); (var) != NULL && ((tvar) = LIST_NEXT((var), field), 1); (var) = (tvar)) + +static char attr_tostring(type_attribute a) +{ + switch (a) { + case TYPE_ATTR_STRING: + return 's'; + case TYPE_ATTR_DECIMAL: + return 'd'; + case TYPE_ATTR_HEX: + return 'h'; + case TYPE_ATTR_BOOL: + return 'b'; + case TYPE_ATTR_IP: + return 'i'; + case TYPE_ATTR_MAC: + return 'm'; + } + + return 's'; +} + +static char access_tostring(access_attribute a) +{ + switch (a) { + case ACCESS_ATTR_ANY: + return 'a'; + case ACCESS_ATTR_READ_ONLY: + return 'r'; + case ACCESS_ATTR_WRITE_ONCE: + return 'o'; + case ACCESS_ATTR_CHANGE_DEFAULT: + return 'c'; + } + + return 'a'; +} + +static struct var_entry *__libuboot_get_env(struct vars *envs, const char *varname) +{ + struct var_entry *entry; + + LIST_FOREACH(entry, envs, next) + { + if (strcmp(varname, entry->name) == 0) + return entry; + } + + return NULL; +} + +static void free_var_entry(struct vars *envs, struct var_entry *entry) +{ + if (entry) { + LIST_REMOVE(entry, next); + free(entry->name); + free(entry->value); + free(entry); + } +} + +static bool check_compatible_devices(struct uboot_ctx *ctx) +{ + if (!ctx->redundant) + return true; + + if (ctx->envdevs[0].flagstype != ctx->envdevs[1].flagstype) + return false; + if (ctx->envdevs[0].envsize != ctx->envdevs[1].envsize) + return false; + + return true; +} + +static int inline spiFlashRead(struct uboot_flash_env *dev, unsigned int offset, void *data, size_t len) +{ + if (dev->read) { + return dev->read(offset, data, len); + } + return -128; +} + +static int inline spiFlashWrite(struct uboot_flash_env *dev, unsigned int offset, void *data, size_t len) +{ + if (dev->write) { + return dev->write(offset, data, len); + } + return -128; +} + +static int inline spiFlashErase(struct uboot_flash_env *dev, unsigned int offset, size_t len) +{ + if (dev->erase) { + return dev->erase(offset, len); + } + return 0; +} + +static int devread(struct uboot_ctx *ctx, unsigned int copy, void *data) +{ + int ret; + struct uboot_flash_env *dev; + + if (copy > 1) + return -1; + + dev = &ctx->envdevs[copy]; + + /* read data from dev->offset with dev->envsize */ + ret = spiFlashRead(dev, dev->offset, data, dev->envsize); + if (ret) { + return -2; + } + return dev->envsize; +} + +static int devwrite(struct uboot_ctx *ctx, unsigned int copy, void *data) +{ + int ret = 0; + struct uboot_flash_env *dev; + + if (copy > 1) + return -1; + + dev = &ctx->envdevs[copy]; + + ret = spiFlashErase(dev, dev->offset, dev->envsize); + if (ret) { + printf("Erase environment area failed.\r\n"); + return ret; + } + + /* write dev->offset with dev->envsize to flash */ + ret = spiFlashWrite(dev, dev->offset, data, dev->envsize); + return ret; +} + +static int set_obsolete_flag(struct uboot_flash_env *dev) +{ +#if 0 + uint8_t offsetflags = offsetof(struct uboot_env_redund, flags); + unsigned char flag = 0; + + return spiFlashWrite(dev, dev->offset + offsetflags, &flag, sizeof flag); +#else + return 0; +#endif +} + +int libuboot_env_store(struct uboot_ctx *ctx) +{ + struct var_entry *entry; + void *image; + char *data; + char *buf; + bool saveflags = false; + size_t size; + uint8_t offsetdata; + int ret; + int copy; + + /* + * Allocate the bigger of the case + */ + image = malloc(sizeof(struct uboot_env_redund) + ctx->size); + if (!image) + return -1; + + if (ctx->redundant) + offsetdata = offsetof(struct uboot_env_redund, data); + else + offsetdata = offsetof(struct uboot_env_noredund, data); + + data = (char *)((uint8_t *)image + offsetdata); + + buf = data; + LIST_FOREACH(entry, &ctx->varlist, next) + { + size = (ctx->size - offsetdata) - (buf - data); + if ((strlen(entry->name) + strlen(entry->value) + 2) > size) + return -1; + + if (entry->type || entry->access) + saveflags = true; + + buf += snprintf(buf, size, "%s=%s", entry->name, entry->value); + buf++; + } + + /* + * Now save the .flags + */ + if (saveflags) { + bool first = true; + size = (ctx->size - offsetdata) - (buf - data); + buf += snprintf(buf, size, ".flags="); + + LIST_FOREACH(entry, &ctx->varlist, next) + { + size = (ctx->size - offsetdata) - (buf - data); + if (entry->type || entry->access) { + buf += snprintf(buf, size, "%s%s:%c%c", first ? "" : ",", entry->name, attr_tostring(entry->type), + access_tostring(entry->access)); + first = false; + } + } + buf++; + } + *buf++ = '\0'; + + if (ctx->redundant) { + unsigned char flags = ctx->envdevs[ctx->current].flags; + switch (ctx->envdevs[ctx->current].flagstype) { + case FLAGS_INCREMENTAL: + flags++; + break; + case FLAGS_BOOLEAN: + flags = 1; + break; + default: + break; + } + ((struct uboot_env_redund *)image)->flags = flags; + } + + *(uint32_t *)image = crc32(0, (uint8_t *)data, ctx->size - offsetdata); + + copy = ctx->redundant ? (ctx->current ? 0 : 1) : 0; + ret = devwrite(ctx, copy, image); + free(image); + + if (ret == ctx->size) + ret = 0; + + if (ctx->redundant && !ret) { + if (ctx->envdevs[ctx->current].flagstype == FLAGS_BOOLEAN) + ret = set_obsolete_flag(&ctx->envdevs[ctx->current]); + } + + if (!ret) + ctx->current = ctx->current ? 0 : 1; + + return ret; +} + +static int libuboot_load(struct uboot_ctx *ctx) +{ + int ret, i; + int copies = 1; + uint8_t *buf[2]; + size_t bufsize, usable_envsize; + struct uboot_flash_env *dev; + bool crcenv[2]; + // unsigned char flags[2]; + char *line, *next; + uint8_t offsetdata = offsetof(struct uboot_env_noredund, data); + uint8_t offsetcrc = offsetof(struct uboot_env_noredund, crc); + uint8_t offsetflags = offsetof(struct uboot_env_redund, flags); + uint8_t *data; + struct var_entry *entry; + + ctx->valid = false; + + bufsize = ctx->size; + if (ctx->redundant) { + copies++; + bufsize += ctx->size; + offsetdata = offsetof(struct uboot_env_redund, data); + offsetcrc = offsetof(struct uboot_env_redund, crc); + } + usable_envsize = ctx->size - offsetdata; + buf[0] = malloc(bufsize); + if (!buf[0]) + return -1; + + if (copies > 1) + buf[1] = buf[0] + ctx->envdevs[0].envsize; + + for (i = 0; i < copies; i++) { + data = (uint8_t *)(buf[i] + offsetdata); + uint32_t crc; + + dev = &ctx->envdevs[i]; + ret = devread(ctx, i, buf[i]); + if (ret != ctx->size) { + free(buf[0]); + return -2; + } + crc = *(uint32_t *)(buf[i] + offsetcrc); + dev->crc = crc32(0, data, usable_envsize); + crcenv[i] = dev->crc == crc; + if (ctx->redundant) + dev->flags = *(buf[i] + offsetflags); + } + if (!ctx->redundant) { + ctx->current = 0; + ctx->valid = crcenv[0]; + } else { + if (crcenv[0] && !crcenv[1]) { + ctx->valid = true; + ctx->current = 0; + } else if (!crcenv[0] && crcenv[1]) { + ctx->valid = true; + ctx->current = 1; + } else if (!crcenv[0] && !crcenv[1]) { + ctx->valid = false; + ctx->current = 0; + } else { /* both valid, check flags */ + ctx->valid = true; + if (ctx->envdevs[1].flags > ctx->envdevs[0].flags) + ctx->current = 1; + else + ctx->current = 0; + switch (ctx->envdevs[0].flagstype) { + case FLAGS_BOOLEAN: + if (ctx->envdevs[1].flags == 0xFF) + ctx->current = 1; + else if (ctx->envdevs[0].flags == 0xFF) + ctx->current = 0; + break; + case FLAGS_INCREMENTAL: + /* check overflow */ + if (ctx->envdevs[0].flags == 0xFF && ctx->envdevs[1].flags == 0) + ctx->current = 1; + else if (ctx->envdevs[1].flags == 0xFF && ctx->envdevs[0].flags == 0) + ctx->current = 0; + break; + default: + break; + } + } + } + +#if !defined(NDEBUG) + fprintf(stdout, "Environment %s, copy %d\r\n", ctx->valid ? "OK" : "WRONG", ctx->current); +#endif + + data = buf[ctx->current] + offsetdata; + + char *flagsvar = NULL; + + if (ctx->valid) { + for (line = (char *)data; *line; line = next + 1) { + char *value; + + /* + * Search the end of the string pointed by line + */ + for (next = line; *next; ++next) { + if ((next - (char *)data) > usable_envsize) { + free(buf[0]); + return -2; + } + } + + value = strchr(line, '='); + if (!value) + continue; + + *value++ = '\0'; + + if (!strcmp(line, ".flags")) + flagsvar = strdup(value); + else + libuboot_set_env(ctx, line, value); + } + } + + /* + * Parse .flags and set the attributes for a variable + */ + char *pvar; + char *pval; + if (flagsvar) { +#if !defined(NDEBUG) + fprintf(stdout, "Environment FLAGS %s\n", flagsvar); +#endif + pvar = flagsvar; + + while (*pvar && (pvar - flagsvar) < strlen(flagsvar)) { + char *pnext; + pval = strchr(pvar, ':'); + if (!pval) + break; + + *pval++ = '\0'; + pnext = strchr(pval, ','); + if (!pnext) + pnext = flagsvar + strlen(flagsvar); + else + *pnext++ = '\0'; + + entry = __libuboot_get_env(&ctx->varlist, pvar); + if (entry) { + int i; + for (i = 0; i < strlen(pval); i++) { + switch (pval[i]) { + case 's': + entry->type = TYPE_ATTR_STRING; + break; + case 'd': + entry->type = TYPE_ATTR_DECIMAL; + break; + case 'x': + entry->type = TYPE_ATTR_HEX; + break; + case 'b': + entry->type = TYPE_ATTR_BOOL; + break; + case 'i': + entry->type = TYPE_ATTR_IP; + break; + case 'm': + entry->type = TYPE_ATTR_MAC; + break; + case 'a': + entry->access = ACCESS_ATTR_ANY; + break; + case 'r': + entry->access = ACCESS_ATTR_READ_ONLY; + break; + case 'o': + entry->access = ACCESS_ATTR_WRITE_ONCE; + break; + case 'c': + entry->access = ACCESS_ATTR_CHANGE_DEFAULT; + break; + default: /* ignore it */ + break; + } + } + } + + pvar = pnext; + } + } + free(flagsvar); + + free(buf[0]); + + return ctx->valid ? 0 : -4; +} + +static bool libuboot_validate_flags(struct var_entry *entry, const char *value) +{ + bool ok_type = true, ok_access = true; + unsigned long long test; + + switch (entry->access) { + case ACCESS_ATTR_ANY: + ok_access = true; + break; + case ACCESS_ATTR_READ_ONLY: + case ACCESS_ATTR_WRITE_ONCE: + ok_access = false; + break; + case ACCESS_ATTR_CHANGE_DEFAULT: + break; + } + + if (!ok_access) + return false; + + if (!value) + return true; + + switch (entry->type) { + case TYPE_ATTR_STRING: + ok_type = true; + break; + case TYPE_ATTR_DECIMAL: + case TYPE_ATTR_HEX: + errno = 0; + ok_type = strlen(value) > 2 && (value[0] == 0) && (value[1] == 'x' || value[1] == 'X'); + if (ok_type) { + test = strtoul(value, NULL, 16); + if (errno) + ok_type = false; + } + break; + case TYPE_ATTR_BOOL: + ok_access = (value[0] == '1' || value[0] == 'y' || value[0] == 't' || value[0] == 'Y' || value[0] == 'T' || + value[0] == '0' || value[0] == 'n' || value[0] == 'f' || value[0] == 'N' || value[0] == 'F') && + (strlen(value) != 1); + break; + case TYPE_ATTR_IP: + case TYPE_ATTR_MAC: + break; + } + (void)test; /* suppressing warning */ + return ok_type; +} + +int libuboot_set_env(struct uboot_ctx *ctx, const char *varname, const char *value) +{ + struct var_entry *entry, *elm, *lastentry; + struct vars *envs = &ctx->varlist; + + /* U-Boot setenv treats '=' as an illegal character for variable names */ + if (strchr(varname, '=')) + return -1; + + entry = __libuboot_get_env(envs, varname); + if (entry) { + if (libuboot_validate_flags(entry, value)) { + if (!value) { + free_var_entry(envs, entry); + } else { + free(entry->value); + entry->value = strdup(value); + } + return 0; + } else { + return -3; + } + } + + if (!value) + return 0; + + entry = (struct var_entry *)calloc(1, sizeof(*entry)); + if (!entry) + return -1; + entry->name = strdup(varname); + if (!entry->name) { + free(entry); + return -1; + } + entry->value = strdup(value); + if (!entry->value) { + free(entry->name); + free(entry); + return -1; + } + lastentry = NULL; + LIST_FOREACH(elm, envs, next) + { + if (strcmp(elm->name, varname) > 0) { + LIST_INSERT_BEFORE(elm, entry, next); + return 0; + } + lastentry = elm; + } + if (lastentry) + LIST_INSERT_AFTER(lastentry, entry, next); + else + LIST_INSERT_HEAD(envs, entry, next); + + return 0; +} + +char *libuboot_get_env(struct uboot_ctx *ctx, const char *varname) +{ + struct var_entry *entry; + struct vars *envs = &ctx->varlist; + + entry = __libuboot_get_env(envs, varname); + if (!entry) + return NULL; + + return entry->value; + /* return strdup(entry->value); */ +} + +const char *libuboot_getname(void *entry) +{ + struct var_entry *e = entry; + + return e ? e->name : NULL; +} + +const char *libuboot_getvalue(void *entry) +{ + struct var_entry *e = entry; + + return e ? e->value : NULL; +} + +void *libuboot_iterator(struct uboot_ctx *ctx, void *next) +{ + if (!next) + return ctx->varlist.lh_first; + else + return ((struct var_entry *)next)->next.le_next; +} + +int libuboot_configure(struct uboot_ctx *ctx, struct uboot_env_device *envdevs, int copies) +{ + if (envdevs) { + struct uboot_flash_env *dev; + int i, n = copies; + dev = &ctx->envdevs[0]; + if (n > 2) + n = 2; + for (i = 0; i < n; i++, envdevs++, dev++) { + if (!envdevs) + break; + memset(dev->devname, 0, sizeof(dev->devname)); + strncpy(dev->devname, envdevs->devname, sizeof(dev->devname) - 1); + dev->offset = envdevs->offset; + dev->envsize = envdevs->envsize; + dev->sectorsize = envdevs->sectorsize; + dev->envsectors = envdevs->envsectors; + dev->read = envdevs->read; + dev->erase = envdevs->erase; + dev->write = envdevs->write; + + if (!ctx->size) + ctx->size = dev->envsize; + + if (i > 0) { + ctx->redundant = true; + if (!check_compatible_devices(ctx)) + return -2; + } + } + } + + return 0; +} + +int libuboot_initialize(struct uboot_ctx **out, struct uboot_env_device *envdevs, int copies) +{ + struct uboot_ctx *ctx; + int ret; + + *out = NULL; + ctx = calloc(1, sizeof(*ctx)); + if (!ctx) + return -1; + + ctx->valid = false; + ret = libuboot_configure(ctx, envdevs, copies); + + if (ret < 0) { + free(ctx); + return ret; + } + + *out = ctx; + return 0; +} + +int libuboot_open(struct uboot_ctx *ctx) +{ + if (!ctx) + return -1; + + return libuboot_load(ctx); +} + +void libuboot_close(struct uboot_ctx *ctx) +{ + struct var_entry *e, *tmp; + + if (!ctx) + return; + ctx->valid = false; + + LIST_FOREACH_SAFE(e, &ctx->varlist, next, tmp) + { + if (e->name) + free(e->name); + if (e->value) + free(e->value); + LIST_REMOVE(e, next); + free(e); + } +} + +void libuboot_exit(struct uboot_ctx *ctx) +{ + free(ctx); +} diff --git a/ubootenv/uboot_private.h b/ubootenv/uboot_private.h new file mode 100644 index 0000000..f4105b5 --- /dev/null +++ b/ubootenv/uboot_private.h @@ -0,0 +1,190 @@ +#ifndef __LIBUBOOT_PRIVATE_H__ +#define __LIBUBOOT_PRIVATE_H__ + +#if defined(__DCC__) +#define bool int +#define true 1 +#define false 0 +#else +#include +#endif +#include +#include +#include "libuboot.h" + +/* + * List definitions. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List functions. + */ +#define LIST_INIT(head) do { \ + (head)->lh_first = NULL; \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ + (listelm)->field.le_next->field.le_prev = \ + &(elm)->field.le_next; \ + (listelm)->field.le_next = (elm); \ + (elm)->field.le_prev = &(listelm)->field.le_next; \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + (elm)->field.le_next = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &(elm)->field.le_next; \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.le_next = (head)->lh_first) != NULL) \ + (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ + (head)->lh_first = (elm); \ + (elm)->field.le_prev = &(head)->lh_first; \ +} while (/*CONSTCOND*/0) + +#define LIST_REMOVE(elm, field) do { \ + if ((elm)->field.le_next != NULL) \ + (elm)->field.le_next->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = (elm)->field.le_next; \ +} while (/*CONSTCOND*/0) + +#define LIST_FOREACH(var, head, field) \ + for ((var) = ((head)->lh_first); \ + (var); \ + (var) = ((var)->field.le_next)) + +/* + * List access methods. + */ +#define LIST_EMPTY(head) ((head)->lh_first == NULL) +#define LIST_FIRST(head) ((head)->lh_first) +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +typedef enum { + TYPE_ATTR_STRING, /* default */ + TYPE_ATTR_DECIMAL, + TYPE_ATTR_HEX, + TYPE_ATTR_BOOL, + TYPE_ATTR_IP, + TYPE_ATTR_MAC +} type_attribute; + +typedef enum { + ACCESS_ATTR_ANY, /* default */ + ACCESS_ATTR_READ_ONLY, + ACCESS_ATTR_WRITE_ONCE, + ACCESS_ATTR_CHANGE_DEFAULT, +} access_attribute; + +enum flags_type { + FLAGS_NONE, + FLAGS_BOOLEAN, + FLAGS_INCREMENTAL +}; + +/** + * U-Boot environment should always be redundant, but + * for compatibility reasons a single copy must + * be also supported. Structure is different because + * there is no flags in the single copy + */ +struct uboot_env_noredund { + /** computed crc32 value */ + uint32_t crc; + /** placeholder to point to the env in flash */ + char data[]; +}; + +struct uboot_env_redund { + /** computed crc32 value */ + uint32_t crc; + /** flags, see flags_type */ + unsigned char flags; + /** placeholder to point to the env in flash */ + char data[]; +}; + +struct uboot_flash_env { + /** path to device or file where env is stored */ + char devname[DEVNAME_MAX_LENGTH]; + /** Start offset inside device path */ + long long int offset; + /** size of environment */ + size_t envsize; + /** Size of sector (for MTD) */ + size_t sectorsize; + /** Number of sectors for each environment */ + long unsigned int envsectors; + /** MTD structure as returned by ioctl() call */ + //struct mtd_info_user mtdinfo; + /** Computed CRC on the stored environment */ + uint32_t crc; + /** file descriptor used to access the device */ + int fd; + /** flags (see flags_type) are one byte in the stored environment */ + unsigned char flags; + /** flags according to device type */ + enum flags_type flagstype; + + /* operators */ + int (*read)(unsigned int offset, void *buf, size_t len); + int (*erase)(unsigned int offset, size_t len); + int (*write)(unsigned int offset, const void *buf, size_t len); +}; + +/** Internal structure for an environment variable + */ +struct var_entry { + /** Variable's name */ + char *name; + /** Variable's value */ + char *value; + /** Type of the variable, see access_attribute */ + type_attribute type; + /** Permissions for the variable */ + access_attribute access; + /** Pointer to next element in the list */ + LIST_ENTRY(var_entry) next; +}; + +LIST_HEAD(vars, var_entry); + +/** libubootenv context + */ +struct uboot_ctx { + /** true if the environment is redundant */ + bool redundant; + /** set to valid after a successful load */ + bool valid; + /** size of the environment */ + size_t size; + /** devices where environment is stored */ + struct uboot_flash_env envdevs[2]; + + /** Set which device contains the current(last valid) environment */ + int current; + /** semaphore on the environment */ + int lock; + /** pointer to the internal db */ + struct vars varlist; +}; + +uint32_t crc32 (uint32_t crc, const uint8_t *p, size_t len); +#endif diff --git a/vxbLfsLib.c b/vxbLfsLib.c index 60bf039..493b49e 100644 --- a/vxbLfsLib.c +++ b/vxbLfsLib.c @@ -26,6 +26,8 @@ #include "lfs/lfs.c" #include "lfs/lfs_util.c" +#define LFS_NAME "littlefs" + typedef struct __lfs_drv_ctrl lfsDrvCtrl; typedef struct lfs_ios_drv lfsIosDrv; @@ -549,7 +551,7 @@ LOCAL struct vxbSpiRegister lfsDevDriver = { VXB_DEVID_DEVICE, /* devID */ VXB_BUSID_PLB, /* busID */ VXBUS_VERSION_5, /* vxbVersion */ - "lfs", /* drvName */ + LFS_NAME, /* drvName */ &lfsDevFuncs, /* pDrvBusFuncs */ lfsDevMethods, /* pMethods */ NULL, /* devProbe */ @@ -568,7 +570,7 @@ LOCAL lfsDrvCtrl * getLfsLowHandle(int unit) VXB_DEVICE_ID pDev; lfsDrvCtrl *pDrvCtrl = NULL; - pDev = vxbInstByNameFind("lfs", unit); + pDev = vxbInstByNameFind(LFS_NAME, unit); if (pDev == NULL) { printf("Can't find lfs%d\n", unit); return NULL;