| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 1 | /* | 
| Grant Erickson | bc11756 | 2008-05-06 20:16:15 -0700 | [diff] [blame] | 2 | * (C) Copyright 2000-2008 | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 3 | * Wolfgang Denk, DENX Software Engineering, wd@denx.de. | 
|  | 4 | * | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 5 | * (C) Copyright 2008 | 
|  | 6 | * Guennadi Liakhovetski, DENX Software Engineering, lg@denx.de. | 
|  | 7 | * | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 8 | * See file CREDITS for list of people who contributed to this | 
|  | 9 | * project. | 
|  | 10 | * | 
|  | 11 | * This program is free software; you can redistribute it and/or | 
|  | 12 | * modify it under the terms of the GNU General Public License as | 
|  | 13 | * published by the Free Software Foundation; either version 2 of | 
|  | 14 | * the License, or (at your option) any later version. | 
|  | 15 | * | 
|  | 16 | * This program is distributed in the hope that it will be useful, | 
|  | 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 19 | * GNU General Public License for more details. | 
|  | 20 | * | 
|  | 21 | * You should have received a copy of the GNU General Public License | 
|  | 22 | * along with this program; if not, write to the Free Software | 
|  | 23 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | 
|  | 24 | * MA 02111-1307 USA | 
|  | 25 | */ | 
|  | 26 |  | 
|  | 27 | #include <errno.h> | 
|  | 28 | #include <fcntl.h> | 
|  | 29 | #include <stdio.h> | 
|  | 30 | #include <stdlib.h> | 
|  | 31 | #include <stddef.h> | 
|  | 32 | #include <string.h> | 
|  | 33 | #include <sys/types.h> | 
|  | 34 | #include <sys/ioctl.h> | 
|  | 35 | #include <sys/stat.h> | 
|  | 36 | #include <unistd.h> | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 37 |  | 
| Markus Klotzbücher | 6de66b3 | 2007-11-27 10:23:20 +0100 | [diff] [blame] | 38 | #ifdef MTD_OLD | 
| Wolfgang Denk | 1711f3b | 2008-09-02 21:17:36 +0200 | [diff] [blame] | 39 | # include <stdint.h> | 
| Markus Klotzbücher | 6de66b3 | 2007-11-27 10:23:20 +0100 | [diff] [blame] | 40 | # include <linux/mtd/mtd.h> | 
|  | 41 | #else | 
| Wolfgang Denk | c83d7ca | 2008-01-08 22:58:27 +0100 | [diff] [blame] | 42 | # define  __user	/* nothing */ | 
| Markus Klotzbücher | 6de66b3 | 2007-11-27 10:23:20 +0100 | [diff] [blame] | 43 | # include <mtd/mtd-user.h> | 
|  | 44 | #endif | 
|  | 45 |  | 
|  | 46 | #include "fw_env.h" | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 47 |  | 
|  | 48 | #define	CMD_GETENV	"fw_printenv" | 
|  | 49 | #define	CMD_SETENV	"fw_setenv" | 
|  | 50 |  | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 51 | #define min(x, y) ({				\ | 
|  | 52 | typeof(x) _min1 = (x);			\ | 
|  | 53 | typeof(y) _min2 = (y);			\ | 
|  | 54 | (void) (&_min1 == &_min2);		\ | 
|  | 55 | _min1 < _min2 ? _min1 : _min2; }) | 
|  | 56 |  | 
|  | 57 | struct envdev_s { | 
| Markus Klotzbücher | 6de66b3 | 2007-11-27 10:23:20 +0100 | [diff] [blame] | 58 | char devname[16];		/* Device name */ | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 59 | ulong devoff;			/* Device offset */ | 
|  | 60 | ulong env_size;			/* environment size */ | 
|  | 61 | ulong erase_size;		/* device erase size */ | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 62 | ulong env_sectors;		/* number of environment sectors */ | 
|  | 63 | uint8_t mtd_type;		/* type of the MTD device */ | 
|  | 64 | }; | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 65 |  | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 66 | static struct envdev_s envdevices[2] = | 
|  | 67 | { | 
|  | 68 | { | 
|  | 69 | .mtd_type = MTD_ABSENT, | 
|  | 70 | }, { | 
|  | 71 | .mtd_type = MTD_ABSENT, | 
|  | 72 | }, | 
|  | 73 | }; | 
|  | 74 | static int dev_current; | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 75 |  | 
|  | 76 | #define DEVNAME(i)    envdevices[(i)].devname | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 77 | #define DEVOFFSET(i)  envdevices[(i)].devoff | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 78 | #define ENVSIZE(i)    envdevices[(i)].env_size | 
|  | 79 | #define DEVESIZE(i)   envdevices[(i)].erase_size | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 80 | #define ENVSECTORS(i) envdevices[(i)].env_sectors | 
|  | 81 | #define DEVTYPE(i)    envdevices[(i)].mtd_type | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 82 |  | 
| Jean-Christophe PLAGNIOL-VILLARD | 0e8d158 | 2008-09-10 22:48:06 +0200 | [diff] [blame] | 83 | #define CONFIG_ENV_SIZE ENVSIZE(dev_current) | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 84 |  | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 85 | #define ENV_SIZE      getenvsize() | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 86 |  | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 87 | struct env_image_single { | 
|  | 88 | uint32_t	crc;	/* CRC32 over data bytes    */ | 
|  | 89 | char		data[]; | 
|  | 90 | }; | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 91 |  | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 92 | struct env_image_redundant { | 
|  | 93 | uint32_t	crc;	/* CRC32 over data bytes    */ | 
|  | 94 | unsigned char	flags;	/* active or obsolete */ | 
|  | 95 | char		data[]; | 
|  | 96 | }; | 
|  | 97 |  | 
|  | 98 | enum flag_scheme { | 
|  | 99 | FLAG_NONE, | 
|  | 100 | FLAG_BOOLEAN, | 
|  | 101 | FLAG_INCREMENTAL, | 
|  | 102 | }; | 
|  | 103 |  | 
|  | 104 | struct environment { | 
|  | 105 | void			*image; | 
|  | 106 | uint32_t		*crc; | 
|  | 107 | unsigned char		*flags; | 
|  | 108 | char			*data; | 
|  | 109 | enum flag_scheme	flag_scheme; | 
|  | 110 | }; | 
|  | 111 |  | 
|  | 112 | static struct environment environment = { | 
|  | 113 | .flag_scheme = FLAG_NONE, | 
|  | 114 | }; | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 115 |  | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 116 | static int HaveRedundEnv = 0; | 
|  | 117 |  | 
| Markus Klotzbücher | 6de66b3 | 2007-11-27 10:23:20 +0100 | [diff] [blame] | 118 | static unsigned char active_flag = 1; | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 119 | /* obsolete_flag must be 0 to efficiently set it on NOR flash without erasing */ | 
| Markus Klotzbücher | 6de66b3 | 2007-11-27 10:23:20 +0100 | [diff] [blame] | 120 | static unsigned char obsolete_flag = 0; | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 121 |  | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 122 |  | 
|  | 123 | #define XMK_STR(x)	#x | 
|  | 124 | #define MK_STR(x)	XMK_STR(x) | 
|  | 125 |  | 
| Markus Klotzbücher | 6de66b3 | 2007-11-27 10:23:20 +0100 | [diff] [blame] | 126 | static char default_environment[] = { | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 127 | #if defined(CONFIG_BOOTARGS) | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 128 | "bootargs=" CONFIG_BOOTARGS "\0" | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 129 | #endif | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 130 | #if defined(CONFIG_BOOTCOMMAND) | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 131 | "bootcmd=" CONFIG_BOOTCOMMAND "\0" | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 132 | #endif | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 133 | #if defined(CONFIG_RAMBOOTCOMMAND) | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 134 | "ramboot=" CONFIG_RAMBOOTCOMMAND "\0" | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 135 | #endif | 
|  | 136 | #if defined(CONFIG_NFSBOOTCOMMAND) | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 137 | "nfsboot=" CONFIG_NFSBOOTCOMMAND "\0" | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 138 | #endif | 
|  | 139 | #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 140 | "bootdelay=" MK_STR (CONFIG_BOOTDELAY) "\0" | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 141 | #endif | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 142 | #if defined(CONFIG_BAUDRATE) && (CONFIG_BAUDRATE >= 0) | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 143 | "baudrate=" MK_STR (CONFIG_BAUDRATE) "\0" | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 144 | #endif | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 145 | #ifdef	CONFIG_LOADS_ECHO | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 146 | "loads_echo=" MK_STR (CONFIG_LOADS_ECHO) "\0" | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 147 | #endif | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 148 | #ifdef	CONFIG_ETHADDR | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 149 | "ethaddr=" MK_STR (CONFIG_ETHADDR) "\0" | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 150 | #endif | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 151 | #ifdef	CONFIG_ETH1ADDR | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 152 | "eth1addr=" MK_STR (CONFIG_ETH1ADDR) "\0" | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 153 | #endif | 
|  | 154 | #ifdef	CONFIG_ETH2ADDR | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 155 | "eth2addr=" MK_STR (CONFIG_ETH2ADDR) "\0" | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 156 | #endif | 
| wdenk | e2ffd59 | 2004-12-31 09:32:47 +0000 | [diff] [blame] | 157 | #ifdef	CONFIG_ETH3ADDR | 
|  | 158 | "eth3addr=" MK_STR (CONFIG_ETH3ADDR) "\0" | 
|  | 159 | #endif | 
| richardretanubun | c68a05f | 2008-09-29 18:28:23 -0400 | [diff] [blame] | 160 | #ifdef	CONFIG_ETH4ADDR | 
|  | 161 | "eth4addr=" MK_STR (CONFIG_ETH4ADDR) "\0" | 
|  | 162 | #endif | 
|  | 163 | #ifdef	CONFIG_ETH5ADDR | 
|  | 164 | "eth5addr=" MK_STR (CONFIG_ETH5ADDR) "\0" | 
|  | 165 | #endif | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 166 | #ifdef	CONFIG_ETHPRIME | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 167 | "ethprime=" CONFIG_ETHPRIME "\0" | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 168 | #endif | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 169 | #ifdef	CONFIG_IPADDR | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 170 | "ipaddr=" MK_STR (CONFIG_IPADDR) "\0" | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 171 | #endif | 
|  | 172 | #ifdef	CONFIG_SERVERIP | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 173 | "serverip=" MK_STR (CONFIG_SERVERIP) "\0" | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 174 | #endif | 
| Jean-Christophe PLAGNIOL-VILLARD | 6d0f6bc | 2008-10-16 15:01:15 +0200 | [diff] [blame^] | 175 | #ifdef	CONFIG_SYS_AUTOLOAD | 
|  | 176 | "autoload=" CONFIG_SYS_AUTOLOAD "\0" | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 177 | #endif | 
|  | 178 | #ifdef	CONFIG_ROOTPATH | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 179 | "rootpath=" MK_STR (CONFIG_ROOTPATH) "\0" | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 180 | #endif | 
|  | 181 | #ifdef	CONFIG_GATEWAYIP | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 182 | "gatewayip=" MK_STR (CONFIG_GATEWAYIP) "\0" | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 183 | #endif | 
|  | 184 | #ifdef	CONFIG_NETMASK | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 185 | "netmask=" MK_STR (CONFIG_NETMASK) "\0" | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 186 | #endif | 
|  | 187 | #ifdef	CONFIG_HOSTNAME | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 188 | "hostname=" MK_STR (CONFIG_HOSTNAME) "\0" | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 189 | #endif | 
|  | 190 | #ifdef	CONFIG_BOOTFILE | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 191 | "bootfile=" MK_STR (CONFIG_BOOTFILE) "\0" | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 192 | #endif | 
|  | 193 | #ifdef	CONFIG_LOADADDR | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 194 | "loadaddr=" MK_STR (CONFIG_LOADADDR) "\0" | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 195 | #endif | 
|  | 196 | #ifdef	CONFIG_PREBOOT | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 197 | "preboot=" CONFIG_PREBOOT "\0" | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 198 | #endif | 
|  | 199 | #ifdef	CONFIG_CLOCKS_IN_MHZ | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 200 | "clocks_in_mhz=" "1" "\0" | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 201 | #endif | 
| stroese | ad10dd9 | 2003-02-14 11:21:23 +0000 | [diff] [blame] | 202 | #if defined(CONFIG_PCI_BOOTDELAY) && (CONFIG_PCI_BOOTDELAY > 0) | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 203 | "pcidelay=" MK_STR (CONFIG_PCI_BOOTDELAY) "\0" | 
| stroese | ad10dd9 | 2003-02-14 11:21:23 +0000 | [diff] [blame] | 204 | #endif | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 205 | #ifdef  CONFIG_EXTRA_ENV_SETTINGS | 
|  | 206 | CONFIG_EXTRA_ENV_SETTINGS | 
|  | 207 | #endif | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 208 | "\0"		/* Termimate struct environment data with 2 NULs */ | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 209 | }; | 
|  | 210 |  | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 211 | static int flash_io (int mode); | 
| Markus Klotzbücher | 6de66b3 | 2007-11-27 10:23:20 +0100 | [diff] [blame] | 212 | static char *envmatch (char * s1, char * s2); | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 213 | static int env_init (void); | 
|  | 214 | static int parse_config (void); | 
|  | 215 |  | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 216 | #if defined(CONFIG_FILE) | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 217 | static int get_config (char *); | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 218 | #endif | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 219 | static inline ulong getenvsize (void) | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 220 | { | 
| Jean-Christophe PLAGNIOL-VILLARD | 0e8d158 | 2008-09-10 22:48:06 +0200 | [diff] [blame] | 221 | ulong rc = CONFIG_ENV_SIZE - sizeof (long); | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 222 |  | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 223 | if (HaveRedundEnv) | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 224 | rc -= sizeof (char); | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 225 | return rc; | 
|  | 226 | } | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 227 |  | 
|  | 228 | /* | 
|  | 229 | * Search the environment for a variable. | 
|  | 230 | * Return the value, if found, or NULL, if not found. | 
|  | 231 | */ | 
| Markus Klotzbücher | 6de66b3 | 2007-11-27 10:23:20 +0100 | [diff] [blame] | 232 | char *fw_getenv (char *name) | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 233 | { | 
| Markus Klotzbücher | 6de66b3 | 2007-11-27 10:23:20 +0100 | [diff] [blame] | 234 | char *env, *nxt; | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 235 |  | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 236 | if (env_init ()) | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 237 | return NULL; | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 238 |  | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 239 | for (env = environment.data; *env; env = nxt + 1) { | 
| Markus Klotzbücher | 6de66b3 | 2007-11-27 10:23:20 +0100 | [diff] [blame] | 240 | char *val; | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 241 |  | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 242 | for (nxt = env; *nxt; ++nxt) { | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 243 | if (nxt >= &environment.data[ENV_SIZE]) { | 
|  | 244 | fprintf (stderr, "## Error: " | 
|  | 245 | "environment not terminated\n"); | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 246 | return NULL; | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 247 | } | 
|  | 248 | } | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 249 | val = envmatch (name, env); | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 250 | if (!val) | 
|  | 251 | continue; | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 252 | return val; | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 253 | } | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 254 | return NULL; | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 255 | } | 
|  | 256 |  | 
|  | 257 | /* | 
|  | 258 | * Print the current definition of one, or more, or all | 
|  | 259 | * environment variables | 
|  | 260 | */ | 
| Grant Erickson | bc11756 | 2008-05-06 20:16:15 -0700 | [diff] [blame] | 261 | int fw_printenv (int argc, char *argv[]) | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 262 | { | 
| Markus Klotzbücher | 6de66b3 | 2007-11-27 10:23:20 +0100 | [diff] [blame] | 263 | char *env, *nxt; | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 264 | int i, n_flag; | 
| Grant Erickson | bc11756 | 2008-05-06 20:16:15 -0700 | [diff] [blame] | 265 | int rc = 0; | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 266 |  | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 267 | if (env_init ()) | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 268 | return -1; | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 269 |  | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 270 | if (argc == 1) {		/* Print all env variables  */ | 
|  | 271 | for (env = environment.data; *env; env = nxt + 1) { | 
|  | 272 | for (nxt = env; *nxt; ++nxt) { | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 273 | if (nxt >= &environment.data[ENV_SIZE]) { | 
|  | 274 | fprintf (stderr, "## Error: " | 
|  | 275 | "environment not terminated\n"); | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 276 | return -1; | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 277 | } | 
|  | 278 | } | 
|  | 279 |  | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 280 | printf ("%s\n", env); | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 281 | } | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 282 | return 0; | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 283 | } | 
|  | 284 |  | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 285 | if (strcmp (argv[1], "-n") == 0) { | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 286 | n_flag = 1; | 
|  | 287 | ++argv; | 
|  | 288 | --argc; | 
|  | 289 | if (argc != 2) { | 
|  | 290 | fprintf (stderr, "## Error: " | 
|  | 291 | "`-n' option requires exactly one argument\n"); | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 292 | return -1; | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 293 | } | 
|  | 294 | } else { | 
|  | 295 | n_flag = 0; | 
|  | 296 | } | 
|  | 297 |  | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 298 | for (i = 1; i < argc; ++i) {	/* print single env variables   */ | 
| Markus Klotzbücher | 6de66b3 | 2007-11-27 10:23:20 +0100 | [diff] [blame] | 299 | char *name = argv[i]; | 
|  | 300 | char *val = NULL; | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 301 |  | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 302 | for (env = environment.data; *env; env = nxt + 1) { | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 303 |  | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 304 | for (nxt = env; *nxt; ++nxt) { | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 305 | if (nxt >= &environment.data[ENV_SIZE]) { | 
|  | 306 | fprintf (stderr, "## Error: " | 
|  | 307 | "environment not terminated\n"); | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 308 | return -1; | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 309 | } | 
|  | 310 | } | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 311 | val = envmatch (name, env); | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 312 | if (val) { | 
|  | 313 | if (!n_flag) { | 
|  | 314 | fputs (name, stdout); | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 315 | putc ('=', stdout); | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 316 | } | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 317 | puts (val); | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 318 | break; | 
|  | 319 | } | 
|  | 320 | } | 
| Grant Erickson | bc11756 | 2008-05-06 20:16:15 -0700 | [diff] [blame] | 321 | if (!val) { | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 322 | fprintf (stderr, "## Error: \"%s\" not defined\n", name); | 
| Grant Erickson | bc11756 | 2008-05-06 20:16:15 -0700 | [diff] [blame] | 323 | rc = -1; | 
|  | 324 | } | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 325 | } | 
| Grant Erickson | bc11756 | 2008-05-06 20:16:15 -0700 | [diff] [blame] | 326 |  | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 327 | return rc; | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 328 | } | 
|  | 329 |  | 
|  | 330 | /* | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 331 | * Deletes or sets environment variables. Returns -1 and sets errno error codes: | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 332 | * 0	  - OK | 
|  | 333 | * EINVAL - need at least 1 argument | 
|  | 334 | * EROFS  - certain variables ("ethaddr", "serial#") cannot be | 
|  | 335 | *	    modified or deleted | 
|  | 336 | * | 
|  | 337 | */ | 
|  | 338 | int fw_setenv (int argc, char *argv[]) | 
|  | 339 | { | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 340 | int i, len; | 
| Markus Klotzbücher | 6de66b3 | 2007-11-27 10:23:20 +0100 | [diff] [blame] | 341 | char *env, *nxt; | 
|  | 342 | char *oldval = NULL; | 
|  | 343 | char *name; | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 344 |  | 
|  | 345 | if (argc < 2) { | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 346 | errno = EINVAL; | 
|  | 347 | return -1; | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 348 | } | 
|  | 349 |  | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 350 | if (env_init ()) | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 351 | return -1; | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 352 |  | 
|  | 353 | name = argv[1]; | 
|  | 354 |  | 
|  | 355 | /* | 
|  | 356 | * search if variable with this name already exists | 
|  | 357 | */ | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 358 | for (nxt = env = environment.data; *env; env = nxt + 1) { | 
|  | 359 | for (nxt = env; *nxt; ++nxt) { | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 360 | if (nxt >= &environment.data[ENV_SIZE]) { | 
|  | 361 | fprintf (stderr, "## Error: " | 
|  | 362 | "environment not terminated\n"); | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 363 | errno = EINVAL; | 
|  | 364 | return -1; | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 365 | } | 
|  | 366 | } | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 367 | if ((oldval = envmatch (name, env)) != NULL) | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 368 | break; | 
|  | 369 | } | 
|  | 370 |  | 
|  | 371 | /* | 
|  | 372 | * Delete any existing definition | 
|  | 373 | */ | 
|  | 374 | if (oldval) { | 
|  | 375 | /* | 
|  | 376 | * Ethernet Address and serial# can be set only once | 
|  | 377 | */ | 
|  | 378 | if ((strcmp (name, "ethaddr") == 0) || | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 379 | (strcmp (name, "serial#") == 0)) { | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 380 | fprintf (stderr, "Can't overwrite \"%s\"\n", name); | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 381 | errno = EROFS; | 
|  | 382 | return -1; | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 383 | } | 
|  | 384 |  | 
|  | 385 | if (*++nxt == '\0') { | 
|  | 386 | *env = '\0'; | 
|  | 387 | } else { | 
|  | 388 | for (;;) { | 
|  | 389 | *env = *nxt++; | 
|  | 390 | if ((*env == '\0') && (*nxt == '\0')) | 
|  | 391 | break; | 
|  | 392 | ++env; | 
|  | 393 | } | 
|  | 394 | } | 
|  | 395 | *++env = '\0'; | 
|  | 396 | } | 
|  | 397 |  | 
|  | 398 | /* Delete only ? */ | 
|  | 399 | if (argc < 3) | 
|  | 400 | goto WRITE_FLASH; | 
|  | 401 |  | 
|  | 402 | /* | 
|  | 403 | * Append new definition at the end | 
|  | 404 | */ | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 405 | for (env = environment.data; *env || *(env + 1); ++env); | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 406 | if (env > environment.data) | 
|  | 407 | ++env; | 
|  | 408 | /* | 
|  | 409 | * Overflow when: | 
| Jean-Christophe PLAGNIOL-VILLARD | 0e8d158 | 2008-09-10 22:48:06 +0200 | [diff] [blame] | 410 | * "name" + "=" + "val" +"\0\0"  > CONFIG_ENV_SIZE - (env-environment) | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 411 | */ | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 412 | len = strlen (name) + 2; | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 413 | /* add '=' for first arg, ' ' for all others */ | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 414 | for (i = 2; i < argc; ++i) { | 
|  | 415 | len += strlen (argv[i]) + 1; | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 416 | } | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 417 | if (len > (&environment.data[ENV_SIZE] - env)) { | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 418 | fprintf (stderr, | 
|  | 419 | "Error: environment overflow, \"%s\" deleted\n", | 
|  | 420 | name); | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 421 | return -1; | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 422 | } | 
|  | 423 | while ((*env = *name++) != '\0') | 
|  | 424 | env++; | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 425 | for (i = 2; i < argc; ++i) { | 
| Markus Klotzbücher | 6de66b3 | 2007-11-27 10:23:20 +0100 | [diff] [blame] | 426 | char *val = argv[i]; | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 427 |  | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 428 | *env = (i == 2) ? '=' : ' '; | 
|  | 429 | while ((*++env = *val++) != '\0'); | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 430 | } | 
|  | 431 |  | 
|  | 432 | /* end is marked with double '\0' */ | 
|  | 433 | *++env = '\0'; | 
|  | 434 |  | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 435 | WRITE_FLASH: | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 436 |  | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 437 | /* | 
|  | 438 | * Update CRC | 
|  | 439 | */ | 
|  | 440 | *environment.crc = crc32 (0, (uint8_t *) environment.data, ENV_SIZE); | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 441 |  | 
|  | 442 | /* write environment back to flash */ | 
|  | 443 | if (flash_io (O_RDWR)) { | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 444 | fprintf (stderr, "Error: can't write fw_env to flash\n"); | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 445 | return -1; | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 446 | } | 
|  | 447 |  | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 448 | return 0; | 
|  | 449 | } | 
|  | 450 |  | 
|  | 451 | /* | 
|  | 452 | * Test for bad block on NAND, just returns 0 on NOR, on NAND: | 
|  | 453 | * 0	- block is good | 
|  | 454 | * > 0	- block is bad | 
|  | 455 | * < 0	- failed to test | 
|  | 456 | */ | 
|  | 457 | static int flash_bad_block (int fd, uint8_t mtd_type, loff_t *blockstart) | 
|  | 458 | { | 
|  | 459 | if (mtd_type == MTD_NANDFLASH) { | 
|  | 460 | int badblock = ioctl (fd, MEMGETBADBLOCK, blockstart); | 
|  | 461 |  | 
|  | 462 | if (badblock < 0) { | 
|  | 463 | perror ("Cannot read bad block mark"); | 
|  | 464 | return badblock; | 
|  | 465 | } | 
|  | 466 |  | 
|  | 467 | if (badblock) { | 
|  | 468 | #ifdef DEBUG | 
|  | 469 | fprintf (stderr, "Bad block at 0x%llx, " | 
|  | 470 | "skipping\n", *blockstart); | 
|  | 471 | #endif | 
|  | 472 | return badblock; | 
|  | 473 | } | 
|  | 474 | } | 
|  | 475 |  | 
|  | 476 | return 0; | 
|  | 477 | } | 
|  | 478 |  | 
|  | 479 | /* | 
|  | 480 | * Read data from flash at an offset into a provided buffer. On NAND it skips | 
|  | 481 | * bad blocks but makes sure it stays within ENVSECTORS (dev) starting from | 
|  | 482 | * the DEVOFFSET (dev) block. On NOR the loop is only run once. | 
|  | 483 | */ | 
|  | 484 | static int flash_read_buf (int dev, int fd, void *buf, size_t count, | 
|  | 485 | off_t offset, uint8_t mtd_type) | 
|  | 486 | { | 
|  | 487 | size_t blocklen;	/* erase / write length - one block on NAND, | 
|  | 488 | 0 on NOR */ | 
|  | 489 | size_t processed = 0;	/* progress counter */ | 
|  | 490 | size_t readlen = count;	/* current read length */ | 
|  | 491 | off_t top_of_range;	/* end of the last block we may use */ | 
|  | 492 | off_t block_seek;	/* offset inside the current block to the start | 
|  | 493 | of the data */ | 
|  | 494 | loff_t blockstart;	/* running start of the current block - | 
|  | 495 | MEMGETBADBLOCK needs 64 bits */ | 
|  | 496 | int rc; | 
|  | 497 |  | 
|  | 498 | /* | 
|  | 499 | * Start of the first block to be read, relies on the fact, that | 
|  | 500 | * erase sector size is always a power of 2 | 
|  | 501 | */ | 
|  | 502 | blockstart = offset & ~(DEVESIZE (dev) - 1); | 
|  | 503 |  | 
|  | 504 | /* Offset inside a block */ | 
|  | 505 | block_seek = offset - blockstart; | 
|  | 506 |  | 
|  | 507 | if (mtd_type == MTD_NANDFLASH) { | 
|  | 508 | /* | 
|  | 509 | * NAND: calculate which blocks we are reading. We have | 
|  | 510 | * to read one block at a time to skip bad blocks. | 
|  | 511 | */ | 
|  | 512 | blocklen = DEVESIZE (dev); | 
|  | 513 |  | 
|  | 514 | /* | 
|  | 515 | * To calculate the top of the range, we have to use the | 
|  | 516 | * global DEVOFFSET (dev), which can be different from offset | 
|  | 517 | */ | 
|  | 518 | top_of_range = (DEVOFFSET (dev) & ~(blocklen - 1)) + | 
|  | 519 | ENVSECTORS (dev) * blocklen; | 
|  | 520 |  | 
|  | 521 | /* Limit to one block for the first read */ | 
|  | 522 | if (readlen > blocklen - block_seek) | 
|  | 523 | readlen = blocklen - block_seek; | 
|  | 524 | } else { | 
|  | 525 | blocklen = 0; | 
|  | 526 | top_of_range = offset + count; | 
|  | 527 | } | 
|  | 528 |  | 
|  | 529 | /* This only runs once on NOR flash */ | 
|  | 530 | while (processed < count) { | 
|  | 531 | rc = flash_bad_block (fd, mtd_type, &blockstart); | 
|  | 532 | if (rc < 0)		/* block test failed */ | 
|  | 533 | return -1; | 
|  | 534 |  | 
|  | 535 | if (blockstart + block_seek + readlen > top_of_range) { | 
|  | 536 | /* End of range is reached */ | 
|  | 537 | fprintf (stderr, | 
|  | 538 | "Too few good blocks within range\n"); | 
|  | 539 | return -1; | 
|  | 540 | } | 
|  | 541 |  | 
|  | 542 | if (rc) {		/* block is bad */ | 
|  | 543 | blockstart += blocklen; | 
|  | 544 | continue; | 
|  | 545 | } | 
|  | 546 |  | 
|  | 547 | /* | 
|  | 548 | * If a block is bad, we retry in the next block at the same | 
|  | 549 | * offset - see common/env_nand.c::writeenv() | 
|  | 550 | */ | 
|  | 551 | lseek (fd, blockstart + block_seek, SEEK_SET); | 
|  | 552 |  | 
|  | 553 | rc = read (fd, buf + processed, readlen); | 
|  | 554 | if (rc != readlen) { | 
|  | 555 | fprintf (stderr, "Read error on %s: %s\n", | 
|  | 556 | DEVNAME (dev), strerror (errno)); | 
|  | 557 | return -1; | 
|  | 558 | } | 
|  | 559 | #ifdef DEBUG | 
|  | 560 | fprintf (stderr, "Read 0x%x bytes at 0x%llx\n", | 
|  | 561 | rc, blockstart + block_seek); | 
|  | 562 | #endif | 
|  | 563 | processed += readlen; | 
|  | 564 | readlen = min (blocklen, count - processed); | 
|  | 565 | block_seek = 0; | 
|  | 566 | blockstart += blocklen; | 
|  | 567 | } | 
|  | 568 |  | 
|  | 569 | return processed; | 
|  | 570 | } | 
|  | 571 |  | 
|  | 572 | /* | 
|  | 573 | * Write count bytes at offset, but stay within ENVSETCORS (dev) sectors of | 
|  | 574 | * DEVOFFSET (dev). Similar to the read case above, on NOR we erase and write | 
|  | 575 | * the whole data at once. | 
|  | 576 | */ | 
|  | 577 | static int flash_write_buf (int dev, int fd, void *buf, size_t count, | 
|  | 578 | off_t offset, uint8_t mtd_type) | 
|  | 579 | { | 
|  | 580 | void *data; | 
|  | 581 | struct erase_info_user erase; | 
|  | 582 | size_t blocklen;	/* length of NAND block / NOR erase sector */ | 
|  | 583 | size_t erase_len;	/* whole area that can be erased - may include | 
|  | 584 | bad blocks */ | 
|  | 585 | size_t erasesize;	/* erase / write length - one block on NAND, | 
|  | 586 | whole area on NOR */ | 
|  | 587 | size_t processed = 0;	/* progress counter */ | 
|  | 588 | size_t write_total;	/* total size to actually write - excludinig | 
|  | 589 | bad blocks */ | 
|  | 590 | off_t erase_offset;	/* offset to the first erase block (aligned) | 
|  | 591 | below offset */ | 
|  | 592 | off_t block_seek;	/* offset inside the erase block to the start | 
|  | 593 | of the data */ | 
|  | 594 | off_t top_of_range;	/* end of the last block we may use */ | 
|  | 595 | loff_t blockstart;	/* running start of the current block - | 
|  | 596 | MEMGETBADBLOCK needs 64 bits */ | 
|  | 597 | int rc; | 
|  | 598 |  | 
|  | 599 | blocklen = DEVESIZE (dev); | 
|  | 600 |  | 
|  | 601 | /* Erase sector size is always a power of 2 */ | 
|  | 602 | top_of_range = (DEVOFFSET (dev) & ~(blocklen - 1)) + | 
|  | 603 | ENVSECTORS (dev) * blocklen; | 
|  | 604 |  | 
|  | 605 | erase_offset = offset & ~(blocklen - 1); | 
|  | 606 |  | 
|  | 607 | /* Maximum area we may use */ | 
|  | 608 | erase_len = top_of_range - erase_offset; | 
|  | 609 |  | 
|  | 610 | blockstart = erase_offset; | 
|  | 611 | /* Offset inside a block */ | 
|  | 612 | block_seek = offset - erase_offset; | 
|  | 613 |  | 
|  | 614 | /* | 
|  | 615 | * Data size we actually have to write: from the start of the block | 
|  | 616 | * to the start of the data, then count bytes of data, and to the | 
|  | 617 | * end of the block | 
|  | 618 | */ | 
|  | 619 | write_total = (block_seek + count + blocklen - 1) & ~(blocklen - 1); | 
|  | 620 |  | 
|  | 621 | /* | 
|  | 622 | * Support data anywhere within erase sectors: read out the complete | 
|  | 623 | * area to be erased, replace the environment image, write the whole | 
|  | 624 | * block back again. | 
|  | 625 | */ | 
|  | 626 | if (write_total > count) { | 
|  | 627 | data = malloc (erase_len); | 
|  | 628 | if (!data) { | 
|  | 629 | fprintf (stderr, | 
|  | 630 | "Cannot malloc %u bytes: %s\n", | 
|  | 631 | erase_len, strerror (errno)); | 
|  | 632 | return -1; | 
|  | 633 | } | 
|  | 634 |  | 
|  | 635 | rc = flash_read_buf (dev, fd, data, write_total, erase_offset, | 
|  | 636 | mtd_type); | 
|  | 637 | if (write_total != rc) | 
|  | 638 | return -1; | 
|  | 639 |  | 
|  | 640 | /* Overwrite the old environment */ | 
|  | 641 | memcpy (data + block_seek, buf, count); | 
|  | 642 | } else { | 
|  | 643 | /* | 
|  | 644 | * We get here, iff offset is block-aligned and count is a | 
|  | 645 | * multiple of blocklen - see write_total calculation above | 
|  | 646 | */ | 
|  | 647 | data = buf; | 
|  | 648 | } | 
|  | 649 |  | 
|  | 650 | if (mtd_type == MTD_NANDFLASH) { | 
|  | 651 | /* | 
|  | 652 | * NAND: calculate which blocks we are writing. We have | 
|  | 653 | * to write one block at a time to skip bad blocks. | 
|  | 654 | */ | 
|  | 655 | erasesize = blocklen; | 
|  | 656 | } else { | 
|  | 657 | erasesize = erase_len; | 
|  | 658 | } | 
|  | 659 |  | 
|  | 660 | erase.length = erasesize; | 
|  | 661 |  | 
|  | 662 | /* This only runs once on NOR flash */ | 
|  | 663 | while (processed < write_total) { | 
|  | 664 | rc = flash_bad_block (fd, mtd_type, &blockstart); | 
|  | 665 | if (rc < 0)		/* block test failed */ | 
|  | 666 | return rc; | 
|  | 667 |  | 
|  | 668 | if (blockstart + erasesize > top_of_range) { | 
|  | 669 | fprintf (stderr, "End of range reached, aborting\n"); | 
|  | 670 | return -1; | 
|  | 671 | } | 
|  | 672 |  | 
|  | 673 | if (rc) {		/* block is bad */ | 
|  | 674 | blockstart += blocklen; | 
|  | 675 | continue; | 
|  | 676 | } | 
|  | 677 |  | 
|  | 678 | erase.start = blockstart; | 
|  | 679 | ioctl (fd, MEMUNLOCK, &erase); | 
|  | 680 |  | 
|  | 681 | if (ioctl (fd, MEMERASE, &erase) != 0) { | 
|  | 682 | fprintf (stderr, "MTD erase error on %s: %s\n", | 
|  | 683 | DEVNAME (dev), | 
|  | 684 | strerror (errno)); | 
|  | 685 | return -1; | 
|  | 686 | } | 
|  | 687 |  | 
|  | 688 | if (lseek (fd, blockstart, SEEK_SET) == -1) { | 
|  | 689 | fprintf (stderr, | 
|  | 690 | "Seek error on %s: %s\n", | 
|  | 691 | DEVNAME (dev), strerror (errno)); | 
|  | 692 | return -1; | 
|  | 693 | } | 
|  | 694 |  | 
|  | 695 | #ifdef DEBUG | 
|  | 696 | printf ("Write 0x%x bytes at 0x%llx\n", erasesize, blockstart); | 
|  | 697 | #endif | 
|  | 698 | if (write (fd, data + processed, erasesize) != erasesize) { | 
|  | 699 | fprintf (stderr, "Write error on %s: %s\n", | 
|  | 700 | DEVNAME (dev), strerror (errno)); | 
|  | 701 | return -1; | 
|  | 702 | } | 
|  | 703 |  | 
|  | 704 | ioctl (fd, MEMLOCK, &erase); | 
|  | 705 |  | 
|  | 706 | processed  += blocklen; | 
|  | 707 | block_seek = 0; | 
|  | 708 | blockstart += blocklen; | 
|  | 709 | } | 
|  | 710 |  | 
|  | 711 | if (write_total > count) | 
|  | 712 | free (data); | 
|  | 713 |  | 
|  | 714 | return processed; | 
|  | 715 | } | 
|  | 716 |  | 
|  | 717 | /* | 
|  | 718 | * Set obsolete flag at offset - NOR flash only | 
|  | 719 | */ | 
|  | 720 | static int flash_flag_obsolete (int dev, int fd, off_t offset) | 
|  | 721 | { | 
|  | 722 | int rc; | 
|  | 723 |  | 
|  | 724 | /* This relies on the fact, that obsolete_flag == 0 */ | 
|  | 725 | rc = lseek (fd, offset, SEEK_SET); | 
|  | 726 | if (rc < 0) { | 
|  | 727 | fprintf (stderr, "Cannot seek to set the flag on %s \n", | 
|  | 728 | DEVNAME (dev)); | 
|  | 729 | return rc; | 
|  | 730 | } | 
|  | 731 | rc = write (fd, &obsolete_flag, sizeof (obsolete_flag)); | 
|  | 732 | if (rc < 0) | 
|  | 733 | perror ("Could not set obsolete flag"); | 
|  | 734 |  | 
|  | 735 | return rc; | 
|  | 736 | } | 
|  | 737 |  | 
|  | 738 | static int flash_write (int fd_current, int fd_target, int dev_target) | 
|  | 739 | { | 
|  | 740 | int rc; | 
|  | 741 |  | 
|  | 742 | switch (environment.flag_scheme) { | 
|  | 743 | case FLAG_NONE: | 
|  | 744 | break; | 
|  | 745 | case FLAG_INCREMENTAL: | 
|  | 746 | (*environment.flags)++; | 
|  | 747 | break; | 
|  | 748 | case FLAG_BOOLEAN: | 
|  | 749 | *environment.flags = active_flag; | 
|  | 750 | break; | 
|  | 751 | default: | 
|  | 752 | fprintf (stderr, "Unimplemented flash scheme %u \n", | 
|  | 753 | environment.flag_scheme); | 
|  | 754 | return -1; | 
|  | 755 | } | 
|  | 756 |  | 
|  | 757 | #ifdef DEBUG | 
|  | 758 | printf ("Writing new environment at 0x%lx on %s\n", | 
|  | 759 | DEVOFFSET (dev_target), DEVNAME (dev_target)); | 
|  | 760 | #endif | 
|  | 761 | rc = flash_write_buf (dev_target, fd_target, environment.image, | 
| Jean-Christophe PLAGNIOL-VILLARD | 0e8d158 | 2008-09-10 22:48:06 +0200 | [diff] [blame] | 762 | CONFIG_ENV_SIZE, DEVOFFSET (dev_target), | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 763 | DEVTYPE(dev_target)); | 
|  | 764 | if (rc < 0) | 
|  | 765 | return rc; | 
|  | 766 |  | 
|  | 767 | if (environment.flag_scheme == FLAG_BOOLEAN) { | 
|  | 768 | /* Have to set obsolete flag */ | 
|  | 769 | off_t offset = DEVOFFSET (dev_current) + | 
|  | 770 | offsetof (struct env_image_redundant, flags); | 
|  | 771 | #ifdef DEBUG | 
|  | 772 | printf ("Setting obsolete flag in environment at 0x%lx on %s\n", | 
|  | 773 | DEVOFFSET (dev_current), DEVNAME (dev_current)); | 
|  | 774 | #endif | 
|  | 775 | flash_flag_obsolete (dev_current, fd_current, offset); | 
|  | 776 | } | 
|  | 777 |  | 
|  | 778 | return 0; | 
|  | 779 | } | 
|  | 780 |  | 
|  | 781 | static int flash_read (int fd) | 
|  | 782 | { | 
|  | 783 | struct mtd_info_user mtdinfo; | 
|  | 784 | int rc; | 
|  | 785 |  | 
|  | 786 | rc = ioctl (fd, MEMGETINFO, &mtdinfo); | 
|  | 787 | if (rc < 0) { | 
|  | 788 | perror ("Cannot get MTD information"); | 
|  | 789 | return -1; | 
|  | 790 | } | 
|  | 791 |  | 
|  | 792 | if (mtdinfo.type != MTD_NORFLASH && mtdinfo.type != MTD_NANDFLASH) { | 
|  | 793 | fprintf (stderr, "Unsupported flash type %u\n", mtdinfo.type); | 
|  | 794 | return -1; | 
|  | 795 | } | 
|  | 796 |  | 
|  | 797 | DEVTYPE(dev_current) = mtdinfo.type; | 
|  | 798 |  | 
| Jean-Christophe PLAGNIOL-VILLARD | 0e8d158 | 2008-09-10 22:48:06 +0200 | [diff] [blame] | 799 | rc = flash_read_buf (dev_current, fd, environment.image, CONFIG_ENV_SIZE, | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 800 | DEVOFFSET (dev_current), mtdinfo.type); | 
|  | 801 |  | 
| Jean-Christophe PLAGNIOL-VILLARD | 0e8d158 | 2008-09-10 22:48:06 +0200 | [diff] [blame] | 802 | return (rc != CONFIG_ENV_SIZE) ? -1 : 0; | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 803 | } | 
|  | 804 |  | 
|  | 805 | static int flash_io (int mode) | 
|  | 806 | { | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 807 | int fd_current, fd_target, rc, dev_target; | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 808 |  | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 809 | /* dev_current: fd_current, erase_current */ | 
|  | 810 | fd_current = open (DEVNAME (dev_current), mode); | 
|  | 811 | if (fd_current < 0) { | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 812 | fprintf (stderr, | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 813 | "Can't open %s: %s\n", | 
|  | 814 | DEVNAME (dev_current), strerror (errno)); | 
|  | 815 | return -1; | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 816 | } | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 817 |  | 
|  | 818 | if (mode == O_RDWR) { | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 819 | if (HaveRedundEnv) { | 
|  | 820 | /* switch to next partition for writing */ | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 821 | dev_target = !dev_current; | 
|  | 822 | /* dev_target: fd_target, erase_target */ | 
|  | 823 | fd_target = open (DEVNAME (dev_target), mode); | 
|  | 824 | if (fd_target < 0) { | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 825 | fprintf (stderr, | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 826 | "Can't open %s: %s\n", | 
|  | 827 | DEVNAME (dev_target), | 
|  | 828 | strerror (errno)); | 
|  | 829 | rc = -1; | 
|  | 830 | goto exit; | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 831 | } | 
|  | 832 | } else { | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 833 | dev_target = dev_current; | 
|  | 834 | fd_target = fd_current; | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 835 | } | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 836 |  | 
|  | 837 | rc = flash_write (fd_current, fd_target, dev_target); | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 838 |  | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 839 | if (HaveRedundEnv) { | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 840 | if (close (fd_target)) { | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 841 | fprintf (stderr, | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 842 | "I/O error on %s: %s\n", | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 843 | DEVNAME (dev_target), | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 844 | strerror (errno)); | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 845 | rc = -1; | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 846 | } | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 847 | } | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 848 | } else { | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 849 | rc = flash_read (fd_current); | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 850 | } | 
|  | 851 |  | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 852 | exit: | 
|  | 853 | if (close (fd_current)) { | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 854 | fprintf (stderr, | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 855 | "I/O error on %s: %s\n", | 
|  | 856 | DEVNAME (dev_current), strerror (errno)); | 
|  | 857 | return -1; | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 858 | } | 
|  | 859 |  | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 860 | return rc; | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 861 | } | 
|  | 862 |  | 
|  | 863 | /* | 
|  | 864 | * s1 is either a simple 'name', or a 'name=value' pair. | 
|  | 865 | * s2 is a 'name=value' pair. | 
|  | 866 | * If the names match, return the value of s2, else NULL. | 
|  | 867 | */ | 
|  | 868 |  | 
| Markus Klotzbücher | 6de66b3 | 2007-11-27 10:23:20 +0100 | [diff] [blame] | 869 | static char *envmatch (char * s1, char * s2) | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 870 | { | 
|  | 871 |  | 
|  | 872 | while (*s1 == *s2++) | 
|  | 873 | if (*s1++ == '=') | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 874 | return s2; | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 875 | if (*s1 == '\0' && *(s2 - 1) == '=') | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 876 | return s2; | 
|  | 877 | return NULL; | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 878 | } | 
|  | 879 |  | 
|  | 880 | /* | 
|  | 881 | * Prevent confusion if running from erased flash memory | 
|  | 882 | */ | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 883 | static int env_init (void) | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 884 | { | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 885 | int crc0, crc0_ok; | 
|  | 886 | char flag0; | 
|  | 887 | void *addr0; | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 888 |  | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 889 | int crc1, crc1_ok; | 
|  | 890 | char flag1; | 
|  | 891 | void *addr1; | 
|  | 892 |  | 
|  | 893 | struct env_image_single *single; | 
|  | 894 | struct env_image_redundant *redundant; | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 895 |  | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 896 | if (parse_config ())		/* should fill envdevices */ | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 897 | return -1; | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 898 |  | 
| Jean-Christophe PLAGNIOL-VILLARD | 0e8d158 | 2008-09-10 22:48:06 +0200 | [diff] [blame] | 899 | addr0 = calloc (1, CONFIG_ENV_SIZE); | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 900 | if (addr0 == NULL) { | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 901 | fprintf (stderr, | 
|  | 902 | "Not enough memory for environment (%ld bytes)\n", | 
| Jean-Christophe PLAGNIOL-VILLARD | 0e8d158 | 2008-09-10 22:48:06 +0200 | [diff] [blame] | 903 | CONFIG_ENV_SIZE); | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 904 | return -1; | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 905 | } | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 906 |  | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 907 | /* read environment from FLASH to local buffer */ | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 908 | environment.image = addr0; | 
|  | 909 |  | 
|  | 910 | if (HaveRedundEnv) { | 
|  | 911 | redundant = addr0; | 
|  | 912 | environment.crc		= &redundant->crc; | 
|  | 913 | environment.flags	= &redundant->flags; | 
|  | 914 | environment.data	= redundant->data; | 
|  | 915 | } else { | 
|  | 916 | single = addr0; | 
|  | 917 | environment.crc		= &single->crc; | 
|  | 918 | environment.flags	= NULL; | 
|  | 919 | environment.data	= single->data; | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 920 | } | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 921 |  | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 922 | dev_current = 0; | 
|  | 923 | if (flash_io (O_RDONLY)) | 
|  | 924 | return -1; | 
|  | 925 |  | 
|  | 926 | crc0 = crc32 (0, (uint8_t *) environment.data, ENV_SIZE); | 
|  | 927 | crc0_ok = (crc0 == *environment.crc); | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 928 | if (!HaveRedundEnv) { | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 929 | if (!crc0_ok) { | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 930 | fprintf (stderr, | 
|  | 931 | "Warning: Bad CRC, using default environment\n"); | 
| Wolfgang Denk | f07217c | 2006-03-12 23:27:46 +0100 | [diff] [blame] | 932 | memcpy(environment.data, default_environment, sizeof default_environment); | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 933 | } | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 934 | } else { | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 935 | flag0 = *environment.flags; | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 936 |  | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 937 | dev_current = 1; | 
| Jean-Christophe PLAGNIOL-VILLARD | 0e8d158 | 2008-09-10 22:48:06 +0200 | [diff] [blame] | 938 | addr1 = calloc (1, CONFIG_ENV_SIZE); | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 939 | if (addr1 == NULL) { | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 940 | fprintf (stderr, | 
|  | 941 | "Not enough memory for environment (%ld bytes)\n", | 
| Jean-Christophe PLAGNIOL-VILLARD | 0e8d158 | 2008-09-10 22:48:06 +0200 | [diff] [blame] | 942 | CONFIG_ENV_SIZE); | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 943 | return -1; | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 944 | } | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 945 | redundant = addr1; | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 946 |  | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 947 | /* | 
|  | 948 | * have to set environment.image for flash_read(), careful - | 
|  | 949 | * other pointers in environment still point inside addr0 | 
|  | 950 | */ | 
|  | 951 | environment.image = addr1; | 
|  | 952 | if (flash_io (O_RDONLY)) | 
|  | 953 | return -1; | 
|  | 954 |  | 
|  | 955 | /* Check flag scheme compatibility */ | 
|  | 956 | if (DEVTYPE(dev_current) == MTD_NORFLASH && | 
|  | 957 | DEVTYPE(!dev_current) == MTD_NORFLASH) { | 
|  | 958 | environment.flag_scheme = FLAG_BOOLEAN; | 
|  | 959 | } else if (DEVTYPE(dev_current) == MTD_NANDFLASH && | 
|  | 960 | DEVTYPE(!dev_current) == MTD_NANDFLASH) { | 
|  | 961 | environment.flag_scheme = FLAG_INCREMENTAL; | 
|  | 962 | } else { | 
|  | 963 | fprintf (stderr, "Incompatible flash types!\n"); | 
|  | 964 | return -1; | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 965 | } | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 966 |  | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 967 | crc1 = crc32 (0, (uint8_t *) redundant->data, ENV_SIZE); | 
|  | 968 | crc1_ok = (crc1 == redundant->crc); | 
|  | 969 | flag1 = redundant->flags; | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 970 |  | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 971 | if (crc0_ok && !crc1_ok) { | 
|  | 972 | dev_current = 0; | 
|  | 973 | } else if (!crc0_ok && crc1_ok) { | 
|  | 974 | dev_current = 1; | 
|  | 975 | } else if (!crc0_ok && !crc1_ok) { | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 976 | fprintf (stderr, | 
|  | 977 | "Warning: Bad CRC, using default environment\n"); | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 978 | memcpy (environment.data, default_environment, | 
|  | 979 | sizeof default_environment); | 
|  | 980 | dev_current = 0; | 
|  | 981 | } else { | 
|  | 982 | switch (environment.flag_scheme) { | 
|  | 983 | case FLAG_BOOLEAN: | 
|  | 984 | if (flag0 == active_flag && | 
|  | 985 | flag1 == obsolete_flag) { | 
|  | 986 | dev_current = 0; | 
|  | 987 | } else if (flag0 == obsolete_flag && | 
|  | 988 | flag1 == active_flag) { | 
|  | 989 | dev_current = 1; | 
|  | 990 | } else if (flag0 == flag1) { | 
|  | 991 | dev_current = 0; | 
|  | 992 | } else if (flag0 == 0xFF) { | 
|  | 993 | dev_current = 0; | 
|  | 994 | } else if (flag1 == 0xFF) { | 
|  | 995 | dev_current = 1; | 
|  | 996 | } else { | 
|  | 997 | dev_current = 0; | 
|  | 998 | } | 
|  | 999 | break; | 
|  | 1000 | case FLAG_INCREMENTAL: | 
|  | 1001 | if ((flag0 == 255 && flag1 == 0) || | 
|  | 1002 | flag1 > flag0) | 
|  | 1003 | dev_current = 1; | 
|  | 1004 | else if ((flag1 == 255 && flag0 == 0) || | 
|  | 1005 | flag0 > flag1) | 
|  | 1006 | dev_current = 0; | 
|  | 1007 | else /* flags are equal - almost impossible */ | 
|  | 1008 | dev_current = 0; | 
|  | 1009 | break; | 
|  | 1010 | default: | 
|  | 1011 | fprintf (stderr, "Unknown flag scheme %u \n", | 
|  | 1012 | environment.flag_scheme); | 
|  | 1013 | return -1; | 
|  | 1014 | } | 
|  | 1015 | } | 
|  | 1016 |  | 
|  | 1017 | /* | 
|  | 1018 | * If we are reading, we don't need the flag and the CRC any | 
|  | 1019 | * more, if we are writing, we will re-calculate CRC and update | 
|  | 1020 | * flags before writing out | 
|  | 1021 | */ | 
|  | 1022 | if (dev_current) { | 
|  | 1023 | environment.image	= addr1; | 
|  | 1024 | environment.crc		= &redundant->crc; | 
|  | 1025 | environment.flags	= &redundant->flags; | 
|  | 1026 | environment.data	= redundant->data; | 
|  | 1027 | free (addr0); | 
|  | 1028 | } else { | 
|  | 1029 | environment.image	= addr0; | 
|  | 1030 | /* Other pointers are already set */ | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 1031 | free (addr1); | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 1032 | } | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 1033 | } | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 1034 | return 0; | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 1035 | } | 
|  | 1036 |  | 
|  | 1037 |  | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 1038 | static int parse_config () | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 1039 | { | 
|  | 1040 | struct stat st; | 
|  | 1041 |  | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 1042 | #if defined(CONFIG_FILE) | 
|  | 1043 | /* Fills in DEVNAME(), ENVSIZE(), DEVESIZE(). Or don't. */ | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 1044 | if (get_config (CONFIG_FILE)) { | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 1045 | fprintf (stderr, | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 1046 | "Cannot parse config file: %s\n", strerror (errno)); | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 1047 | return -1; | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 1048 | } | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 1049 | #else | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 1050 | strcpy (DEVNAME (0), DEVICE1_NAME); | 
|  | 1051 | DEVOFFSET (0) = DEVICE1_OFFSET; | 
|  | 1052 | ENVSIZE (0) = ENV1_SIZE; | 
|  | 1053 | DEVESIZE (0) = DEVICE1_ESIZE; | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 1054 | ENVSECTORS (0) = DEVICE1_ENVSECTORS; | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 1055 | #ifdef HAVE_REDUND | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 1056 | strcpy (DEVNAME (1), DEVICE2_NAME); | 
|  | 1057 | DEVOFFSET (1) = DEVICE2_OFFSET; | 
|  | 1058 | ENVSIZE (1) = ENV2_SIZE; | 
|  | 1059 | DEVESIZE (1) = DEVICE2_ESIZE; | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 1060 | ENVSECTORS (1) = DEVICE2_ENVSECTORS; | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 1061 | HaveRedundEnv = 1; | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 1062 | #endif | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 1063 | #endif | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 1064 | if (stat (DEVNAME (0), &st)) { | 
|  | 1065 | fprintf (stderr, | 
|  | 1066 | "Cannot access MTD device %s: %s\n", | 
|  | 1067 | DEVNAME (0), strerror (errno)); | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 1068 | return -1; | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 1069 | } | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 1070 |  | 
|  | 1071 | if (HaveRedundEnv && stat (DEVNAME (1), &st)) { | 
|  | 1072 | fprintf (stderr, | 
|  | 1073 | "Cannot access MTD device %s: %s\n", | 
| Wolfgang Denk | e2146b6 | 2005-09-25 01:20:38 +0200 | [diff] [blame] | 1074 | DEVNAME (1), strerror (errno)); | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 1075 | return -1; | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 1076 | } | 
| wdenk | 6aff311 | 2002-12-17 01:51:00 +0000 | [diff] [blame] | 1077 | return 0; | 
|  | 1078 | } | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 1079 |  | 
|  | 1080 | #if defined(CONFIG_FILE) | 
|  | 1081 | static int get_config (char *fname) | 
|  | 1082 | { | 
|  | 1083 | FILE *fp; | 
|  | 1084 | int i = 0; | 
|  | 1085 | int rc; | 
|  | 1086 | char dump[128]; | 
|  | 1087 |  | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 1088 | fp = fopen (fname, "r"); | 
|  | 1089 | if (fp == NULL) | 
|  | 1090 | return -1; | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 1091 |  | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 1092 | while (i < 2 && fgets (dump, sizeof (dump), fp)) { | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 1093 | /* Skip incomplete conversions and comment strings */ | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 1094 | if (dump[0] == '#') | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 1095 | continue; | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 1096 |  | 
|  | 1097 | rc = sscanf (dump, "%s %lx %lx %lx %lx", | 
|  | 1098 | DEVNAME (i), | 
|  | 1099 | &DEVOFFSET (i), | 
|  | 1100 | &ENVSIZE (i), | 
|  | 1101 | &DEVESIZE (i), | 
|  | 1102 | &ENVSECTORS (i)); | 
|  | 1103 |  | 
|  | 1104 | if (rc < 4) | 
|  | 1105 | continue; | 
|  | 1106 |  | 
|  | 1107 | if (rc < 5) | 
|  | 1108 | /* Default - 1 sector */ | 
|  | 1109 | ENVSECTORS (i) = 1; | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 1110 |  | 
|  | 1111 | i++; | 
|  | 1112 | } | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 1113 | fclose (fp); | 
|  | 1114 |  | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 1115 | HaveRedundEnv = i - 1; | 
| wdenk | 4a6fd34 | 2003-04-12 23:38:12 +0000 | [diff] [blame] | 1116 | if (!i) {			/* No valid entries found */ | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 1117 | errno = EINVAL; | 
| Guennadi Liakhovetski | 5608692 | 2008-09-04 13:01:49 +0200 | [diff] [blame] | 1118 | return -1; | 
| wdenk | d0fb80c | 2003-01-11 09:48:40 +0000 | [diff] [blame] | 1119 | } else | 
|  | 1120 | return 0; | 
|  | 1121 | } | 
|  | 1122 | #endif |