* (C) Copyright 2008
* Guennadi Liakhovetski, DENX Software Engineering, lg@denx.de.
*
- * See file CREDITS for list of people who contributed to this
- * project.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
+ * SPDX-License-Identifier: GPL-2.0+
*/
+#define _GNU_SOURCE
+
+#include <compiler.h>
#include <errno.h>
#include <env_flags.h>
#include <fcntl.h>
#include <linux/stringify.h>
+#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include "fw_env.h"
-#define WHITESPACE(c) ((c == '\t') || (c == ' '))
+struct common_args common_args;
+struct printenv_args printenv_args;
+struct setenv_args setenv_args;
+
+#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
#define min(x, y) ({ \
typeof(x) _min1 = (x); \
_min1 < _min2 ? _min1 : _min2; })
struct envdev_s {
- char devname[16]; /* Device name */
+ const char *devname; /* Device name */
ulong devoff; /* Device offset */
ulong env_size; /* environment size */
ulong erase_size; /* device erase size */
.flag_scheme = FLAG_NONE,
};
+static int env_aes_cbc_crypt(char *data, const int enc);
+
static int HaveRedundEnv = 0;
static unsigned char active_flag = 1;
#endif
static inline ulong getenvsize (void)
{
- ulong rc = CUR_ENVSIZE - sizeof(long);
+ ulong rc = CUR_ENVSIZE - sizeof(uint32_t);
if (HaveRedundEnv)
rc -= sizeof (char);
+
+ if (common_args.aes_flag)
+ rc &= ~(AES_KEY_LENGTH - 1);
+
return rc;
}
-static char *fw_string_blank(char *s, int noblank)
+static char *skip_chars(char *s)
{
- int i;
- int len = strlen(s);
-
- for (i = 0; i < len; i++, s++) {
- if ((noblank && !WHITESPACE(*s)) ||
- (!noblank && WHITESPACE(*s)))
- break;
+ for (; *s != '\0'; s++) {
+ if (isblank(*s))
+ return s;
}
- if (i == len)
- return NULL;
+ return NULL;
+}
- return s;
+static char *skip_blanks(char *s)
+{
+ for (; *s != '\0'; s++) {
+ if (!isblank(*s))
+ return s;
+ }
+ return NULL;
}
/*
return NULL;
}
+int parse_aes_key(char *key, uint8_t *bin_key)
+{
+ char tmp[5] = { '0', 'x', 0, 0, 0 };
+ unsigned long ul;
+ int i;
+
+ if (strnlen(key, 64) != 32) {
+ fprintf(stderr,
+ "## Error: '-a' option requires 16-byte AES key\n");
+ return -1;
+ }
+
+ for (i = 0; i < 16; i++) {
+ tmp[2] = key[0];
+ tmp[3] = key[1];
+ errno = 0;
+ ul = strtoul(tmp, NULL, 16);
+ if (errno) {
+ fprintf(stderr,
+ "## Error: '-a' option requires valid AES key\n");
+ return -1;
+ }
+ bin_key[i] = ul & 0xff;
+ key += 2;
+ }
+ return 0;
+}
+
/*
* Print the current definition of one, or more, or all
* environment variables
int fw_printenv (int argc, char *argv[])
{
char *env, *nxt;
- int i, n_flag;
- int rc = 0;
+ int i, rc = 0;
if (fw_env_open())
return -1;
- if (argc == 1) { /* Print all env variables */
+ if (argc == 0) { /* Print all env variables */
for (env = environment.data; *env; env = nxt + 1) {
for (nxt = env; *nxt; ++nxt) {
if (nxt >= &environment.data[ENV_SIZE]) {
return 0;
}
- if (strcmp (argv[1], "-n") == 0) {
- n_flag = 1;
- ++argv;
- --argc;
- if (argc != 2) {
- fprintf (stderr, "## Error: "
- "`-n' option requires exactly one argument\n");
- return -1;
- }
- } else {
- n_flag = 0;
+ if (printenv_args.name_suppress && argc != 1) {
+ fprintf(stderr,
+ "## Error: `-n' option requires exactly one argument\n");
+ return -1;
}
- for (i = 1; i < argc; ++i) { /* print single env variables */
+ for (i = 0; i < argc; ++i) { /* print single env variables */
char *name = argv[i];
char *val = NULL;
}
val = envmatch (name, env);
if (val) {
- if (!n_flag) {
+ if (!printenv_args.name_suppress) {
fputs (name, stdout);
putc ('=', stdout);
}
int fw_env_close(void)
{
+ int ret;
+ if (common_args.aes_flag) {
+ ret = env_aes_cbc_crypt(environment.data, 1);
+ if (ret) {
+ fprintf(stderr,
+ "Error: can't encrypt env for flash\n");
+ return ret;
+ }
+ }
+
/*
* Update CRC
*/
{
int i;
size_t len;
- char *name;
+ char *name, **valv;
char *value = NULL;
+ int valc;
- if (argc < 2) {
+ if (argc < 1) {
+ fprintf(stderr, "## Error: variable name missing\n");
errno = EINVAL;
return -1;
}
return -1;
}
- name = argv[1];
+ name = argv[0];
+ valv = argv + 1;
+ valc = argc - 1;
- if (env_flags_validate_env_set_params(argc, argv) < 0)
+ if (env_flags_validate_env_set_params(name, valv, valc) < 0)
return 1;
len = 0;
- for (i = 2; i < argc; ++i) {
- char *val = argv[i];
+ for (i = 0; i < valc; ++i) {
+ char *val = valv[i];
size_t val_len = strlen(val);
if (value)
}
/* Drop ending line feed / carriage return */
- while (len > 0 && (dump[len - 1] == '\n' ||
- dump[len - 1] == '\r')) {
- dump[len - 1] = '\0';
- len--;
- }
+ dump[--len] = '\0';
+ if (len && dump[len - 1] == '\r')
+ dump[--len] = '\0';
/* Skip comment or empty lines */
- if ((len == 0) || dump[0] == '#')
+ if (len == 0 || dump[0] == '#')
continue;
/*
* Search for variable's name,
* remove leading whitespaces
*/
- name = fw_string_blank(dump, 1);
+ name = skip_blanks(dump);
if (!name)
continue;
/* The first white space is the end of variable name */
- val = fw_string_blank(name, 0);
+ val = skip_chars(name);
len = strlen(name);
if (val) {
*val++ = '\0';
if ((val - name) < len)
- val = fw_string_blank(val, 1);
+ val = skip_blanks(val);
else
val = NULL;
}
MEMGETBADBLOCK needs 64 bits */
int rc;
- blocklen = DEVESIZE (dev);
+ /*
+ * For mtd devices only offset and size of the environment do matter
+ */
+ if (mtd_type == MTD_ABSENT) {
+ blocklen = count;
+ top_of_range = offset + count;
+ erase_len = blocklen;
+ blockstart = offset;
+ block_seek = 0;
+ write_total = blocklen;
+ } else {
+ blocklen = DEVESIZE(dev);
- top_of_range = ((DEVOFFSET(dev) / blocklen) +
- ENVSECTORS (dev)) * blocklen;
+ top_of_range = ((DEVOFFSET(dev) / blocklen) +
+ ENVSECTORS(dev)) * blocklen;
- erase_offset = (offset / blocklen) * blocklen;
+ erase_offset = (offset / blocklen) * blocklen;
- /* Maximum area we may use */
- erase_len = top_of_range - erase_offset;
+ /* Maximum area we may use */
+ erase_len = top_of_range - erase_offset;
- blockstart = erase_offset;
- /* Offset inside a block */
- block_seek = offset - erase_offset;
+ blockstart = erase_offset;
+ /* Offset inside a block */
+ block_seek = offset - erase_offset;
- /*
- * Data size we actually have to write: from the start of the block
- * to the start of the data, then count bytes of data, and to the
- * end of the block
- */
- write_total = ((block_seek + count + blocklen - 1) /
- blocklen) * blocklen;
+ /*
+ * Data size we actually write: from the start of the block
+ * to the start of the data, then count bytes of data, and
+ * to the end of the block
+ */
+ write_total = ((block_seek + count + blocklen - 1) /
+ blocklen) * blocklen;
+ }
/*
* Support data anywhere within erase sectors: read out the complete
continue;
}
- erase.start = blockstart;
- ioctl (fd, MEMUNLOCK, &erase);
- /* These do not need an explicit erase cycle */
- if (mtd_type != MTD_ABSENT &&
- mtd_type != MTD_DATAFLASH)
- if (ioctl (fd, MEMERASE, &erase) != 0) {
- fprintf (stderr, "MTD erase error on %s: %s\n",
- DEVNAME (dev),
- strerror (errno));
- return -1;
- }
+ if (mtd_type != MTD_ABSENT) {
+ erase.start = blockstart;
+ ioctl(fd, MEMUNLOCK, &erase);
+ /* These do not need an explicit erase cycle */
+ if (mtd_type != MTD_DATAFLASH)
+ if (ioctl(fd, MEMERASE, &erase) != 0) {
+ fprintf(stderr,
+ "MTD erase error on %s: %s\n",
+ DEVNAME(dev), strerror(errno));
+ return -1;
+ }
+ }
if (lseek (fd, blockstart, SEEK_SET) == -1) {
fprintf (stderr,
return -1;
}
- ioctl (fd, MEMLOCK, &erase);
+ if (mtd_type != MTD_ABSENT)
+ ioctl(fd, MEMLOCK, &erase);
- processed += blocklen;
+ processed += erasesize;
block_seek = 0;
- blockstart += blocklen;
+ blockstart += erasesize;
}
if (write_total > count)
return rc;
}
+/* Encrypt or decrypt the environment before writing or reading it. */
+static int env_aes_cbc_crypt(char *payload, const int enc)
+{
+ uint8_t *data = (uint8_t *)payload;
+ const int len = getenvsize();
+ uint8_t key_exp[AES_EXPAND_KEY_LENGTH];
+ uint32_t aes_blocks;
+
+ /* First we expand the key. */
+ aes_expand_key(common_args.aes_key, key_exp);
+
+ /* Calculate the number of AES blocks to encrypt. */
+ aes_blocks = DIV_ROUND_UP(len, AES_KEY_LENGTH);
+
+ if (enc)
+ aes_cbc_encrypt_blocks(key_exp, data, data, aes_blocks);
+ else
+ aes_cbc_decrypt_blocks(key_exp, data, data, aes_blocks);
+
+ return 0;
+}
+
static int flash_write (int fd_current, int fd_target, int dev_target)
{
int rc;
fprintf(stderr, "Writing new environment at 0x%lx on %s\n",
DEVOFFSET (dev_target), DEVNAME (dev_target));
#endif
+
rc = flash_write_buf(dev_target, fd_target, environment.image,
CUR_ENVSIZE, DEVOFFSET(dev_target),
DEVTYPE(dev_target));
rc = flash_read_buf(dev_current, fd, environment.image, CUR_ENVSIZE,
DEVOFFSET (dev_current), mtdinfo.type);
+ if (rc != CUR_ENVSIZE)
+ return -1;
- return (rc != CUR_ENVSIZE) ? -1 : 0;
+ return 0;
}
static int flash_io (int mode)
unsigned char flag1;
void *addr1;
+ int ret;
+
struct env_image_single *single;
struct env_image_redundant *redundant;
return -1;
crc0 = crc32 (0, (uint8_t *) environment.data, ENV_SIZE);
+
+ if (common_args.aes_flag) {
+ ret = env_aes_cbc_crypt(environment.data, 0);
+ if (ret)
+ return ret;
+ }
+
crc0_ok = (crc0 == *environment.crc);
if (!HaveRedundEnv) {
if (!crc0_ok) {
} else if (DEVTYPE(dev_current) == MTD_UBIVOLUME &&
DEVTYPE(!dev_current) == MTD_UBIVOLUME) {
environment.flag_scheme = FLAG_INCREMENTAL;
+ } else if (DEVTYPE(dev_current) == MTD_ABSENT &&
+ DEVTYPE(!dev_current) == MTD_ABSENT) {
+ environment.flag_scheme = FLAG_INCREMENTAL;
} else {
fprintf (stderr, "Incompatible flash types!\n");
return -1;
}
crc1 = crc32 (0, (uint8_t *) redundant->data, ENV_SIZE);
+
+ if (common_args.aes_flag) {
+ ret = env_aes_cbc_crypt(redundant->data, 0);
+ if (ret)
+ return ret;
+ }
+
crc1_ok = (crc1 == redundant->crc);
flag1 = redundant->flags;
#if defined(CONFIG_FILE)
/* Fills in DEVNAME(), ENVSIZE(), DEVESIZE(). Or don't. */
- if (get_config (CONFIG_FILE)) {
- fprintf (stderr,
- "Cannot parse config file: %s\n", strerror (errno));
+ if (get_config(common_args.config_file)) {
+ fprintf(stderr, "Cannot parse config file '%s': %m\n",
+ common_args.config_file);
return -1;
}
#else
- strcpy (DEVNAME (0), DEVICE1_NAME);
+ DEVNAME (0) = DEVICE1_NAME;
DEVOFFSET (0) = DEVICE1_OFFSET;
ENVSIZE (0) = ENV1_SIZE;
- /* Default values are: erase-size=env-size, #sectors=1 */
+ /* Default values are: erase-size=env-size */
DEVESIZE (0) = ENVSIZE (0);
- ENVSECTORS (0) = 1;
+ /* #sectors=env-size/erase-size (rounded up) */
+ ENVSECTORS (0) = (ENVSIZE(0) + DEVESIZE(0) - 1) / DEVESIZE(0);
#ifdef DEVICE1_ESIZE
DEVESIZE (0) = DEVICE1_ESIZE;
#endif
#endif
#ifdef HAVE_REDUND
- strcpy (DEVNAME (1), DEVICE2_NAME);
+ DEVNAME (1) = DEVICE2_NAME;
DEVOFFSET (1) = DEVICE2_OFFSET;
ENVSIZE (1) = ENV2_SIZE;
- /* Default values are: erase-size=env-size, #sectors=1 */
+ /* Default values are: erase-size=env-size */
DEVESIZE (1) = ENVSIZE (1);
- ENVSECTORS (1) = 1;
+ /* #sectors=env-size/erase-size (rounded up) */
+ ENVSECTORS (1) = (ENVSIZE(1) + DEVESIZE(1) - 1) / DEVESIZE(1);
#ifdef DEVICE2_ESIZE
DEVESIZE (1) = DEVICE2_ESIZE;
#endif
int i = 0;
int rc;
char dump[128];
+ char *devname;
fp = fopen (fname, "r");
if (fp == NULL)
if (dump[0] == '#')
continue;
- rc = sscanf (dump, "%s %lx %lx %lx %lx",
- DEVNAME (i),
+ rc = sscanf (dump, "%ms %lx %lx %lx %lx",
+ &devname,
&DEVOFFSET (i),
&ENVSIZE (i),
&DEVESIZE (i),
if (rc < 3)
continue;
+ DEVNAME(i) = devname;
+
if (rc < 4)
/* Assume the erase size is the same as the env-size */
DEVESIZE(i) = ENVSIZE(i);
if (rc < 5)
- /* Default - 1 sector */
- ENVSECTORS (i) = 1;
+ /* Assume enough env sectors to cover the environment */
+ ENVSECTORS (i) = (ENVSIZE(i) + DEVESIZE(i) - 1) / DEVESIZE(i);
i++;
}