Newer
Older
* (C) Copyright 2000-2010
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
*
* (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
* Andreas Heppel <aheppel@sysgo.de>
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <command.h>
#include <environment.h>
#include <linux/stddef.h>
#include <search.h>
#include <errno.h>
DECLARE_GLOBAL_DATA_PTR;
/************************************************************************
* Default settings to be used when no valid environment is found
*/
#include <env_default.h>
struct hsearch_data env_htab = {
__weak uchar env_get_char_spec(int index)
{
return *((uchar *)(gd->env_addr + index));
}
static uchar env_get_char_init(int index)
{
/* if crc was bad, use the default environment */
if (gd->env_valid)
return env_get_char_spec(index);
return default_environment[index];
uchar env_get_char_memory(int index)
return *env_get_addr(index);
uchar env_get_char(int index)
{
/* if relocated to RAM */
if (gd->flags & GD_FLG_RELOC)
return env_get_char_memory(index);
return env_get_char_init(index);
const uchar *env_get_addr(int index)
if (gd->env_valid)
return (uchar *)(gd->env_addr + index);
else
return &default_environment[index];
/*
* Read an environment variable as a boolean
* Return -1 if variable does not exist (default to true)
*/
int getenv_yesno(const char *var)
{
char *s = getenv(var);
if (s == NULL)
return -1;
return (*s == '1' || *s == 'y' || *s == 'Y' || *s == 't' || *s == 'T') ?
1 : 0;
}
/*
* Look up the variable from the default environment
*/
char *getenv_default(const char *name)
{
char *ret_val;
unsigned long really_valid = gd->env_valid;
unsigned long real_gd_flags = gd->flags;
/* Pretend that the image is bad. */
gd->flags &= ~GD_FLG_ENV_READY;
gd->env_valid = 0;
ret_val = getenv(name);
gd->env_valid = really_valid;
gd->flags = real_gd_flags;
return ret_val;
}
void set_default_env(const char *s)
if (sizeof(default_environment) > ENV_SIZE) {
puts("*** Error - default environment is too large\n\n");
return;
}
if (s) {
if (*s == '!') {
printf("*** Warning - %s, "
"using default environment\n\n",
} else {
puts(s);
}
} else {
puts("Using default environment\n\n");
}
if (himport_r(&env_htab, (char *)default_environment,
Alexander Holler
committed
sizeof(default_environment), '\0', flags, 0,
error("Environment import failed: errno = %d\n", errno);
gd->flags |= GD_FLG_ENV_READY;
gd->flags |= GD_FLG_ENV_DEFAULT;
/* [re]set individual variables to their value in the default environment */
int set_default_vars(int nvars, char * const vars[])
{
/*
* Special use-case: import from default environment
* (and use \0 as a separator)
*/
return himport_r(&env_htab, (const char *)default_environment,
Alexander Holler
committed
H_NOCLEAR | H_INTERACTIVE, 0, nvars, vars);
/**
* env_aes_cbc_get_key() - Get AES-128-CBC key for the environment
*
* This function shall return 16-byte array containing AES-128 key used
* to encrypt and decrypt the environment. This function must be overridden
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
* by the implementer as otherwise the environment encryption will not
* work.
*/
__weak uint8_t *env_aes_cbc_get_key(void)
{
return NULL;
}
static int env_aes_cbc_crypt(env_t *env, const int enc)
{
unsigned char *data = env->data;
uint8_t *key;
uint8_t key_exp[AES_EXPAND_KEY_LENGTH];
uint32_t aes_blocks;
key = env_aes_cbc_get_key();
if (!key)
return -EINVAL;
/* First we expand the key. */
aes_expand_key(key, key_exp);
/* Calculate the number of AES blocks to encrypt. */
aes_blocks = ENV_SIZE / 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;
}
#else
static inline int env_aes_cbc_crypt(env_t *env, const int enc)
{
return 0;
}
#endif
/*
* Check if CRC is valid and (if yes) import the environment.
* Note that "buf" may or may not be aligned.
*/
int env_import(const char *buf, int check)
env_t *ep = (env_t *)buf;
if (check) {
uint32_t crc;
memcpy(&crc, &ep->crc, sizeof(crc));
if (crc32(0, ep->data, ENV_SIZE) != crc) {
set_default_env("!bad CRC");
return 0;
}
}
/* Decrypt the env if desired. */
ret = env_aes_cbc_crypt(ep, 0);
if (ret) {
error("Failed to decrypt env!\n");
set_default_env("!import failed");
return ret;
}
Alexander Holler
committed
if (himport_r(&env_htab, (char *)ep->data, ENV_SIZE, '\0', 0, 0,
gd->flags |= GD_FLG_ENV_READY;
return 1;
}
error("Cannot import environment: errno = %d\n", errno);
set_default_env("!import failed");
return 0;
}
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
static unsigned char env_flags;
int env_import_redund(const char *buf1, const char *buf2)
{
int crc1_ok, crc2_ok;
env_t *ep, *tmp_env1, *tmp_env2;
tmp_env1 = (env_t *)buf1;
tmp_env2 = (env_t *)buf2;
crc1_ok = crc32(0, tmp_env1->data, ENV_SIZE) ==
tmp_env1->crc;
crc2_ok = crc32(0, tmp_env2->data, ENV_SIZE) ==
tmp_env2->crc;
if (!crc1_ok && !crc2_ok) {
set_default_env("!bad CRC");
return 0;
} else if (crc1_ok && !crc2_ok) {
gd->env_valid = 1;
} else if (!crc1_ok && crc2_ok) {
gd->env_valid = 2;
} else {
/* both ok - check serial */
if (tmp_env1->flags == 255 && tmp_env2->flags == 0)
gd->env_valid = 2;
else if (tmp_env2->flags == 255 && tmp_env1->flags == 0)
gd->env_valid = 1;
else if (tmp_env1->flags > tmp_env2->flags)
gd->env_valid = 1;
else if (tmp_env2->flags > tmp_env1->flags)
gd->env_valid = 2;
else /* flags are equal - almost impossible */
gd->env_valid = 1;
}
if (gd->env_valid == 1)
ep = tmp_env1;
else
ep = tmp_env2;
env_flags = ep->flags;
return env_import((char *)ep, 0);
}
#endif /* CONFIG_SYS_REDUNDAND_ENVIRONMENT */
/* Export the environment and generate CRC for it. */
int env_export(env_t *env_out)
{
char *res;
ssize_t len;
res = (char *)env_out->data;
len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL);
if (len < 0) {
error("Cannot export environment: errno = %d\n", errno);
return 1;
}
/* Encrypt the env if desired. */
ret = env_aes_cbc_crypt(env_out, 1);
if (ret)
return ret;
env_out->crc = crc32(0, env_out->data, ENV_SIZE);
#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
env_out->flags = ++env_flags; /* increase the serial */
#endif
void env_relocate(void)
#if defined(CONFIG_NEEDS_MANUAL_RELOC)
Heiko Schocher
committed
env_reloc();
env_htab.change_ok += gd->reloc_off;
Heiko Schocher
committed
#endif
#if defined(CONFIG_ENV_IS_NOWHERE) || defined(CONFIG_SPL_BUILD)
/* Environment not changable */
set_default_env(NULL);
bootstage_error(BOOTSTAGE_ID_NET_CHECKSUM);
set_default_env("!bad CRC");
} else {
#if defined(CONFIG_AUTO_COMPLETE) && !defined(CONFIG_SPL_BUILD)
int env_complete(char *var, int maxv, char *cmdv[], int bufsz, char *buf)
{
ENTRY *match;
int found, idx;
found = 0;
cmdv[0] = NULL;
while ((idx = hmatch_r(var, idx, &match, &env_htab))) {
int vallen = strlen(match->key) + 1;
if (found >= maxv - 2 || bufsz < vallen)
memcpy(buf, match->key, vallen);
buf += vallen;
bufsz -= vallen;
qsort(cmdv, found, sizeof(cmdv[0]), strcmp_compar);
if (idx)
cmdv[found++] = "...";
cmdv[found] = NULL;
return found;
}
#endif