surenyi
2 weeks ago
12 changed files with 1342 additions and 20 deletions
@ -0,0 +1,161 @@ |
|||
#include <vxWorks.h> |
|||
#include <vsbConfig.h> |
|||
#include <semLib.h> |
|||
#include <stdlib.h> |
|||
#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; |
|||
} |
@ -0,0 +1,113 @@ |
|||
#include <stdint.h> |
|||
#include <stddef.h> |
|||
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; |
|||
} |
|||
|
@ -0,0 +1,145 @@ |
|||
#ifndef __LIBUBOOT_H___ |
|||
#define __LIBUBOOT_H___ |
|||
|
|||
#ifdef __cplusplus |
|||
extern "C" { |
|||
#endif |
|||
|
|||
#include <stddef.h> |
|||
|
|||
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 |
|||
|
@ -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 <ctype.h> |
|||
#include <errno.h> |
|||
#include <limits.h> |
|||
#include <stddef.h> |
|||
#include <stdint.h> |
|||
#include <stdio.h> |
|||
#include <stdlib.h> |
|||
#include <string.h> |
|||
|
|||
#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); |
|||
} |
@ -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 <stdbool.h> |
|||
#endif |
|||
#include <stdint.h> |
|||
#include <stddef.h> |
|||
#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 |
Loading…
Reference in new issue