sdk-hwV1.3/lichee/brandy-2.0/spl/common/env.c

599 lines
12 KiB
C
Raw Normal View History

2024-05-07 10:09:20 +00:00
#include <common.h>
#include <arch/spinor.h>
#include <u-boot/zlib.h>
#include <part_efi.h>
#if ENV_DEBUG
#define env_dbg(fmt, arg...) printf(fmt, ##arg)
#else
#define env_dbg(fmt, arg...)
#endif
#define ENV_NAME "env"
#define ENV_NAME_2 "env-redund"
#define O_RDONLY (00000000)
#define O_WRONLY (00000001)
#define O_RDWR (00000002)
static uint env_start_sector;
static uint env_start_sector_2;
static uint env_sectors;
static uint env_sectors_2;
static uint cur_envsize;
#ifdef CFG_SUNXI_HAVE_REDUNDENV
static unsigned char active_flag = 1;
/* obsolete_flag must be 0 to efficiently set it on NOR flash without erasing */
static unsigned char obsolete_flag;
#endif
static unsigned long usable_envsize;
#define ENV_SIZE usable_envsize
static int dev_current;
static int s_is_env_opened;
struct env_image_single {
uint32_t crc; /* CRC32 over data bytes */
char data[];
};
struct env_image_redundant {
uint32_t crc; /* CRC32 over data bytes */
unsigned char flags; /* active or obsolete */
char data[];
};
enum flag_scheme {
FLAG_NONE,
FLAG_BOOLEAN,
FLAG_INCREMENTAL,
};
struct environment {
void *image;
uint32_t *crc;
unsigned char *flags;
char *data;
enum flag_scheme flag_scheme;
};
static struct environment environment = {
.flag_scheme = FLAG_NONE,
};
static int flash_io(int mode);
/*
* s1 is either a simple 'name', or a 'name=value' pair.
* s2 is a 'name=value' pair.
* If the names match, return the value of s2, else NULL.
*/
static char *envmatch(char *s1, char *s2)
{
if (s1 == NULL || s2 == NULL)
return NULL;
while (*s1 == *s2++)
if (*s1++ == '=')
return s2;
if (*s1 == '\0' && *(s2 - 1) == '=')
return s2;
return NULL;
}
/**
* Search the environment for a variable.
* Return the value, if found, or NULL, if not found.
*/
char *fw_getenv(char *name)
{
char *env, *nxt;
for (nxt = environment.data; !(*nxt); ++nxt) {
if (nxt >= &environment.data[ENV_SIZE]) {
env_dbg("## Error: environment not terminated\n");
return NULL;
}
}
for (env = nxt; *env; env = nxt + 1) {
char *val;
for (nxt = env; *nxt; ++nxt) {
if (nxt >= &environment.data[ENV_SIZE]) {
env_dbg("## Error: environment not terminated\n");
return NULL;
}
}
val = envmatch(name, env);
if (!val)
continue;
return val;
}
return NULL;
}
#if ENV_DEBUG
void fw_env_dump(void)
{
char *env, *nxt;
for (nxt = environment.data; !(*nxt); ++nxt) {
if (nxt >= &environment.data[ENV_SIZE])
env_dbg("## Error: environment not terminated\n");
}
for (env = nxt; *env; env = nxt + 1) {
for (nxt = env; *nxt; ++nxt) {
if (nxt >= &environment.data[ENV_SIZE])
env_dbg("## Error: environment not terminated\n");
}
env_dbg("env: %s\n", env);
}
}
#endif
#if 0
/*
* Set/Clear a single variable in the environment.
* This is called in sequence to update the environment
* in RAM without updating the copy in flash after each set
*/
int fw_env_write(char *name, char *value)
{
int len;
char *env, *nxt;
char *oldval = NULL;
int deleting, creating, overwriting;
/*
* search if variable with this name already exists
*/
for (nxt = env = environment.data; *env; env = nxt + 1) {
for (nxt = env; *nxt; ++nxt) {
if (nxt >= &environment.data[ENV_SIZE]) {
env_dbg("## Error: "
"environment not terminated\n");
/* errno = EINVAL; */
return -1;
}
}
oldval = envmatch(name, env);
if (oldval)
break;
}
deleting = (oldval && !(value && strlen(value)));
creating = (!oldval && (value && strlen(value)));
overwriting = (oldval && (value && strlen(value)));
if (deleting) {
env_dbg("env: delting\n");
} else if (overwriting) {
env_dbg("env: overwriting\n");
} else if (creating) {
env_dbg("env: creating\n");
} else {
env_dbg("env: nothing\n");
return 0;
}
if (deleting || overwriting) {
if (*++nxt == '\0') {
*env = '\0';
} else {
for (;;) {
*env = *nxt++;
if ((*env == '\0') && (*nxt == '\0'))
break;
++env;
}
}
*++env = '\0';
}
/* Delete only ? */
if (!value || !strlen(value))
return 0;
/*
* Append new definition at the end
*/
for (env = environment.data; *env || *(env + 1); ++env)
;
if (env > environment.data)
++env;
/*
* Overflow when:
* "name" + "=" + "val" +"\0\0" > cur_envsize - (env-environment)
*/
len = strlen(name) + 2;
/* add '=' for first arg, ' ' for all others */
len += strlen(value) + 1;
if (len > (&environment.data[ENV_SIZE] - env)) {
env_dbg("Error: environment overflow, \"%s\" deleted\n", name);
return -1;
}
while ((*env = *name++) != '\0')
env++;
*env = '=';
while ((*++env = *value++) != '\0')
;
/* end is marked with double '\0' */
*++env = '\0';
return 0;
}
int fw_env_flush(void)
{
/*
* Update CRC
*/
#ifdef CFG_USE_DCACHE
dcache_enable();
#endif
*environment.crc = crc32(0, (uint8_t *) environment.data, ENV_SIZE);
#ifdef CFG_USE_DCACHE
dcache_disable();
#endif
/* write environment back to flash */
if (flash_io(O_RDWR)) {
env_dbg("Error: can't write fw_env to flash\n");
return -1;
}
return 0;
}
#endif
static int flash_env_init(void)
{
#ifdef CFG_SUNXI_HAVE_REDUNDENV
get_part_info_by_name(ENV_NAME, &env_start_sector, &env_sectors);
get_part_info_by_name(ENV_NAME_2, &env_start_sector_2, &env_sectors_2);
env_dbg("env_start_sector_2=%d %d\n", env_start_sector, env_start_sector_2);
env_dbg("env_sectors_2=%d %d\n", env_sectors, env_sectors_2);
#else
get_part_info_by_name(ENV_NAME, &env_start_sector, &env_sectors);
env_dbg("env_start_sector=%d\n", env_start_sector);
env_dbg("env_sectors=%d\n", env_sectors);
#endif
if (env_start_sector == 0 && env_start_sector_2 == 0) {
env_dbg("%s and %s part info all can't find\n",
ENV_NAME, ENV_NAME_2);
return -1;
}
if (env_sectors != 0 || env_sectors == env_sectors_2) {
#ifdef CFG_SUNXI_ENV_SIZE
cur_envsize = CFG_SUNXI_ENV_SIZE;
#else
/* for env 4k is enough, but partition size may not only 4k (maybe align to 32k or 64k) */
cur_envsize = 4 * 1024;
#endif
#ifdef CFG_SUNXI_HAVE_REDUNDENV
} else {
env_dbg("%s and %s part size diff\n", ENV_NAME, ENV_NAME_2);
return -1;
#endif
}
return 0;
}
static int flash_env_read(void *buf, size_t size)
{
int start_sector;
if (dev_current == 0) {
start_sector = env_start_sector;
} else if (dev_current == 1) {
start_sector = env_start_sector_2;
} else {
return -1;
}
if (spl_flash_init()) {
env_dbg("flash init fail\n");
return -1;
}
if (spl_flash_read(start_sector, size/512, buf)) {
env_dbg("read flash fail\n");
return -1;
}
// env_dbg("buf%x,%x,%x,%x\n",*(char*)buf,*(char*)(buf+1),*(char*)(buf+2),*(char*)(buf+3));
return 0;
}
/*
* Set obsolete flag at offset - NOR flash only
*/
#if 0
static int flash_flag_obsolete(off_t offset)
{
return -1;
}
static int flash_env_write(void *buf, size_t size)
{
int next_env_sector;
int current_env_sector;
if (dev_current == 0) {
current_env_sector = env_start_sector;
next_env_sector = env_start_sector_2;
} else if (dev_current == 1) {
current_env_sector = env_start_sector_2;
next_env_sector = env_start_sector;
} else {
return -1;
}
if (spl_flash_init()) {
env_dbg("flash init fail\n");
return -1;
}
switch (environment.flag_scheme) {
case FLAG_NONE:
break;
case FLAG_INCREMENTAL:
(*environment.flags)++;
break;
case FLAG_BOOLEAN:
*environment.flags = active_flag;
break;
default:
env_dbg("Unimplemented flash scheme %d\n",
environment.flag_scheme);
return -1;
}
if (spl_flash_erase(next_env_sector, size/512)) {
env_dbg("erase flash fail\n");
return -1;
}
if (spl_flash_write(next_env_sector, size/512, buf)) {
env_dbg("write flash fail\n");
return -1;
}
if (environment.flag_scheme == FLAG_BOOLEAN) {
/* Have to set obsolete flag */
int offset = current_env_sector * 512 +
offsetof(struct env_image_redundant, flags);
flash_flag_obsolete(offset);
}
return 0;
}
#endif
static int flash_io(int mode)
{
void *buf;
size_t size;
int rc;
if (mode == O_RDONLY) {
buf = environment.image;
size = cur_envsize;
rc = flash_env_read(buf, size);
} else {
env_dbg("%s %d %s boot0 not support write env\n", __FILE__, __LINE__, __func__);
return -1;
}
return rc;
}
/*
* Prevent confusion if running from erased flash memory
*/
int fw_env_open(void)
{
int crc0, crc0_ok;
void *addr0 = NULL;
#ifdef CFG_SUNXI_HAVE_REDUNDENV
int crc1, crc1_ok;
unsigned char flag0;
unsigned char flag1;
void *addr1 = NULL;
#endif
int ret;
if (s_is_env_opened) {
env_dbg("env has been opened: %d\n", s_is_env_opened);
return 0;
}
if (flash_env_init()) {
env_dbg("env init fail\n");
return -1;
}
env_dbg("cur_envsize 0x%x\n", cur_envsize);
addr0 = malloc(cur_envsize + 1024); // more memory for fixed emmc boot memory error
if (addr0 == NULL) {
env_dbg("Not enough memory for environment (%ld bytes)\n",
cur_envsize);
ret = -1;
goto open_cleanup;
}
memset(addr0, 0x00, cur_envsize);
/* read environment from FLASH to local buffer */
environment.image = addr0;
#ifdef CFG_SUNXI_HAVE_REDUNDENV
struct env_image_redundant *redundant;
redundant = addr0;
environment.crc = &redundant->crc;
environment.flags = &redundant->flags;
environment.data = redundant->data;
#else
struct env_image_single *single;
single = addr0;
environment.crc = &single->crc;
environment.flags = NULL;
environment.data = single->data;
#endif
usable_envsize = cur_envsize - sizeof(uint32_t);
#ifdef CFG_SUNXI_HAVE_REDUNDENV
usable_envsize -= sizeof(char);
#endif
dev_current = 0;
if (flash_io(O_RDONLY)) {
ret = -1;
goto open_cleanup;
}
#ifdef AS
dcache_enable();
#endif
crc0 = crc32(0, (uint8_t *)environment.data, ENV_SIZE);
#ifdef AS
dcache_disable();
#endif
crc0_ok = (crc0 == *environment.crc);
#ifdef CFG_SUNXI_HAVE_REDUNDENV
flag0 = *environment.flags;
dev_current = 1;
env_dbg("cur_envsize 0x%x\n", cur_envsize);
addr1 = malloc(cur_envsize + 1024);// more memory for fixed emmc boot memory error
//env_dbg("env addr1:0x%x\n", addr1);
if (addr1 == NULL) {
env_dbg("Not enough memory for environment (%ld bytes)\n",
cur_envsize);
ret = -1;
goto open_cleanup;
}
memset(addr1, 0x00, cur_envsize);
redundant = addr1;
/*
* have to set environment.image for flash_read(), careful -
* other pointers in environment still point inside addr0
*/
environment.image = addr1;
if (flash_io(O_RDONLY)) {
ret = -1;
goto open_cleanup;
}
/* Check flag scheme compatibility */
/* Hardcode */
environment.flag_scheme = FLAG_INCREMENTAL;
#ifdef As
dcache_enable();
#endif
crc1 = crc32(0, (uint8_t *)redundant->data, ENV_SIZE);
#ifdef As
dcache_disable();
#endif
crc1_ok = (crc1 == redundant->crc);
flag1 = redundant->flags;
env_dbg("crc0_ok:%d, crc1_ok:%d flag0:%d flag1:%d\n", crc0_ok, crc1_ok, flag0, flag1);
if (crc0_ok && !crc1_ok) {
dev_current = 0;
} else if (!crc0_ok && crc1_ok) {
dev_current = 1;
} else if (!crc0_ok && !crc1_ok) {
env_dbg("Warning: Bad CRC\n");
return -1;
} else {
switch (environment.flag_scheme) {
case FLAG_BOOLEAN:
if (flag0 == active_flag &&
flag1 == obsolete_flag) {
dev_current = 0;
} else if (flag0 == obsolete_flag &&
flag1 == active_flag) {
dev_current = 1;
} else if (flag0 == flag1) {
dev_current = 0;
} else if (flag0 == 0xFF) {
dev_current = 0;
} else if (flag1 == 0xFF) {
dev_current = 1;
} else {
dev_current = 0;
}
break;
case FLAG_INCREMENTAL:
if (flag0 == 255 && flag1 == 0)
dev_current = 1;
else if ((flag1 == 255 && flag0 == 0) ||
flag0 >= flag1)
dev_current = 0;
else /* flag1 > flag0 */
dev_current = 1;
break;
default:
env_dbg("Unknown flag scheme %d\n",
environment.flag_scheme);
return -1;
}
}
/*
* If we are reading, we don't need the flag and the CRC any
* more, if we are writing, we will re-calculate CRC and update
* flags before writing out
*/
if (dev_current) {
environment.image = addr1;
environment.crc = &redundant->crc;
environment.flags = &redundant->flags;
environment.data = redundant->data;
free(addr0);
} else {
environment.image = addr0;
/* Other pointers are already set */
free(addr1);
}
env_dbg("dev_current:%d\n", dev_current);
#else
if (!crc0_ok) {
env_dbg("Warning: Bad CRC\n");
return -1;
}
#endif
s_is_env_opened = 1;
return 0;
open_cleanup:
if (addr0)
free(addr0);
#ifdef CFG_SUNXI_HAVE_REDUNDENV
if (addr1)
free(addr1);
#endif
return ret;
}
/*
* Simply free allocated buffer with environment
*/
int fw_env_close(void)
{
if (environment.image)
free(environment.image);
environment.image = NULL;
s_is_env_opened = 0;
return 0;
}