u-boot-brain/common/env_mmc.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

203 lines
4.2 KiB
C

/*
* (C) Copyright 2008-2011 Freescale Semiconductor, Inc.
*
* 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
*/
/* #define DEBUG */
#include <common.h>
#include <command.h>
#include <environment.h>
#include <linux/stddef.h>
#include <malloc.h>
#include <mmc.h>
#include <search.h>
#include <errno.h>
char *env_name_spec = "MMC";
#ifdef ENV_IS_EMBEDDED
env_t *env_ptr = &environment;
#else /* ! ENV_IS_EMBEDDED */
env_t *env_ptr;
#endif /* ENV_IS_EMBEDDED */
DEFINE_CACHE_ALIGN_BUFFER(char, env_buf, CONFIG_ENV_SIZE);
DECLARE_GLOBAL_DATA_PTR;
#if !defined(CONFIG_ENV_OFFSET)
#define CONFIG_ENV_OFFSET 0
#endif
__weak int mmc_get_env_addr(struct mmc *mmc, u32 *env_addr)
{
*env_addr = CONFIG_ENV_OFFSET;
return 0;
}
int env_init(void)
{
/* use default */
gd->env_addr = (ulong)&default_environment[0];
gd->env_valid = 1;
return 0;
}
static int init_mmc_for_env(struct mmc *mmc)
{
if (!mmc) {
puts("No MMC card found\n");
return -1;
}
if (mmc_init(mmc)) {
puts("MMC init failed\n");
return -1;
}
#ifdef CONFIG_SYS_MMC_ENV_PART
if (CONFIG_SYS_MMC_ENV_PART != mmc->part_num) {
if (mmc_switch_part(CONFIG_SYS_MMC_ENV_DEV,
CONFIG_SYS_MMC_ENV_PART)) {
puts("MMC partition switch failed\n");
return -1;
}
}
#endif
return 0;
}
static void fini_mmc_for_env(struct mmc *mmc)
{
#ifdef CONFIG_SYS_MMC_ENV_PART
if (CONFIG_SYS_MMC_ENV_PART != mmc->part_num)
mmc_switch_part(CONFIG_SYS_MMC_ENV_DEV,
mmc->part_num);
#endif
}
#ifdef CONFIG_CMD_SAVEENV
static inline int write_env(struct mmc *mmc, unsigned long size,
unsigned long offset, const void *buffer)
{
uint blk_start, blk_cnt, n;
blk_start = ALIGN(offset, mmc->write_bl_len) / mmc->write_bl_len;
blk_cnt = ALIGN(size, mmc->write_bl_len) / mmc->write_bl_len;
n = mmc->block_dev.block_write(CONFIG_SYS_MMC_ENV_DEV, blk_start,
blk_cnt, (u_char *)buffer);
return (n == blk_cnt) ? 0 : -1;
}
int saveenv(void)
{
env_t *env_new = (env_t *)env_buf;
ssize_t len;
char *res;
struct mmc *mmc = find_mmc_device(CONFIG_SYS_MMC_ENV_DEV);
u32 offset;
int ret;
if (init_mmc_for_env(mmc))
return 1;
if (mmc_get_env_addr(mmc, &offset)) {
ret = 1;
goto fini;
}
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);
ret = 1;
goto fini;
}
env_new->crc = crc32(0, env_new->data, ENV_SIZE);
printf("Writing to MMC(%d)... ", CONFIG_SYS_MMC_ENV_DEV);
if (write_env(mmc, CONFIG_ENV_SIZE, offset, (u_char *)env_new)) {
puts("failed\n");
ret = 1;
goto fini;
}
puts("done\n");
ret = 0;
fini:
fini_mmc_for_env(mmc);
return ret;
}
#endif /* CONFIG_CMD_SAVEENV */
static inline int read_env(struct mmc *mmc, unsigned long size,
unsigned long offset, const void *buffer)
{
uint blk_start, blk_cnt, n;
blk_start = ALIGN(offset, mmc->read_bl_len) / mmc->read_bl_len;
blk_cnt = ALIGN(size, mmc->read_bl_len) / mmc->read_bl_len;
n = mmc->block_dev.block_read(CONFIG_SYS_MMC_ENV_DEV, blk_start,
blk_cnt, (uchar *)buffer);
return (n == blk_cnt) ? 0 : -1;
}
void env_relocate_spec(void)
{
#if !defined(ENV_IS_EMBEDDED)
struct mmc *mmc = find_mmc_device(CONFIG_SYS_MMC_ENV_DEV);
u32 offset;
int ret;
if (init_mmc_for_env(mmc)) {
ret = 1;
goto err;
}
if (mmc_get_env_addr(mmc, &offset)) {
ret = 1;
goto fini;
}
if (read_env(mmc, CONFIG_ENV_SIZE, offset, env_buf)) {
ret = 1;
goto fini;
}
env_import(env_buf, 1);
ret = 0;
fini:
fini_mmc_for_env(mmc);
err:
if (ret)
set_default_env(NULL);
#endif
}