u-boot-brain/common/env_eeprom.c
Rob Herring 60d7d5a631 env: fix potential stack overflow in environment functions
Most of the various environment functions create CONFIG_ENV_SIZE buffers on
the stack. At least on ARM and PPC which have 4KB stacks, this can overflow
the stack if we have large environment sizes. So move all the buffers off
the stack to static buffers.

Signed-off-by: Rob Herring <rob.herring@calxeda.com>
2013-04-02 16:23:34 -04:00

293 lines
6.7 KiB
C

/*
* (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>
*
* 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
*/
#include <common.h>
#include <command.h>
#include <environment.h>
#include <linux/stddef.h>
#if defined(CONFIG_I2C_ENV_EEPROM_BUS)
#include <i2c.h>
#endif
#include <search.h>
#include <errno.h>
#include <linux/compiler.h> /* for BUG_ON */
DECLARE_GLOBAL_DATA_PTR;
env_t *env_ptr;
static char env_buf[CONFIG_ENV_SIZE];
char *env_name_spec = "EEPROM";
int env_eeprom_bus = -1;
static int eeprom_bus_read(unsigned dev_addr, unsigned offset,
uchar *buffer, unsigned cnt)
{
int rcode;
#if defined(CONFIG_I2C_ENV_EEPROM_BUS)
int old_bus = i2c_get_bus_num();
if (gd->flags & GD_FLG_RELOC) {
if (env_eeprom_bus == -1) {
I2C_MUX_DEVICE *dev = NULL;
dev = i2c_mux_ident_muxstring(
(uchar *)CONFIG_I2C_ENV_EEPROM_BUS);
if (dev != NULL)
env_eeprom_bus = dev->busid;
else
printf("error adding env eeprom bus.\n");
}
if (old_bus != env_eeprom_bus) {
i2c_set_bus_num(env_eeprom_bus);
old_bus = env_eeprom_bus;
}
} else {
rcode = i2c_mux_ident_muxstring_f(
(uchar *)CONFIG_I2C_ENV_EEPROM_BUS);
}
#endif
rcode = eeprom_read(dev_addr, offset, buffer, cnt);
#if defined(CONFIG_I2C_ENV_EEPROM_BUS)
if (old_bus != env_eeprom_bus)
i2c_set_bus_num(old_bus);
#endif
return rcode;
}
static int eeprom_bus_write(unsigned dev_addr, unsigned offset,
uchar *buffer, unsigned cnt)
{
int rcode;
#if defined(CONFIG_I2C_ENV_EEPROM_BUS)
int old_bus = i2c_get_bus_num();
rcode = i2c_mux_ident_muxstring_f((uchar *)CONFIG_I2C_ENV_EEPROM_BUS);
#endif
rcode = eeprom_write(dev_addr, offset, buffer, cnt);
#if defined(CONFIG_I2C_ENV_EEPROM_BUS)
i2c_set_bus_num(old_bus);
#endif
return rcode;
}
uchar env_get_char_spec(int index)
{
uchar c;
unsigned int off = CONFIG_ENV_OFFSET;
#ifdef CONFIG_ENV_OFFSET_REDUND
if (gd->env_valid == 2)
off = CONFIG_ENV_OFFSET_REDUND;
#endif
eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
off + index + offsetof(env_t, data), &c, 1);
return c;
}
void env_relocate_spec(void)
{
char *buf = env_buf;
unsigned int off = CONFIG_ENV_OFFSET;
#ifdef CONFIG_ENV_OFFSET_REDUND
if (gd->env_valid == 2)
off = CONFIG_ENV_OFFSET_REDUND;
#endif
eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
off, (uchar *)buf, CONFIG_ENV_SIZE);
env_import(buf, 1);
}
int saveenv(void)
{
env_t *env_new = (env_t *)env_buf;
ssize_t len;
char *res;
int rc;
unsigned int off = CONFIG_ENV_OFFSET;
#ifdef CONFIG_ENV_OFFSET_REDUND
unsigned int off_red = CONFIG_ENV_OFFSET_REDUND;
char flag_obsolete = OBSOLETE_FLAG;
#endif
BUG_ON(env_ptr != NULL);
res = (char *)env_new->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;
}
env_new->crc = crc32(0, env_new->data, ENV_SIZE);
#ifdef CONFIG_ENV_OFFSET_REDUND
if (gd->env_valid == 1) {
off = CONFIG_ENV_OFFSET_REDUND;
off_red = CONFIG_ENV_OFFSET;
}
env_new->flags = ACTIVE_FLAG;
#endif
rc = eeprom_bus_write(CONFIG_SYS_DEF_EEPROM_ADDR,
off, (uchar *)env_new, CONFIG_ENV_SIZE);
#ifdef CONFIG_ENV_OFFSET_REDUND
if (rc == 0) {
eeprom_bus_write(CONFIG_SYS_DEF_EEPROM_ADDR,
off_red + offsetof(env_t, flags),
(uchar *)&flag_obsolete, 1);
if (gd->env_valid == 1)
gd->env_valid = 2;
else
gd->env_valid = 1;
}
#endif
return rc;
}
/*
* Initialize Environment use
*
* We are still running from ROM, so data use is limited.
* Use a (moderately small) buffer on the stack
*/
#ifdef CONFIG_ENV_OFFSET_REDUND
int env_init(void)
{
ulong len, crc[2], crc_tmp;
unsigned int off, off_env[2];
uchar buf[64], flags[2];
int i, crc_ok[2] = {0, 0};
eeprom_init(); /* prepare for EEPROM read/write */
off_env[0] = CONFIG_ENV_OFFSET;
off_env[1] = CONFIG_ENV_OFFSET_REDUND;
for (i = 0; i < 2; i++) {
/* read CRC */
eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
off_env[i] + offsetof(env_t, crc),
(uchar *)&crc[i], sizeof(ulong));
/* read FLAGS */
eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
off_env[i] + offsetof(env_t, flags),
(uchar *)&flags[i], sizeof(uchar));
crc_tmp = 0;
len = ENV_SIZE;
off = off_env[i] + offsetof(env_t, data);
while (len > 0) {
int n = (len > sizeof(buf)) ? sizeof(buf) : len;
eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, off,
buf, n);
crc_tmp = crc32(crc_tmp, buf, n);
len -= n;
off += n;
}
if (crc_tmp == crc[i])
crc_ok[i] = 1;
}
if (!crc_ok[0] && !crc_ok[1]) {
gd->env_addr = 0;
gd->env_valid = 0;
return 0;
} else if (crc_ok[0] && !crc_ok[1]) {
gd->env_valid = 1;
} else if (!crc_ok[0] && crc_ok[1]) {
gd->env_valid = 2;
} else {
/* both ok - check serial */
if (flags[0] == ACTIVE_FLAG && flags[1] == OBSOLETE_FLAG)
gd->env_valid = 1;
else if (flags[0] == OBSOLETE_FLAG && flags[1] == ACTIVE_FLAG)
gd->env_valid = 2;
else if (flags[0] == 0xFF && flags[1] == 0)
gd->env_valid = 2;
else if (flags[1] == 0xFF && flags[0] == 0)
gd->env_valid = 1;
else /* flags are equal - almost impossible */
gd->env_valid = 1;
}
if (gd->env_valid == 2)
gd->env_addr = off_env[1] + offsetof(env_t, data);
else if (gd->env_valid == 1)
gd->env_addr = off_env[0] + offsetof(env_t, data);
return 0;
}
#else
int env_init(void)
{
ulong crc, len, new;
unsigned off;
uchar buf[64];
eeprom_init(); /* prepare for EEPROM read/write */
/* read old CRC */
eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
CONFIG_ENV_OFFSET + offsetof(env_t, crc),
(uchar *)&crc, sizeof(ulong));
new = 0;
len = ENV_SIZE;
off = offsetof(env_t, data);
while (len > 0) {
int n = (len > sizeof(buf)) ? sizeof(buf) : len;
eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
CONFIG_ENV_OFFSET + off, buf, n);
new = crc32(new, buf, n);
len -= n;
off += n;
}
if (crc == new) {
gd->env_addr = offsetof(env_t, data);
gd->env_valid = 1;
} else {
gd->env_addr = 0;
gd->env_valid = 0;
}
return 0;
}
#endif