blob: 4eacbb5961e2f16433b901df49a841b4ff399457 [file] [log] [blame]
/* Copyright 2015 Google Inc. All Rights Reserved.
*
* Note: flash_erase usage in here sends start and end as the same address.
* This is because the size of each sysvar env is a single erase-block (64k).
* The flash_erase function includes the addresses under the "last" param as
* part of the erase section, meaning inclusion of 0x8 as an address will erase
* 0x8 up to 0x9 (64k). 0x9 cannot be included as the end address because it is
* not in a sector boundary.
*/
#include <sysvar.h>
#include <asm/io.h>
#include <common.h>
#include <config.h>
#include <command.h>
#include <flash.h>
struct sysvar_uboot {
const char *sysvar_name;
const char *uboot_name;
};
struct sysvar_uboot_map {
struct sysvar_uboot *map;
};
static const struct sysvar_uboot su_mappings[] = {
{
.sysvar_name = ACTIVATED_KERNEL_NAME_SV,
.uboot_name = ACTIVATED_KERNEL_NAME_UB,
},
{
.sysvar_name = MAC_ADDR1_SV,
.uboot_name = MAC_ADDR1_UB,
},
};
static const struct sysvar_uboot_map su_map = {
.map = su_mappings,
};
// Some bit twiddling for erasing the correct sector on Atheros flash. This
// only erases multiples of 64kb.
#define __SYSVAR_ERASE_SECTOR(x) (((x) >> 16) & 0xff)
// Board-based implementation supplies pointer to flash info.
extern flash_info_t flash_info[];
struct sysvar_buf ro_buf;
struct sysvar_buf rw_buf;
const long sysvar_offset[SYSVAR_SPI_BLOCK] = {
SYSVAR_RW_OFFSET0, SYSVAR_RW_OFFSET1, SYSVAR_RO_OFFSET0, SYSVAR_RO_OFFSET1
};
/*
* print_msg - print the message string
*/
static void print_msg(char *msg, int idx) {
if (idx < 0) {
printf("SV: %s\n", msg);
} else {
printf("SV: System variables(%s) has been %s\n",
(idx < SYSVAR_RO_BUF) ? "RW" : "RO", msg);
}
}
/*
* print_err - print the error message
*/
static void print_err(char *err, int idx) {
if (idx < 0) {
printf("## Error: %s\n", err);
} else {
printf("## Error: failed to %s system variables(%s)\n",
err, (idx < SYSVAR_RO_BUF) ? "RW" : "RO");
}
}
/*
* data_recovery - system variables recovering routine
*/
int data_recovery(struct sysvar_buf *buf, int idx) {
int i, j, ret, sector;
/* load the system variables */
printf("SV: Recovering data");
for (i = idx, j = idx + 1; i < idx + 2; i++, j--) {
/* read the data from SPI flash */
if (read_buff(flash_info, buf->data, sysvar_offset[i], buf->data_len))
continue;
/* check crc32 and wc32 (write count) */
if (check_var(buf, SYSVAR_LOAD_MODE) == SYSVAR_SUCCESS) {
/* erase SPI flash */
sector = __SYSVAR_ERASE_SECTOR(sysvar_offset[j]);
ret = flash_erase(flash_info, sector, sector);
if (ret) {
print_err("erase", j);
goto recovery_err;
}
/* check crc32 and wc32 (write count) */
if (check_var(buf, SYSVAR_SAVE_MODE))
goto recovery_err;
/* write system variables(RW) to SPI flash */
ret = write_buff(flash_info, buf->data, sysvar_offset[j] + CFG_FLASH_BASE,
buf->data_len);
printf("\n");
if (ret) {
print_err("write", j);
goto recovery_err;
}
buf->loaded = true;
print_msg("Data recovery was completed", SYSVAR_MESSAGE);
return 0;
}
}
recovery_err:
clear_buf(buf);
printf("\n");
return 0;
}
/*
* data_load - load the data from SPI flash to data buffer
*/
int data_load(struct sysvar_buf *buf, int idx) {
int i, j;
/* load the system variables */
for (i = idx, j = 0; i < idx + 2; i++, j++) {
buf->failed[j] = false;
/* read the data from SPI flash */
if (read_buff(flash_info, buf->data, sysvar_offset[i], buf->data_len)) {
buf->failed[j] = true;
}
/* check crc32 and wc32 (write count) */
if (check_var(buf, SYSVAR_LOAD_MODE))
buf->failed[j] = true;
}
load_err:
if (buf->failed[0] || buf->failed[1])
return data_recovery(buf, idx);
return 0;
}
/*
* data_save - save the data from data buffer to SPI flash
*/
int data_save(struct sysvar_buf *buf, int *idx) {
int i, j, ret, sector;
/* save the system variables */
for (j = 0; j < 2; j++) {
i = idx[j];
printf("SV: Erasing SPI flash 0x%08lx - 0x%08lx\n",
sysvar_offset[i], sysvar_offset[i] + buf->data_len);
sector = __SYSVAR_ERASE_SECTOR(sysvar_offset[i]);
ret = flash_erase(flash_info, sector, sector);
if (ret) {
print_err("erase", i);
return 1;
}
/* check crc32 and wc32 (write count) */
if (check_var(buf, SYSVAR_SAVE_MODE)) {
print_err("save", SYSVAR_RO_BUF);
return 1;
}
/* write system variables to SPI flash */
printf("SV: Writing to SPI flash");
ret = write_buff(flash_info, buf->data, sysvar_offset[i] + CFG_FLASH_BASE,
buf->data_len);
printf("\n");
if (ret) {
print_err("write", i);
return 1;
}
}
return 0;
}
/*
* sf_loadvar - load the data from SPI flash to data buffer
*/
int sf_loadvar(void) {
if (data_load(&rw_buf, SYSVAR_RW_BUF)) {
return 1;
}
/* move the data from data buffer to data list */
if (load_var(&rw_buf)) {
return 1;
}
rw_buf.loaded = true;
print_msg("loaded", SYSVAR_RW_BUF);
if (data_load(&ro_buf, SYSVAR_RO_BUF))
return 1;
/* move the data from data buffer to data list */
if (load_var(&ro_buf))
return 1;
ro_buf.loaded = true;
print_msg("loaded", SYSVAR_RO_BUF);
return 0;
}
/*
* sf_savevar - save the data from data buffer to SPI flash
*/
int sf_savevar(struct sysvar_buf *buf, int idx) {
int save_idx[2];
/* move the data from data list to data buffer */
if (save_var(buf))
return 1;
/* erase failed partition first
* part0 part1 erase
* ----- ----- -----
* ok ok 0, 1
* failed ok 0, 1
* ok failed 1, 0
* failed failed 0, 1
*/
if (buf->failed[1]) {
save_idx[0] = idx + 1;
save_idx[1] = idx;
} else {
save_idx[0] = idx;
save_idx[1] = idx + 1;
}
/* save the data from data buffer to SPI flash */
if (data_save(buf, save_idx))
return 1;
printf("\n");
print_msg("saved", idx);
return 0;
}
/*
* sf_getvar - get or print the system variable from data list
*/
int sf_getvar(char *name, char *value, int len) {
struct sysvar_list *var = NULL;
if (name == NULL) {
/* print all system variables(RO) */
print_var(&ro_buf);
/* print all system variables(RW) */
print_var(&rw_buf);
return 0;
}
/* find the system variable(RO) */
var = find_var(&ro_buf, name);
if (var != NULL)
goto get_data;
/* find the system variable(RW) */
var = find_var(&rw_buf, name);
if (var != NULL)
goto get_data;
/* system variable not found */
printf("## SYSVAR: '%s' not found\n", name);
return 1;
get_data:
return get_var(var, name, value, len);
}
/*
* sf_setvar - add or delete the system variable in data list
*/
int sf_setvar(struct sysvar_buf *buf, int idx, char *name, char *value) {
struct sysvar_list *var = NULL;
int ret = 0;
if (name != NULL) {
if (idx < SYSVAR_RO_BUF) {
/* read only variable? */
var = find_var(&ro_buf, name);
} else {
/* variable existed? */
var = find_var(&rw_buf, name);
}
if (var != NULL) {
if (idx < SYSVAR_RO_BUF) {
printf("## Error: '%s' is read only variable\n", name);
return 1;
} else {
printf("## Error: '%s' is not read only variable\n", name);
return 1;
}
}
var = find_var(buf, name);
if (var != NULL) {
/* delete system variable */
ret = delete_var(buf, var);
if (ret != SYSVAR_SUCCESS) {
printf("## Error: failed to delete '%s'\n", name);
return 1;
}
/* add system variable */
if (value != NULL) {
ret = set_var(buf, name, value);
if (ret == 0)
print_msg("added", idx);
} else {
print_msg("deleted", idx);
}
} else {
/* add system variable */
if (value != NULL) {
ret = set_var(buf, name, value);
if (ret == 0)
print_msg("added", idx);
} else {
printf("## Error: '%s' not found\n", name);
ret = 1;
}
}
} else {
/* delete all of system variables */
ret = clear_var(buf);
if (ret == 0)
print_msg("deleted", idx);
}
return ret;
}
/*
* do_flloadvar - load system variables from SPI flash
*
* sf_loadvar command:
* sf_loadvar - load system variables from persistent storage
*/
int do_flloadvar(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) {
/* load system variables from SPI flash */
return sf_loadvar();
}
U_BOOT_CMD(
loadvar, 1, 0, do_flloadvar,
"loadvar - load system variables\n",
" - load system variables from SPI flash\n"
);
/*
* do_flsavevar - save system variables(RW) to SPI flash
*
* sf_savevar command:
* sf_savevar - save system variables(RW) to SPI flash
*/
int do_flsavevar(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
/* save system variables(RW) */
return sf_savevar(&rw_buf, SYSVAR_RW_BUF);
}
U_BOOT_CMD(
savevar, 1, 0, do_flsavevar,
"savevar - save system variables(RW)\n",
" - save system variables(RW) to SPI flash\n"
);
/*
* do_flprintvar - print system variables
*
* printvar command:
* printvar name - print system variable with name
* printvar - print all system variables
*/
int do_flprintvar(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) {
char value[SYSVAR_VALUE];
if (argv[1] == NULL) {
/* print all system variables */
sf_getvar(NULL, NULL, 0);
printf("\nSV: System Variables(RO): %d/%d bytes\n",
ro_buf.used_len, ro_buf.total_len);
printf("SV: System Variables(RW): %d/%d bytes\n",
rw_buf.used_len, rw_buf.total_len);
} else {
/* get a system variable */
if (sf_getvar(argv[1], value, SYSVAR_VALUE) == 0) {
puts(argv[1]);
putc('=');
/* puts value in case CONFIG_SYS_PBSIZE < SYSVAR_VALUE */
puts(value);
putc('\n');
printf("\nSV: System Variable: %d bytes\n",
(int)(SYSVAR_NAME + 2 + strlen(value)));
}
}
return 0;
}
U_BOOT_CMD(
printvar, 2, 0, do_flprintvar,
"printvar - print system variables\n",
" - print values of all system variables\n"
"printvar name ...\n"
" - print value of system variable 'name'\n"
);
int set_sysvar_uboot(const char *var, const char *name) {
const char *uboot_name = get_uboot_name(var);
if (uboot_name) {
setenv(uboot_name, name);
}
}
/*
* do_flsetvar - add or delete system variables(RW)
*
* sf_setvar command:
* sf_setvar name value - add system variable with name:value
* sf_setvar name - delete system variable with name
* sf_setvar - delete all system variables
*/
int do_flsetvar(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) {
int ret = 0;
if (argc == 3) {
/* add a system variable(RW) */
ret = sf_setvar(&rw_buf, SYSVAR_RW_BUF, argv[1], argv[2]);
if (ret == SYSVAR_SUCCESS) {
set_sysvar_uboot(argv[1], argv[2]);
}
} else if (argc == 2) {
/* delete a system variable(RW) */
ret = sf_setvar(&rw_buf, SYSVAR_RW_BUF, argv[1], NULL);
if (ret == SYSVAR_SUCCESS) {
set_sysvar_uboot(argv[1], NULL);
}
} else {
/* delete all system variables(RW) */
ret = sf_setvar(&rw_buf, SYSVAR_RW_BUF, NULL, NULL);
delete_uboot_vars();
}
return ret;
}
U_BOOT_CMD(
setvar, 3, 0, do_flsetvar,
"setvar - set system variables(RW)\n",
"setvar name value ...\n"
" - set system variable(RW) 'name' to 'value ...'\n"
"setvar name\n"
" - delete system variable(RW) 'name'\n"
"setvar\n"
" - delete all system variables(RW)\n"
);
/*
* sysvar_dump - dump the data buffer in binary/ascii format
*/
void sysvar_dump(struct sysvar_buf *buf, int idx, bool load) {
extern char console_buffer[];
int start = 0;
printf("System Variables(%s):\n", (idx < SYSVAR_RO_BUF) ? "RW" : "RO");
printf("offset : 0x%08lx\n", sysvar_offset[idx]);
printf("size : %d bytes\n", buf->data_len);
printf("total : %d bytes\n", buf->total_len);
printf("used : %d bytes\n", buf->used_len);
printf("wc32 : 0x%08lx\n", get_wc32(buf));
printf("crc32 : 0x%08lx\n", get_crc32(buf));
while (1) {
/* dump one page data in data buffer */
dump_buf(buf, start, PAGE_SIZE);
/* continue to dump...? */
readline("(n)ext, (p)rev, (f)irst, (l)ast ? >> ");
if (strcmp(console_buffer, "n") == 0) {
start += PAGE_SIZE; /* go to next page */
if (start >= buf->data_len)
return;
} else if (strcmp(console_buffer, "p") == 0) {
start -= PAGE_SIZE; /* go to previous page */
if (start < 0)
return;
} else if (strcmp(console_buffer, "f") == 0) {
if (start == 0)
return;
start = 0; /* go to first page */
} else if (strcmp(console_buffer, "l") == 0) {
if (start == buf->data_len - PAGE_SIZE)
return;
start = buf->data_len - PAGE_SIZE; /* go to last page */
} else {
return;
}
}
}
/*
* sysvar_io - SPI flash IO operations
*/
int sysvar_io(int argc, char *argv[]) {
struct sysvar_buf *buf;
int i, idx, sector, ret = 0;
if (strcmp(argv[1], "0") == 0) {
idx = 0;
buf = &rw_buf;
} else if (strcmp(argv[1], "1") == 0) {
idx = 1;
buf = &rw_buf;
} else if (strcmp(argv[1], "2") == 0) {
idx = 2;
buf = &ro_buf;
} else if (strcmp(argv[1], "3") == 0) {
idx = 3;
buf = &ro_buf;
} else {
print_err("invalid SPI flash device", SYSVAR_MESSAGE);
return 1;
}
if (strcmp(argv[0], "write") == 0) {
/* fill data to data buffer */
for (i = 0; i < buf->data_len; i++)
buf->data[i] = i;
/* write the data buffer to spi_flash */
ret = write_buff(flash_info, buf->data, sysvar_offset[idx] + CFG_FLASH_BASE,
buf->data_len);
printf("\n");
} else if (strcmp(argv[0], "erase") == 0) {
/* erase spi_flash */
sector = __SYSVAR_ERASE_SECTOR(sysvar_offset[idx]);
ret = flash_erase(flash_info, sector, sector);
}
if (ret == 0) {
ret = read_buff(flash_info, buf->data, sysvar_offset[idx], buf->data_len);
if (ret == 0)
sysvar_dump(buf, idx, false);
}
if (ret != 0)
printf("## Error: SPI flash %s failed at 0x%08lx\n",
argv[0], sysvar_offset[idx]);
rw_buf.loaded = false;
ro_buf.loaded = false;
return ret;
}
/*
* do_flsysvar - system variable debug functions
*/
int do_flsysvar(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) {
if (argc < 2)
goto usage;
if (strcmp(argv[1], "set") == 0) {
if (argc == 4) {
/* add a system variable(RO) */
return sf_setvar(&ro_buf, SYSVAR_RO_BUF, argv[2], argv[3]);
} else if (argc == 3) {
/* delete a system variable(RO) */
return sf_setvar(&ro_buf, SYSVAR_RO_BUF, argv[2], NULL);
} else if (argc == 2) {
/* delete all system variables(RO) */
return sf_setvar(&ro_buf, SYSVAR_RO_BUF, NULL, NULL);
} else {
goto usage;
}
}
if (strcmp(argv[1], "save") == 0 && argc == 2) {
/* save system variables(RO) */
return sf_savevar(&ro_buf, SYSVAR_RO_BUF);
}
if (strcmp(argv[1], "dump") == 0 && argc == 3) {
if (strcmp(argv[2], "rw") == 0) {
/* dump data in data buffer(RW) */
sysvar_dump(&rw_buf, SYSVAR_RW_BUF, true);
} else if (strcmp(argv[2], "ro") == 0) {
/* dump data in data buffer(RO) */
sysvar_dump(&ro_buf, SYSVAR_RO_BUF, true);
} else {
goto usage;
}
return 0;
}
if ((strcmp(argv[1], "read") == 0 && argc == 3) ||
(strcmp(argv[1], "write") == 0 && argc == 3) ||
(strcmp(argv[1], "erase") == 0 && argc == 3))
return sysvar_io(argc - 1, argv + 1);
usage:
printf("Usage:\n%s\n", cmdtp->usage);
return 1;
}
U_BOOT_CMD(
sysvar, 4, 0, do_flsysvar,
"sysvar - system variable debug functions\n",
"set name value\n"
" - set system variable(RO) 'name' to 'value ...'\n"
"sysvar set name\n"
" - delete system variable(RO) 'name'\n"
"sysvar set\n"
" - delete all system variables(RO)\n"
"sysvar save\n"
" - save system variables(RO) to SPI flash\n"
"sysvar dump rw|ro\n"
" - dump data in data buffer\n"
"sysvar read 0|1|2|3\n"
" - read data from SPI flash 0|1|2|3 to data buffer\n"
"sysvar write 0|1|2|3\n"
" - write data from data buffer to SPI flash 0|1|2|3\n"
"sysvar erase 0|1|2|3\n"
" - erase data on SPI flash 0|1|2|3\n"
);
/*
* sysvar_buf_init - Initializes a sysvar_buf struct.
*/
static int sysvar_buf_init(struct sysvar_buf *buf, bool is_ro) {
if (buf == NULL) {
return SYSVAR_PARAM_ERR;
}
memset(buf, 0, sizeof(*buf));
buf->list = (struct sysvar_list *)malloc(sizeof(struct sysvar_list));
if (buf->list == NULL) {
printf("list allocation ");
return SYSVAR_MEMORY_ERR;
}
buf->data = (uchar *) map_physmem(is_ro ? SYSVAR_RO_MEM : SYSVAR_RW_MEM);
if (buf->data == NULL) {
printf("data allocation ");
free(buf->list);
return SYSVAR_MEMORY_ERR;
}
buf->data_len = SYSVAR_BLOCK_SIZE;
buf->total_len = SYSVAR_BLOCK_SIZE - SYSVAR_HEAD;
buf->free_len = buf->total_len;
buf->readonly = is_ro;
strncpy(buf->list->name, is_ro ? "ro" : "rw", SYSVAR_NAME);
buf->list->value = NULL;
buf->list->len = SYSVAR_NAME + 2;
buf->list->next = NULL;
buf->loaded = false;
return SYSVAR_SUCCESS;
}
static void sysvar_env_init(struct sysvar_buf *buf) {
struct sysvar_list *curr = buf->list->next;
while (curr != NULL) {
/* Should only set env variables for special vars. */
set_sysvar_uboot(curr->name, curr->value);
curr = curr->next;
}
}
int sysvar_init(void) {
printf("sysvar_init. . .\n");
if (sysvar_buf_init(&rw_buf, false) || sysvar_buf_init(&ro_buf, true) ||
sf_loadvar()) {
printf("init failure!\n");
return SYSVAR_MEMORY_ERR;
}
sysvar_env_init(&rw_buf);
sysvar_env_init(&ro_buf);
printf("init success!\n");
return SYSVAR_SUCCESS;
}
int is_sysvar_shared(const char *var) {
int i;
for (i = 0; i < ARRAY_SIZE(su_map.map); ++i) {
if (strcmp(var, su_map.map[i].uboot_name) == 0 ||
strcmp(var, su_map.map[i].sysvar_name) == 0) {
return true;
}
}
return false;
}
const char *get_uboot_name(const char *sysvar_name) {
int i;
for (i = 0; i < ARRAY_SIZE(su_map.map); ++i) {
if (strcmp(sysvar_name, su_map.map[i].uboot_name) == 0) {
return su_map.map[i].uboot_name;
}
}
return NULL;
}
void delete_uboot_vars(void) {
int i;
for (i = 0; i < ARRAY_SIZE(su_map.map); ++i) {
setenv(su_map.map[i].sysvar_name, NULL);
setenv(su_map.map[i].uboot_name, NULL);
}
}