?? fw_env.c
字號(hào):
/* * (C) Copyright 2000-2008 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. * * (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 */#include <errno.h>#include <fcntl.h>#include <stdio.h>#include <stdlib.h>#include <stddef.h>#include <string.h>#include <sys/types.h>#include <sys/ioctl.h>#include <sys/stat.h>#include <unistd.h>#ifdef MTD_OLD# include <stdint.h># include <linux/mtd/mtd.h>#else# define __user /* nothing */# include <mtd/mtd-user.h>#endif#include "fw_env.h"#define CMD_GETENV "fw_printenv"#define CMD_SETENV "fw_setenv"#define min(x, y) ({ \ typeof(x) _min1 = (x); \ typeof(y) _min2 = (y); \ (void) (&_min1 == &_min2); \ _min1 < _min2 ? _min1 : _min2; })struct envdev_s { char devname[16]; /* Device name */ ulong devoff; /* Device offset */ ulong env_size; /* environment size */ ulong erase_size; /* device erase size */ ulong env_sectors; /* number of environment sectors */ uint8_t mtd_type; /* type of the MTD device */};static struct envdev_s envdevices[2] ={ { .mtd_type = MTD_ABSENT, }, { .mtd_type = MTD_ABSENT, },};static int dev_current;#define DEVNAME(i) envdevices[(i)].devname#define DEVOFFSET(i) envdevices[(i)].devoff#define ENVSIZE(i) envdevices[(i)].env_size#define DEVESIZE(i) envdevices[(i)].erase_size#define ENVSECTORS(i) envdevices[(i)].env_sectors#define DEVTYPE(i) envdevices[(i)].mtd_type#define CONFIG_ENV_SIZE ENVSIZE(dev_current)#define ENV_SIZE getenvsize()struct env_image_single { uint32_t crc; /* CRC32 over data bytes */ char data[];};struct env_image_redundant { uint32_t crc; /* CRC32 over data bytes */ unsigned char flags; /* active or obsolete */ char data[];};enum flag_scheme { FLAG_NONE, FLAG_BOOLEAN, FLAG_INCREMENTAL,};struct environment { void *image; uint32_t *crc; unsigned char *flags; char *data; enum flag_scheme flag_scheme;};static struct environment environment = { .flag_scheme = FLAG_NONE,};static int HaveRedundEnv = 0;static unsigned char active_flag = 1;/* obsolete_flag must be 0 to efficiently set it on NOR flash without erasing */static unsigned char obsolete_flag = 0;#define XMK_STR(x) #x#define MK_STR(x) XMK_STR(x)static char default_environment[] = {#if defined(CONFIG_BOOTARGS) "bootargs=" CONFIG_BOOTARGS "\0"#endif#if defined(CONFIG_BOOTCOMMAND) "bootcmd=" CONFIG_BOOTCOMMAND "\0"#endif#if defined(CONFIG_RAMBOOTCOMMAND) "ramboot=" CONFIG_RAMBOOTCOMMAND "\0"#endif#if defined(CONFIG_NFSBOOTCOMMAND) "nfsboot=" CONFIG_NFSBOOTCOMMAND "\0"#endif#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) "bootdelay=" MK_STR (CONFIG_BOOTDELAY) "\0"#endif#if defined(CONFIG_BAUDRATE) && (CONFIG_BAUDRATE >= 0) "baudrate=" MK_STR (CONFIG_BAUDRATE) "\0"#endif#ifdef CONFIG_LOADS_ECHO "loads_echo=" MK_STR (CONFIG_LOADS_ECHO) "\0"#endif#ifdef CONFIG_ETHADDR "ethaddr=" MK_STR (CONFIG_ETHADDR) "\0"#endif#ifdef CONFIG_ETH1ADDR "eth1addr=" MK_STR (CONFIG_ETH1ADDR) "\0"#endif#ifdef CONFIG_ETH2ADDR "eth2addr=" MK_STR (CONFIG_ETH2ADDR) "\0"#endif#ifdef CONFIG_ETH3ADDR "eth3addr=" MK_STR (CONFIG_ETH3ADDR) "\0"#endif#ifdef CONFIG_ETHPRIME "ethprime=" CONFIG_ETHPRIME "\0"#endif#ifdef CONFIG_IPADDR "ipaddr=" MK_STR (CONFIG_IPADDR) "\0"#endif#ifdef CONFIG_SERVERIP "serverip=" MK_STR (CONFIG_SERVERIP) "\0"#endif#ifdef CFG_AUTOLOAD "autoload=" CFG_AUTOLOAD "\0"#endif#ifdef CONFIG_ROOTPATH "rootpath=" MK_STR (CONFIG_ROOTPATH) "\0"#endif#ifdef CONFIG_GATEWAYIP "gatewayip=" MK_STR (CONFIG_GATEWAYIP) "\0"#endif#ifdef CONFIG_NETMASK "netmask=" MK_STR (CONFIG_NETMASK) "\0"#endif#ifdef CONFIG_HOSTNAME "hostname=" MK_STR (CONFIG_HOSTNAME) "\0"#endif#ifdef CONFIG_BOOTFILE "bootfile=" MK_STR (CONFIG_BOOTFILE) "\0"#endif#ifdef CONFIG_LOADADDR "loadaddr=" MK_STR (CONFIG_LOADADDR) "\0"#endif#ifdef CONFIG_PREBOOT "preboot=" CONFIG_PREBOOT "\0"#endif#ifdef CONFIG_CLOCKS_IN_MHZ "clocks_in_mhz=" "1" "\0"#endif#if defined(CONFIG_PCI_BOOTDELAY) && (CONFIG_PCI_BOOTDELAY > 0) "pcidelay=" MK_STR (CONFIG_PCI_BOOTDELAY) "\0"#endif#ifdef CONFIG_EXTRA_ENV_SETTINGS CONFIG_EXTRA_ENV_SETTINGS#endif "\0" /* Termimate struct environment data with 2 NULs */};static int flash_io (int mode);static char *envmatch (char * s1, char * s2);static int env_init (void);static int parse_config (void);#if defined(CONFIG_FILE)static int get_config (char *);#endifstatic inline ulong getenvsize (void){ ulong rc = CONFIG_ENV_SIZE - sizeof (long); if (HaveRedundEnv) rc -= sizeof (char); return rc;}/* * Search the environment for a variable. * Return the value, if found, or NULL, if not found. */char *fw_getenv (char *name){ char *env, *nxt; if (env_init ()) return NULL; for (env = environment.data; *env; env = nxt + 1) { char *val; for (nxt = env; *nxt; ++nxt) { if (nxt >= &environment.data[ENV_SIZE]) { fprintf (stderr, "## Error: " "environment not terminated\n"); return NULL; } } val = envmatch (name, env); if (!val) continue; return val; } return NULL;}/* * 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; if (env_init ()) return -1; if (argc == 1) { /* Print all env variables */ for (env = environment.data; *env; env = nxt + 1) { for (nxt = env; *nxt; ++nxt) { if (nxt >= &environment.data[ENV_SIZE]) { fprintf (stderr, "## Error: " "environment not terminated\n"); return -1; } } printf ("%s\n", env); } 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; } for (i = 1; i < argc; ++i) { /* print single env variables */ char *name = argv[i]; char *val = NULL; for (env = environment.data; *env; env = nxt + 1) { for (nxt = env; *nxt; ++nxt) { if (nxt >= &environment.data[ENV_SIZE]) { fprintf (stderr, "## Error: " "environment not terminated\n"); return -1; } } val = envmatch (name, env); if (val) { if (!n_flag) { fputs (name, stdout); putc ('=', stdout); } puts (val); break; } } if (!val) { fprintf (stderr, "## Error: \"%s\" not defined\n", name); rc = -1; } } return rc;}/* * Deletes or sets environment variables. Returns -1 and sets errno error codes: * 0 - OK * EINVAL - need at least 1 argument * EROFS - certain variables ("ethaddr", "serial#") cannot be * modified or deleted * */int fw_setenv (int argc, char *argv[]){ int i, len; char *env, *nxt; char *oldval = NULL; char *name; if (argc < 2) { errno = EINVAL; return -1; } if (env_init ()) return -1; name = argv[1]; /* * search if variable with this name already exists */ for (nxt = env = environment.data; *env; env = nxt + 1) { for (nxt = env; *nxt; ++nxt) { if (nxt >= &environment.data[ENV_SIZE]) { fprintf (stderr, "## Error: " "environment not terminated\n"); errno = EINVAL; return -1; } } if ((oldval = envmatch (name, env)) != NULL) break; } /* * Delete any existing definition */ if (oldval) { /* * Ethernet Address and serial# can be set only once */ if ((strcmp (name, "ethaddr") == 0) || (strcmp (name, "serial#") == 0)) { fprintf (stderr, "Can't overwrite \"%s\"\n", name); errno = EROFS; return -1; } if (*++nxt == '\0') { *env = '\0'; } else { for (;;) { *env = *nxt++; if ((*env == '\0') && (*nxt == '\0')) break; ++env; } } *++env = '\0'; } /* Delete only ? */ if (argc < 3) goto WRITE_FLASH; /* * Append new definition at the end */ for (env = environment.data; *env || *(env + 1); ++env); if (env > environment.data) ++env; /* * Overflow when: * "name" + "=" + "val" +"\0\0" > CONFIG_ENV_SIZE - (env-environment) */ len = strlen (name) + 2; /* add '=' for first arg, ' ' for all others */ for (i = 2; i < argc; ++i) { len += strlen (argv[i]) + 1; } if (len > (&environment.data[ENV_SIZE] - env)) { fprintf (stderr, "Error: environment overflow, \"%s\" deleted\n", name); return -1; } while ((*env = *name++) != '\0') env++; for (i = 2; i < argc; ++i) { char *val = argv[i]; *env = (i == 2) ? '=' : ' '; while ((*++env = *val++) != '\0'); } /* end is marked with double '\0' */ *++env = '\0'; WRITE_FLASH: /* * Update CRC */ *environment.crc = crc32 (0, (uint8_t *) environment.data, ENV_SIZE); /* write environment back to flash */ if (flash_io (O_RDWR)) { fprintf (stderr, "Error: can't write fw_env to flash\n"); return -1; } return 0;}/* * Test for bad block on NAND, just returns 0 on NOR, on NAND: * 0 - block is good * > 0 - block is bad * < 0 - failed to test */static int flash_bad_block (int fd, uint8_t mtd_type, loff_t *blockstart){ if (mtd_type == MTD_NANDFLASH) { int badblock = ioctl (fd, MEMGETBADBLOCK, blockstart); if (badblock < 0) { perror ("Cannot read bad block mark"); return badblock; } if (badblock) {#ifdef DEBUG fprintf (stderr, "Bad block at 0x%llx, " "skipping\n", *blockstart);#endif return badblock; } } return 0;}/* * Read data from flash at an offset into a provided buffer. On NAND it skips * bad blocks but makes sure it stays within ENVSECTORS (dev) starting from * the DEVOFFSET (dev) block. On NOR the loop is only run once. */static int flash_read_buf (int dev, int fd, void *buf, size_t count, off_t offset, uint8_t mtd_type){ size_t blocklen; /* erase / write length - one block on NAND, 0 on NOR */ size_t processed = 0; /* progress counter */ size_t readlen = count; /* current read length */ off_t top_of_range; /* end of the last block we may use */ off_t block_seek; /* offset inside the current block to the start of the data */ loff_t blockstart; /* running start of the current block - MEMGETBADBLOCK needs 64 bits */ int rc; /* * Start of the first block to be read, relies on the fact, that * erase sector size is always a power of 2 */ blockstart = offset & ~(DEVESIZE (dev) - 1); /* Offset inside a block */ block_seek = offset - blockstart; if (mtd_type == MTD_NANDFLASH) { /* * NAND: calculate which blocks we are reading. We have * to read one block at a time to skip bad blocks. */ blocklen = DEVESIZE (dev); /* * To calculate the top of the range, we have to use the * global DEVOFFSET (dev), which can be different from offset */ top_of_range = (DEVOFFSET (dev) & ~(blocklen - 1)) + ENVSECTORS (dev) * blocklen; /* Limit to one block for the first read */ if (readlen > blocklen - block_seek) readlen = blocklen - block_seek; } else { blocklen = 0; top_of_range = offset + count; } /* This only runs once on NOR flash */ while (processed < count) { rc = flash_bad_block (fd, mtd_type, &blockstart); if (rc < 0) /* block test failed */ return -1; if (blockstart + block_seek + readlen > top_of_range) { /* End of range is reached */ fprintf (stderr, "Too few good blocks within range\n"); return -1; } if (rc) { /* block is bad */ blockstart += blocklen; continue; } /* * If a block is bad, we retry in the next block at the same * offset - see common/env_nand.c::writeenv() */ lseek (fd, blockstart + block_seek, SEEK_SET); rc = read (fd, buf + processed, readlen); if (rc != readlen) { fprintf (stderr, "Read error on %s: %s\n", DEVNAME (dev), strerror (errno)); return -1; }#ifdef DEBUG fprintf (stderr, "Read 0x%x bytes at 0x%llx\n", rc, blockstart + block_seek);#endif processed += readlen; readlen = min (blocklen, count - processed); block_seek = 0;
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -