]>
Commit | Line | Data |
---|---|---|
6aff3115 | 1 | /* |
0aef7bc7 | 2 | * (C) Copyright 2000-2010 |
6aff3115 WD |
3 | * Wolfgang Denk, DENX Software Engineering, wd@denx.de. |
4 | * | |
56086921 GL |
5 | * (C) Copyright 2008 |
6 | * Guennadi Liakhovetski, DENX Software Engineering, lg@denx.de. | |
7 | * | |
6aff3115 WD |
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 | |
4a6fd34b | 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
6aff3115 WD |
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> | |
6aff3115 | 37 | |
6de66b35 | 38 | #ifdef MTD_OLD |
1711f3bd | 39 | # include <stdint.h> |
6de66b35 MK |
40 | # include <linux/mtd/mtd.h> |
41 | #else | |
c83d7ca4 | 42 | # define __user /* nothing */ |
6de66b35 MK |
43 | # include <mtd/mtd-user.h> |
44 | #endif | |
45 | ||
46 | #include "fw_env.h" | |
6aff3115 | 47 | |
bd7b26f8 | 48 | #define WHITESPACE(c) ((c == '\t') || (c == ' ')) |
6aff3115 | 49 | |
56086921 GL |
50 | #define min(x, y) ({ \ |
51 | typeof(x) _min1 = (x); \ | |
52 | typeof(y) _min2 = (y); \ | |
53 | (void) (&_min1 == &_min2); \ | |
54 | _min1 < _min2 ? _min1 : _min2; }) | |
55 | ||
56 | struct envdev_s { | |
6de66b35 | 57 | char devname[16]; /* Device name */ |
4a6fd34b WD |
58 | ulong devoff; /* Device offset */ |
59 | ulong env_size; /* environment size */ | |
60 | ulong erase_size; /* device erase size */ | |
56086921 GL |
61 | ulong env_sectors; /* number of environment sectors */ |
62 | uint8_t mtd_type; /* type of the MTD device */ | |
63 | }; | |
6aff3115 | 64 | |
56086921 GL |
65 | static struct envdev_s envdevices[2] = |
66 | { | |
67 | { | |
68 | .mtd_type = MTD_ABSENT, | |
69 | }, { | |
70 | .mtd_type = MTD_ABSENT, | |
71 | }, | |
72 | }; | |
73 | static int dev_current; | |
6aff3115 WD |
74 | |
75 | #define DEVNAME(i) envdevices[(i)].devname | |
d0fb80c3 | 76 | #define DEVOFFSET(i) envdevices[(i)].devoff |
6aff3115 WD |
77 | #define ENVSIZE(i) envdevices[(i)].env_size |
78 | #define DEVESIZE(i) envdevices[(i)].erase_size | |
56086921 GL |
79 | #define ENVSECTORS(i) envdevices[(i)].env_sectors |
80 | #define DEVTYPE(i) envdevices[(i)].mtd_type | |
6aff3115 | 81 | |
0e8d1586 | 82 | #define CONFIG_ENV_SIZE ENVSIZE(dev_current) |
6aff3115 | 83 | |
d0fb80c3 | 84 | #define ENV_SIZE getenvsize() |
6aff3115 | 85 | |
56086921 GL |
86 | struct env_image_single { |
87 | uint32_t crc; /* CRC32 over data bytes */ | |
88 | char data[]; | |
89 | }; | |
6aff3115 | 90 | |
56086921 GL |
91 | struct env_image_redundant { |
92 | uint32_t crc; /* CRC32 over data bytes */ | |
93 | unsigned char flags; /* active or obsolete */ | |
94 | char data[]; | |
95 | }; | |
96 | ||
97 | enum flag_scheme { | |
98 | FLAG_NONE, | |
99 | FLAG_BOOLEAN, | |
100 | FLAG_INCREMENTAL, | |
101 | }; | |
102 | ||
103 | struct environment { | |
104 | void *image; | |
105 | uint32_t *crc; | |
106 | unsigned char *flags; | |
107 | char *data; | |
108 | enum flag_scheme flag_scheme; | |
109 | }; | |
110 | ||
111 | static struct environment environment = { | |
112 | .flag_scheme = FLAG_NONE, | |
113 | }; | |
6aff3115 | 114 | |
d0fb80c3 WD |
115 | static int HaveRedundEnv = 0; |
116 | ||
6de66b35 | 117 | static unsigned char active_flag = 1; |
56086921 | 118 | /* obsolete_flag must be 0 to efficiently set it on NOR flash without erasing */ |
6de66b35 | 119 | static unsigned char obsolete_flag = 0; |
d0fb80c3 | 120 | |
6aff3115 WD |
121 | |
122 | #define XMK_STR(x) #x | |
123 | #define MK_STR(x) XMK_STR(x) | |
124 | ||
6de66b35 | 125 | static char default_environment[] = { |
d0fb80c3 | 126 | #if defined(CONFIG_BOOTARGS) |
4a6fd34b | 127 | "bootargs=" CONFIG_BOOTARGS "\0" |
6aff3115 | 128 | #endif |
d0fb80c3 | 129 | #if defined(CONFIG_BOOTCOMMAND) |
4a6fd34b | 130 | "bootcmd=" CONFIG_BOOTCOMMAND "\0" |
6aff3115 | 131 | #endif |
d0fb80c3 | 132 | #if defined(CONFIG_RAMBOOTCOMMAND) |
4a6fd34b | 133 | "ramboot=" CONFIG_RAMBOOTCOMMAND "\0" |
d0fb80c3 WD |
134 | #endif |
135 | #if defined(CONFIG_NFSBOOTCOMMAND) | |
4a6fd34b | 136 | "nfsboot=" CONFIG_NFSBOOTCOMMAND "\0" |
d0fb80c3 WD |
137 | #endif |
138 | #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) | |
4a6fd34b | 139 | "bootdelay=" MK_STR (CONFIG_BOOTDELAY) "\0" |
6aff3115 | 140 | #endif |
d0fb80c3 | 141 | #if defined(CONFIG_BAUDRATE) && (CONFIG_BAUDRATE >= 0) |
4a6fd34b | 142 | "baudrate=" MK_STR (CONFIG_BAUDRATE) "\0" |
6aff3115 | 143 | #endif |
d0fb80c3 | 144 | #ifdef CONFIG_LOADS_ECHO |
4a6fd34b | 145 | "loads_echo=" MK_STR (CONFIG_LOADS_ECHO) "\0" |
d0fb80c3 | 146 | #endif |
6aff3115 | 147 | #ifdef CONFIG_ETHADDR |
4a6fd34b | 148 | "ethaddr=" MK_STR (CONFIG_ETHADDR) "\0" |
6aff3115 | 149 | #endif |
d0fb80c3 | 150 | #ifdef CONFIG_ETH1ADDR |
4a6fd34b | 151 | "eth1addr=" MK_STR (CONFIG_ETH1ADDR) "\0" |
d0fb80c3 WD |
152 | #endif |
153 | #ifdef CONFIG_ETH2ADDR | |
4a6fd34b | 154 | "eth2addr=" MK_STR (CONFIG_ETH2ADDR) "\0" |
d0fb80c3 | 155 | #endif |
e2ffd59b WD |
156 | #ifdef CONFIG_ETH3ADDR |
157 | "eth3addr=" MK_STR (CONFIG_ETH3ADDR) "\0" | |
158 | #endif | |
c68a05fe | 159 | #ifdef CONFIG_ETH4ADDR |
160 | "eth4addr=" MK_STR (CONFIG_ETH4ADDR) "\0" | |
161 | #endif | |
162 | #ifdef CONFIG_ETH5ADDR | |
163 | "eth5addr=" MK_STR (CONFIG_ETH5ADDR) "\0" | |
164 | #endif | |
d0fb80c3 | 165 | #ifdef CONFIG_ETHPRIME |
4a6fd34b | 166 | "ethprime=" CONFIG_ETHPRIME "\0" |
d0fb80c3 | 167 | #endif |
6aff3115 | 168 | #ifdef CONFIG_IPADDR |
4a6fd34b | 169 | "ipaddr=" MK_STR (CONFIG_IPADDR) "\0" |
6aff3115 WD |
170 | #endif |
171 | #ifdef CONFIG_SERVERIP | |
4a6fd34b | 172 | "serverip=" MK_STR (CONFIG_SERVERIP) "\0" |
6aff3115 | 173 | #endif |
6d0f6bcf JCPV |
174 | #ifdef CONFIG_SYS_AUTOLOAD |
175 | "autoload=" CONFIG_SYS_AUTOLOAD "\0" | |
d0fb80c3 WD |
176 | #endif |
177 | #ifdef CONFIG_ROOTPATH | |
4a6fd34b | 178 | "rootpath=" MK_STR (CONFIG_ROOTPATH) "\0" |
d0fb80c3 WD |
179 | #endif |
180 | #ifdef CONFIG_GATEWAYIP | |
4a6fd34b | 181 | "gatewayip=" MK_STR (CONFIG_GATEWAYIP) "\0" |
d0fb80c3 WD |
182 | #endif |
183 | #ifdef CONFIG_NETMASK | |
4a6fd34b | 184 | "netmask=" MK_STR (CONFIG_NETMASK) "\0" |
d0fb80c3 WD |
185 | #endif |
186 | #ifdef CONFIG_HOSTNAME | |
4a6fd34b | 187 | "hostname=" MK_STR (CONFIG_HOSTNAME) "\0" |
d0fb80c3 WD |
188 | #endif |
189 | #ifdef CONFIG_BOOTFILE | |
4a6fd34b | 190 | "bootfile=" MK_STR (CONFIG_BOOTFILE) "\0" |
d0fb80c3 WD |
191 | #endif |
192 | #ifdef CONFIG_LOADADDR | |
4a6fd34b | 193 | "loadaddr=" MK_STR (CONFIG_LOADADDR) "\0" |
d0fb80c3 WD |
194 | #endif |
195 | #ifdef CONFIG_PREBOOT | |
4a6fd34b | 196 | "preboot=" CONFIG_PREBOOT "\0" |
d0fb80c3 WD |
197 | #endif |
198 | #ifdef CONFIG_CLOCKS_IN_MHZ | |
4a6fd34b | 199 | "clocks_in_mhz=" "1" "\0" |
d0fb80c3 | 200 | #endif |
ad10dd9a | 201 | #if defined(CONFIG_PCI_BOOTDELAY) && (CONFIG_PCI_BOOTDELAY > 0) |
4a6fd34b | 202 | "pcidelay=" MK_STR (CONFIG_PCI_BOOTDELAY) "\0" |
ad10dd9a | 203 | #endif |
d0fb80c3 WD |
204 | #ifdef CONFIG_EXTRA_ENV_SETTINGS |
205 | CONFIG_EXTRA_ENV_SETTINGS | |
206 | #endif | |
56086921 | 207 | "\0" /* Termimate struct environment data with 2 NULs */ |
6aff3115 WD |
208 | }; |
209 | ||
4a6fd34b | 210 | static int flash_io (int mode); |
6de66b35 | 211 | static char *envmatch (char * s1, char * s2); |
4a6fd34b WD |
212 | static int parse_config (void); |
213 | ||
d0fb80c3 | 214 | #if defined(CONFIG_FILE) |
4a6fd34b | 215 | static int get_config (char *); |
d0fb80c3 | 216 | #endif |
4a6fd34b | 217 | static inline ulong getenvsize (void) |
d0fb80c3 | 218 | { |
0e8d1586 | 219 | ulong rc = CONFIG_ENV_SIZE - sizeof (long); |
4a6fd34b | 220 | |
d0fb80c3 | 221 | if (HaveRedundEnv) |
4a6fd34b | 222 | rc -= sizeof (char); |
d0fb80c3 WD |
223 | return rc; |
224 | } | |
6aff3115 | 225 | |
bd7b26f8 SB |
226 | static char *fw_string_blank(char *s, int noblank) |
227 | { | |
228 | int i; | |
229 | int len = strlen(s); | |
230 | ||
231 | for (i = 0; i < len; i++, s++) { | |
232 | if ((noblank && !WHITESPACE(*s)) || | |
233 | (!noblank && WHITESPACE(*s))) | |
234 | break; | |
235 | } | |
236 | if (i == len) | |
237 | return NULL; | |
238 | ||
239 | return s; | |
240 | } | |
241 | ||
6aff3115 WD |
242 | /* |
243 | * Search the environment for a variable. | |
244 | * Return the value, if found, or NULL, if not found. | |
245 | */ | |
6de66b35 | 246 | char *fw_getenv (char *name) |
6aff3115 | 247 | { |
6de66b35 | 248 | char *env, *nxt; |
6aff3115 | 249 | |
bd7b26f8 | 250 | if (fw_env_open()) |
56086921 | 251 | return NULL; |
6aff3115 | 252 | |
4a6fd34b | 253 | for (env = environment.data; *env; env = nxt + 1) { |
6de66b35 | 254 | char *val; |
6aff3115 | 255 | |
4a6fd34b | 256 | for (nxt = env; *nxt; ++nxt) { |
6aff3115 WD |
257 | if (nxt >= &environment.data[ENV_SIZE]) { |
258 | fprintf (stderr, "## Error: " | |
259 | "environment not terminated\n"); | |
56086921 | 260 | return NULL; |
6aff3115 WD |
261 | } |
262 | } | |
4a6fd34b | 263 | val = envmatch (name, env); |
6aff3115 WD |
264 | if (!val) |
265 | continue; | |
56086921 | 266 | return val; |
6aff3115 | 267 | } |
56086921 | 268 | return NULL; |
6aff3115 WD |
269 | } |
270 | ||
271 | /* | |
272 | * Print the current definition of one, or more, or all | |
273 | * environment variables | |
274 | */ | |
bc11756d | 275 | int fw_printenv (int argc, char *argv[]) |
6aff3115 | 276 | { |
6de66b35 | 277 | char *env, *nxt; |
6aff3115 | 278 | int i, n_flag; |
bc11756d | 279 | int rc = 0; |
6aff3115 | 280 | |
bd7b26f8 | 281 | if (fw_env_open()) |
56086921 | 282 | return -1; |
6aff3115 | 283 | |
4a6fd34b WD |
284 | if (argc == 1) { /* Print all env variables */ |
285 | for (env = environment.data; *env; env = nxt + 1) { | |
286 | for (nxt = env; *nxt; ++nxt) { | |
6aff3115 WD |
287 | if (nxt >= &environment.data[ENV_SIZE]) { |
288 | fprintf (stderr, "## Error: " | |
289 | "environment not terminated\n"); | |
56086921 | 290 | return -1; |
6aff3115 WD |
291 | } |
292 | } | |
293 | ||
4a6fd34b | 294 | printf ("%s\n", env); |
6aff3115 | 295 | } |
56086921 | 296 | return 0; |
6aff3115 WD |
297 | } |
298 | ||
4a6fd34b | 299 | if (strcmp (argv[1], "-n") == 0) { |
6aff3115 WD |
300 | n_flag = 1; |
301 | ++argv; | |
302 | --argc; | |
303 | if (argc != 2) { | |
304 | fprintf (stderr, "## Error: " | |
305 | "`-n' option requires exactly one argument\n"); | |
56086921 | 306 | return -1; |
6aff3115 WD |
307 | } |
308 | } else { | |
309 | n_flag = 0; | |
310 | } | |
311 | ||
4a6fd34b | 312 | for (i = 1; i < argc; ++i) { /* print single env variables */ |
6de66b35 MK |
313 | char *name = argv[i]; |
314 | char *val = NULL; | |
6aff3115 | 315 | |
4a6fd34b | 316 | for (env = environment.data; *env; env = nxt + 1) { |
6aff3115 | 317 | |
4a6fd34b | 318 | for (nxt = env; *nxt; ++nxt) { |
6aff3115 WD |
319 | if (nxt >= &environment.data[ENV_SIZE]) { |
320 | fprintf (stderr, "## Error: " | |
321 | "environment not terminated\n"); | |
56086921 | 322 | return -1; |
6aff3115 WD |
323 | } |
324 | } | |
4a6fd34b | 325 | val = envmatch (name, env); |
6aff3115 WD |
326 | if (val) { |
327 | if (!n_flag) { | |
328 | fputs (name, stdout); | |
4a6fd34b | 329 | putc ('=', stdout); |
6aff3115 | 330 | } |
4a6fd34b | 331 | puts (val); |
6aff3115 WD |
332 | break; |
333 | } | |
334 | } | |
bc11756d | 335 | if (!val) { |
4a6fd34b | 336 | fprintf (stderr, "## Error: \"%s\" not defined\n", name); |
bc11756d GE |
337 | rc = -1; |
338 | } | |
6aff3115 | 339 | } |
bc11756d | 340 | |
56086921 | 341 | return rc; |
6aff3115 WD |
342 | } |
343 | ||
bd7b26f8 | 344 | int fw_env_close(void) |
6aff3115 | 345 | { |
bd7b26f8 SB |
346 | /* |
347 | * Update CRC | |
348 | */ | |
349 | *environment.crc = crc32(0, (uint8_t *) environment.data, ENV_SIZE); | |
6aff3115 | 350 | |
bd7b26f8 SB |
351 | /* write environment back to flash */ |
352 | if (flash_io(O_RDWR)) { | |
353 | fprintf(stderr, | |
354 | "Error: can't write fw_env to flash\n"); | |
355 | return -1; | |
6aff3115 WD |
356 | } |
357 | ||
bd7b26f8 SB |
358 | return 0; |
359 | } | |
6aff3115 | 360 | |
bd7b26f8 SB |
361 | |
362 | /* | |
363 | * Set/Clear a single variable in the environment. | |
364 | * This is called in sequence to update the environment | |
365 | * in RAM without updating the copy in flash after each set | |
366 | */ | |
367 | int fw_env_write(char *name, char *value) | |
368 | { | |
369 | int len; | |
370 | char *env, *nxt; | |
371 | char *oldval = NULL; | |
6aff3115 WD |
372 | |
373 | /* | |
374 | * search if variable with this name already exists | |
375 | */ | |
4a6fd34b WD |
376 | for (nxt = env = environment.data; *env; env = nxt + 1) { |
377 | for (nxt = env; *nxt; ++nxt) { | |
6aff3115 | 378 | if (nxt >= &environment.data[ENV_SIZE]) { |
bd7b26f8 | 379 | fprintf(stderr, "## Error: " |
6aff3115 | 380 | "environment not terminated\n"); |
56086921 GL |
381 | errno = EINVAL; |
382 | return -1; | |
6aff3115 WD |
383 | } |
384 | } | |
4a6fd34b | 385 | if ((oldval = envmatch (name, env)) != NULL) |
6aff3115 WD |
386 | break; |
387 | } | |
388 | ||
389 | /* | |
390 | * Delete any existing definition | |
391 | */ | |
392 | if (oldval) { | |
393 | /* | |
394 | * Ethernet Address and serial# can be set only once | |
395 | */ | |
396 | if ((strcmp (name, "ethaddr") == 0) || | |
4a6fd34b | 397 | (strcmp (name, "serial#") == 0)) { |
6aff3115 | 398 | fprintf (stderr, "Can't overwrite \"%s\"\n", name); |
56086921 GL |
399 | errno = EROFS; |
400 | return -1; | |
6aff3115 WD |
401 | } |
402 | ||
403 | if (*++nxt == '\0') { | |
404 | *env = '\0'; | |
405 | } else { | |
406 | for (;;) { | |
407 | *env = *nxt++; | |
408 | if ((*env == '\0') && (*nxt == '\0')) | |
409 | break; | |
410 | ++env; | |
411 | } | |
412 | } | |
413 | *++env = '\0'; | |
414 | } | |
415 | ||
416 | /* Delete only ? */ | |
bd7b26f8 SB |
417 | if (!value || !strlen(value)) |
418 | return 0; | |
6aff3115 WD |
419 | |
420 | /* | |
421 | * Append new definition at the end | |
422 | */ | |
4a6fd34b | 423 | for (env = environment.data; *env || *(env + 1); ++env); |
6aff3115 WD |
424 | if (env > environment.data) |
425 | ++env; | |
426 | /* | |
427 | * Overflow when: | |
0e8d1586 | 428 | * "name" + "=" + "val" +"\0\0" > CONFIG_ENV_SIZE - (env-environment) |
6aff3115 | 429 | */ |
4a6fd34b | 430 | len = strlen (name) + 2; |
6aff3115 | 431 | /* add '=' for first arg, ' ' for all others */ |
bd7b26f8 SB |
432 | len += strlen(value) + 1; |
433 | ||
4a6fd34b | 434 | if (len > (&environment.data[ENV_SIZE] - env)) { |
6aff3115 WD |
435 | fprintf (stderr, |
436 | "Error: environment overflow, \"%s\" deleted\n", | |
437 | name); | |
56086921 | 438 | return -1; |
6aff3115 | 439 | } |
bd7b26f8 | 440 | |
6aff3115 WD |
441 | while ((*env = *name++) != '\0') |
442 | env++; | |
bd7b26f8 SB |
443 | *env = '='; |
444 | while ((*++env = *value++) != '\0') | |
445 | ; | |
446 | ||
447 | /* end is marked with double '\0' */ | |
448 | *++env = '\0'; | |
449 | ||
450 | return 0; | |
451 | } | |
452 | ||
453 | /* | |
454 | * Deletes or sets environment variables. Returns -1 and sets errno error codes: | |
455 | * 0 - OK | |
456 | * EINVAL - need at least 1 argument | |
457 | * EROFS - certain variables ("ethaddr", "serial#") cannot be | |
458 | * modified or deleted | |
459 | * | |
460 | */ | |
461 | int fw_setenv(int argc, char *argv[]) | |
462 | { | |
463 | int i, len; | |
464 | char *name; | |
465 | char *value = NULL; | |
466 | char *tmpval = NULL; | |
467 | ||
468 | if (argc < 2) { | |
469 | errno = EINVAL; | |
470 | return -1; | |
471 | } | |
472 | ||
473 | if (fw_env_open()) { | |
474 | fprintf(stderr, "Error: environment not initialized\n"); | |
475 | return -1; | |
476 | } | |
477 | ||
478 | name = argv[1]; | |
479 | ||
480 | len = strlen(name) + 2; | |
481 | for (i = 2; i < argc; ++i) | |
482 | len += strlen(argv[i]) + 1; | |
483 | ||
484 | /* Allocate enough place to the data string */ | |
4a6fd34b | 485 | for (i = 2; i < argc; ++i) { |
6de66b35 | 486 | char *val = argv[i]; |
bd7b26f8 SB |
487 | if (!value) { |
488 | value = (char *)malloc(len - strlen(name)); | |
489 | if (!value) { | |
490 | fprintf(stderr, | |
491 | "Cannot malloc %u bytes: %s\n", | |
492 | len - strlen(name), strerror(errno)); | |
493 | return -1; | |
494 | } | |
495 | memset(value, 0, len - strlen(name)); | |
496 | tmpval = value; | |
497 | } | |
498 | if (i != 2) | |
499 | *tmpval++ = ' '; | |
500 | while (*val != '\0') | |
501 | *tmpval++ = *val++; | |
6aff3115 WD |
502 | } |
503 | ||
bd7b26f8 | 504 | fw_env_write(name, value); |
6aff3115 | 505 | |
bd7b26f8 SB |
506 | if (value) |
507 | free(value); | |
6aff3115 | 508 | |
bd7b26f8 SB |
509 | return fw_env_close(); |
510 | } | |
6aff3115 | 511 | |
bd7b26f8 SB |
512 | /* |
513 | * Parse a file and configure the u-boot variables. | |
514 | * The script file has a very simple format, as follows: | |
515 | * | |
516 | * Each line has a couple with name, value: | |
517 | * <white spaces>variable_name<white spaces>variable_value | |
518 | * | |
519 | * Both variable_name and variable_value are interpreted as strings. | |
520 | * Any character after <white spaces> and before ending \r\n is interpreted | |
521 | * as variable's value (no comment allowed on these lines !) | |
522 | * | |
523 | * Comments are allowed if the first character in the line is # | |
524 | * | |
525 | * Returns -1 and sets errno error codes: | |
526 | * 0 - OK | |
527 | * -1 - Error | |
528 | */ | |
529 | int fw_parse_script(char *fname) | |
530 | { | |
531 | FILE *fp; | |
532 | char dump[1024]; /* Maximum line length in the file */ | |
533 | char *name; | |
534 | char *val; | |
535 | int lineno = 0; | |
536 | int len; | |
537 | int ret = 0; | |
538 | ||
539 | if (fw_env_open()) { | |
540 | fprintf(stderr, "Error: environment not initialized\n"); | |
56086921 | 541 | return -1; |
6aff3115 WD |
542 | } |
543 | ||
bd7b26f8 SB |
544 | if (strcmp(fname, "-") == 0) |
545 | fp = stdin; | |
546 | else { | |
547 | fp = fopen(fname, "r"); | |
548 | if (fp == NULL) { | |
549 | fprintf(stderr, "I cannot open %s for reading\n", | |
550 | fname); | |
551 | return -1; | |
552 | } | |
553 | } | |
554 | ||
555 | while (fgets(dump, sizeof(dump), fp)) { | |
556 | lineno++; | |
557 | len = strlen(dump); | |
558 | ||
559 | /* | |
560 | * Read a whole line from the file. If the line is too long | |
561 | * or is not terminated, reports an error and exit. | |
562 | */ | |
563 | if (dump[len - 1] != '\n') { | |
564 | fprintf(stderr, | |
565 | "Line %d not corrected terminated or too long\n", | |
566 | lineno); | |
567 | ret = -1; | |
568 | break; | |
569 | } | |
570 | ||
571 | /* Drop ending line feed / carriage return */ | |
572 | while (len > 0 && (dump[len - 1] == '\n' || | |
573 | dump[len - 1] == '\r')) { | |
574 | dump[len - 1] = '\0'; | |
575 | len--; | |
576 | } | |
577 | ||
578 | /* Skip comment or empty lines */ | |
579 | if ((len == 0) || dump[0] == '#') | |
580 | continue; | |
581 | ||
582 | /* | |
583 | * Search for variable's name, | |
584 | * remove leading whitespaces | |
585 | */ | |
586 | name = fw_string_blank(dump, 1); | |
587 | if (!name) | |
588 | continue; | |
589 | ||
590 | /* The first white space is the end of variable name */ | |
591 | val = fw_string_blank(name, 0); | |
592 | len = strlen(name); | |
593 | if (val) { | |
594 | *val++ = '\0'; | |
595 | if ((val - name) < len) | |
596 | val = fw_string_blank(val, 1); | |
597 | else | |
598 | val = NULL; | |
599 | } | |
600 | ||
601 | #ifdef DEBUG | |
602 | fprintf(stderr, "Setting %s : %s\n", | |
603 | name, val ? val : " removed"); | |
604 | #endif | |
605 | ||
606 | /* | |
607 | * If there is an error setting a variable, | |
608 | * try to save the environment and returns an error | |
609 | */ | |
610 | if (fw_env_write(name, val)) { | |
611 | fprintf(stderr, | |
612 | "fw_env_write returns with error : %s\n", | |
613 | strerror(errno)); | |
614 | ret = -1; | |
615 | break; | |
616 | } | |
617 | ||
618 | } | |
619 | ||
620 | /* Close file if not stdin */ | |
621 | if (strcmp(fname, "-") != 0) | |
622 | fclose(fp); | |
623 | ||
624 | ret |= fw_env_close(); | |
625 | ||
626 | return ret; | |
627 | ||
6aff3115 WD |
628 | } |
629 | ||
56086921 GL |
630 | /* |
631 | * Test for bad block on NAND, just returns 0 on NOR, on NAND: | |
632 | * 0 - block is good | |
633 | * > 0 - block is bad | |
634 | * < 0 - failed to test | |
635 | */ | |
636 | static int flash_bad_block (int fd, uint8_t mtd_type, loff_t *blockstart) | |
6aff3115 | 637 | { |
56086921 GL |
638 | if (mtd_type == MTD_NANDFLASH) { |
639 | int badblock = ioctl (fd, MEMGETBADBLOCK, blockstart); | |
6aff3115 | 640 | |
56086921 GL |
641 | if (badblock < 0) { |
642 | perror ("Cannot read bad block mark"); | |
643 | return badblock; | |
644 | } | |
645 | ||
646 | if (badblock) { | |
647 | #ifdef DEBUG | |
648 | fprintf (stderr, "Bad block at 0x%llx, " | |
649 | "skipping\n", *blockstart); | |
650 | #endif | |
651 | return badblock; | |
652 | } | |
6aff3115 WD |
653 | } |
654 | ||
56086921 GL |
655 | return 0; |
656 | } | |
657 | ||
658 | /* | |
659 | * Read data from flash at an offset into a provided buffer. On NAND it skips | |
660 | * bad blocks but makes sure it stays within ENVSECTORS (dev) starting from | |
661 | * the DEVOFFSET (dev) block. On NOR the loop is only run once. | |
662 | */ | |
663 | static int flash_read_buf (int dev, int fd, void *buf, size_t count, | |
664 | off_t offset, uint8_t mtd_type) | |
665 | { | |
666 | size_t blocklen; /* erase / write length - one block on NAND, | |
667 | 0 on NOR */ | |
668 | size_t processed = 0; /* progress counter */ | |
669 | size_t readlen = count; /* current read length */ | |
670 | off_t top_of_range; /* end of the last block we may use */ | |
671 | off_t block_seek; /* offset inside the current block to the start | |
672 | of the data */ | |
673 | loff_t blockstart; /* running start of the current block - | |
674 | MEMGETBADBLOCK needs 64 bits */ | |
675 | int rc; | |
676 | ||
9eeaa8e6 | 677 | blockstart = (offset / DEVESIZE (dev)) * DEVESIZE (dev); |
56086921 GL |
678 | |
679 | /* Offset inside a block */ | |
680 | block_seek = offset - blockstart; | |
681 | ||
682 | if (mtd_type == MTD_NANDFLASH) { | |
683 | /* | |
684 | * NAND: calculate which blocks we are reading. We have | |
685 | * to read one block at a time to skip bad blocks. | |
686 | */ | |
687 | blocklen = DEVESIZE (dev); | |
688 | ||
689 | /* | |
690 | * To calculate the top of the range, we have to use the | |
691 | * global DEVOFFSET (dev), which can be different from offset | |
692 | */ | |
9eeaa8e6 RB |
693 | top_of_range = ((DEVOFFSET(dev) / blocklen) + |
694 | ENVSECTORS (dev)) * blocklen; | |
56086921 GL |
695 | |
696 | /* Limit to one block for the first read */ | |
697 | if (readlen > blocklen - block_seek) | |
698 | readlen = blocklen - block_seek; | |
699 | } else { | |
700 | blocklen = 0; | |
701 | top_of_range = offset + count; | |
d0fb80c3 | 702 | } |
6aff3115 | 703 | |
56086921 GL |
704 | /* This only runs once on NOR flash */ |
705 | while (processed < count) { | |
706 | rc = flash_bad_block (fd, mtd_type, &blockstart); | |
707 | if (rc < 0) /* block test failed */ | |
708 | return -1; | |
6aff3115 | 709 | |
56086921 GL |
710 | if (blockstart + block_seek + readlen > top_of_range) { |
711 | /* End of range is reached */ | |
712 | fprintf (stderr, | |
713 | "Too few good blocks within range\n"); | |
714 | return -1; | |
d0fb80c3 WD |
715 | } |
716 | ||
56086921 GL |
717 | if (rc) { /* block is bad */ |
718 | blockstart += blocklen; | |
719 | continue; | |
6aff3115 WD |
720 | } |
721 | ||
56086921 GL |
722 | /* |
723 | * If a block is bad, we retry in the next block at the same | |
724 | * offset - see common/env_nand.c::writeenv() | |
725 | */ | |
726 | lseek (fd, blockstart + block_seek, SEEK_SET); | |
6aff3115 | 727 | |
56086921 GL |
728 | rc = read (fd, buf + processed, readlen); |
729 | if (rc != readlen) { | |
730 | fprintf (stderr, "Read error on %s: %s\n", | |
731 | DEVNAME (dev), strerror (errno)); | |
732 | return -1; | |
6aff3115 | 733 | } |
56086921 GL |
734 | #ifdef DEBUG |
735 | fprintf (stderr, "Read 0x%x bytes at 0x%llx\n", | |
736 | rc, blockstart + block_seek); | |
737 | #endif | |
738 | processed += readlen; | |
739 | readlen = min (blocklen, count - processed); | |
740 | block_seek = 0; | |
741 | blockstart += blocklen; | |
742 | } | |
743 | ||
744 | return processed; | |
745 | } | |
746 | ||
747 | /* | |
9eeaa8e6 RB |
748 | * Write count bytes at offset, but stay within ENVSECTORS (dev) sectors of |
749 | * DEVOFFSET (dev). Similar to the read case above, on NOR and dataflash we | |
750 | * erase and write the whole data at once. | |
56086921 GL |
751 | */ |
752 | static int flash_write_buf (int dev, int fd, void *buf, size_t count, | |
753 | off_t offset, uint8_t mtd_type) | |
754 | { | |
755 | void *data; | |
756 | struct erase_info_user erase; | |
757 | size_t blocklen; /* length of NAND block / NOR erase sector */ | |
758 | size_t erase_len; /* whole area that can be erased - may include | |
759 | bad blocks */ | |
760 | size_t erasesize; /* erase / write length - one block on NAND, | |
761 | whole area on NOR */ | |
762 | size_t processed = 0; /* progress counter */ | |
9eeaa8e6 | 763 | size_t write_total; /* total size to actually write - excluding |
56086921 GL |
764 | bad blocks */ |
765 | off_t erase_offset; /* offset to the first erase block (aligned) | |
766 | below offset */ | |
767 | off_t block_seek; /* offset inside the erase block to the start | |
768 | of the data */ | |
769 | off_t top_of_range; /* end of the last block we may use */ | |
770 | loff_t blockstart; /* running start of the current block - | |
771 | MEMGETBADBLOCK needs 64 bits */ | |
772 | int rc; | |
773 | ||
774 | blocklen = DEVESIZE (dev); | |
775 | ||
9eeaa8e6 RB |
776 | top_of_range = ((DEVOFFSET(dev) / blocklen) + |
777 | ENVSECTORS (dev)) * blocklen; | |
6aff3115 | 778 | |
9eeaa8e6 | 779 | erase_offset = (offset / blocklen) * blocklen; |
6aff3115 | 780 | |
56086921 GL |
781 | /* Maximum area we may use */ |
782 | erase_len = top_of_range - erase_offset; | |
783 | ||
784 | blockstart = erase_offset; | |
785 | /* Offset inside a block */ | |
786 | block_seek = offset - erase_offset; | |
787 | ||
788 | /* | |
789 | * Data size we actually have to write: from the start of the block | |
790 | * to the start of the data, then count bytes of data, and to the | |
791 | * end of the block | |
792 | */ | |
9eeaa8e6 RB |
793 | write_total = ((block_seek + count + blocklen - 1) / |
794 | blocklen) * blocklen; | |
56086921 GL |
795 | |
796 | /* | |
797 | * Support data anywhere within erase sectors: read out the complete | |
798 | * area to be erased, replace the environment image, write the whole | |
799 | * block back again. | |
800 | */ | |
801 | if (write_total > count) { | |
802 | data = malloc (erase_len); | |
803 | if (!data) { | |
d0fb80c3 | 804 | fprintf (stderr, |
56086921 GL |
805 | "Cannot malloc %u bytes: %s\n", |
806 | erase_len, strerror (errno)); | |
807 | return -1; | |
d0fb80c3 | 808 | } |
56086921 GL |
809 | |
810 | rc = flash_read_buf (dev, fd, data, write_total, erase_offset, | |
811 | mtd_type); | |
812 | if (write_total != rc) | |
813 | return -1; | |
814 | ||
815 | /* Overwrite the old environment */ | |
816 | memcpy (data + block_seek, buf, count); | |
817 | } else { | |
818 | /* | |
819 | * We get here, iff offset is block-aligned and count is a | |
820 | * multiple of blocklen - see write_total calculation above | |
821 | */ | |
822 | data = buf; | |
823 | } | |
824 | ||
825 | if (mtd_type == MTD_NANDFLASH) { | |
826 | /* | |
827 | * NAND: calculate which blocks we are writing. We have | |
828 | * to write one block at a time to skip bad blocks. | |
829 | */ | |
830 | erasesize = blocklen; | |
831 | } else { | |
832 | erasesize = erase_len; | |
833 | } | |
834 | ||
835 | erase.length = erasesize; | |
836 | ||
9eeaa8e6 | 837 | /* This only runs once on NOR flash and SPI-dataflash */ |
56086921 GL |
838 | while (processed < write_total) { |
839 | rc = flash_bad_block (fd, mtd_type, &blockstart); | |
840 | if (rc < 0) /* block test failed */ | |
841 | return rc; | |
842 | ||
843 | if (blockstart + erasesize > top_of_range) { | |
844 | fprintf (stderr, "End of range reached, aborting\n"); | |
845 | return -1; | |
6aff3115 | 846 | } |
56086921 GL |
847 | |
848 | if (rc) { /* block is bad */ | |
849 | blockstart += blocklen; | |
850 | continue; | |
851 | } | |
852 | ||
853 | erase.start = blockstart; | |
854 | ioctl (fd, MEMUNLOCK, &erase); | |
855 | ||
9eeaa8e6 RB |
856 | /* Dataflash does not need an explicit erase cycle */ |
857 | if (mtd_type != MTD_DATAFLASH) | |
858 | if (ioctl (fd, MEMERASE, &erase) != 0) { | |
859 | fprintf (stderr, "MTD erase error on %s: %s\n", | |
860 | DEVNAME (dev), | |
861 | strerror (errno)); | |
862 | return -1; | |
863 | } | |
56086921 GL |
864 | |
865 | if (lseek (fd, blockstart, SEEK_SET) == -1) { | |
6aff3115 | 866 | fprintf (stderr, |
56086921 GL |
867 | "Seek error on %s: %s\n", |
868 | DEVNAME (dev), strerror (errno)); | |
869 | return -1; | |
6aff3115 | 870 | } |
56086921 GL |
871 | |
872 | #ifdef DEBUG | |
873 | printf ("Write 0x%x bytes at 0x%llx\n", erasesize, blockstart); | |
874 | #endif | |
875 | if (write (fd, data + processed, erasesize) != erasesize) { | |
876 | fprintf (stderr, "Write error on %s: %s\n", | |
877 | DEVNAME (dev), strerror (errno)); | |
878 | return -1; | |
6aff3115 | 879 | } |
56086921 GL |
880 | |
881 | ioctl (fd, MEMLOCK, &erase); | |
882 | ||
883 | processed += blocklen; | |
884 | block_seek = 0; | |
885 | blockstart += blocklen; | |
886 | } | |
887 | ||
888 | if (write_total > count) | |
889 | free (data); | |
890 | ||
891 | return processed; | |
892 | } | |
893 | ||
894 | /* | |
895 | * Set obsolete flag at offset - NOR flash only | |
896 | */ | |
897 | static int flash_flag_obsolete (int dev, int fd, off_t offset) | |
898 | { | |
899 | int rc; | |
0aef7bc7 | 900 | struct erase_info_user erase; |
56086921 | 901 | |
0aef7bc7 DZ |
902 | erase.start = DEVOFFSET (dev); |
903 | erase.length = DEVESIZE (dev); | |
56086921 GL |
904 | /* This relies on the fact, that obsolete_flag == 0 */ |
905 | rc = lseek (fd, offset, SEEK_SET); | |
906 | if (rc < 0) { | |
907 | fprintf (stderr, "Cannot seek to set the flag on %s \n", | |
908 | DEVNAME (dev)); | |
909 | return rc; | |
910 | } | |
0aef7bc7 | 911 | ioctl (fd, MEMUNLOCK, &erase); |
56086921 | 912 | rc = write (fd, &obsolete_flag, sizeof (obsolete_flag)); |
0aef7bc7 | 913 | ioctl (fd, MEMLOCK, &erase); |
56086921 GL |
914 | if (rc < 0) |
915 | perror ("Could not set obsolete flag"); | |
916 | ||
917 | return rc; | |
918 | } | |
919 | ||
920 | static int flash_write (int fd_current, int fd_target, int dev_target) | |
921 | { | |
922 | int rc; | |
923 | ||
924 | switch (environment.flag_scheme) { | |
925 | case FLAG_NONE: | |
926 | break; | |
927 | case FLAG_INCREMENTAL: | |
928 | (*environment.flags)++; | |
929 | break; | |
930 | case FLAG_BOOLEAN: | |
931 | *environment.flags = active_flag; | |
932 | break; | |
933 | default: | |
934 | fprintf (stderr, "Unimplemented flash scheme %u \n", | |
935 | environment.flag_scheme); | |
936 | return -1; | |
937 | } | |
938 | ||
939 | #ifdef DEBUG | |
940 | printf ("Writing new environment at 0x%lx on %s\n", | |
941 | DEVOFFSET (dev_target), DEVNAME (dev_target)); | |
942 | #endif | |
943 | rc = flash_write_buf (dev_target, fd_target, environment.image, | |
0e8d1586 | 944 | CONFIG_ENV_SIZE, DEVOFFSET (dev_target), |
56086921 GL |
945 | DEVTYPE(dev_target)); |
946 | if (rc < 0) | |
947 | return rc; | |
948 | ||
949 | if (environment.flag_scheme == FLAG_BOOLEAN) { | |
950 | /* Have to set obsolete flag */ | |
951 | off_t offset = DEVOFFSET (dev_current) + | |
952 | offsetof (struct env_image_redundant, flags); | |
953 | #ifdef DEBUG | |
954 | printf ("Setting obsolete flag in environment at 0x%lx on %s\n", | |
955 | DEVOFFSET (dev_current), DEVNAME (dev_current)); | |
956 | #endif | |
957 | flash_flag_obsolete (dev_current, fd_current, offset); | |
958 | } | |
959 | ||
960 | return 0; | |
961 | } | |
962 | ||
963 | static int flash_read (int fd) | |
964 | { | |
965 | struct mtd_info_user mtdinfo; | |
966 | int rc; | |
967 | ||
968 | rc = ioctl (fd, MEMGETINFO, &mtdinfo); | |
969 | if (rc < 0) { | |
970 | perror ("Cannot get MTD information"); | |
971 | return -1; | |
972 | } | |
973 | ||
9eeaa8e6 RB |
974 | if (mtdinfo.type != MTD_NORFLASH && |
975 | mtdinfo.type != MTD_NANDFLASH && | |
976 | mtdinfo.type != MTD_DATAFLASH) { | |
56086921 GL |
977 | fprintf (stderr, "Unsupported flash type %u\n", mtdinfo.type); |
978 | return -1; | |
979 | } | |
980 | ||
981 | DEVTYPE(dev_current) = mtdinfo.type; | |
982 | ||
0e8d1586 | 983 | rc = flash_read_buf (dev_current, fd, environment.image, CONFIG_ENV_SIZE, |
56086921 GL |
984 | DEVOFFSET (dev_current), mtdinfo.type); |
985 | ||
0e8d1586 | 986 | return (rc != CONFIG_ENV_SIZE) ? -1 : 0; |
56086921 GL |
987 | } |
988 | ||
989 | static int flash_io (int mode) | |
990 | { | |
991 | int fd_current, fd_target, rc, dev_target; | |
992 | ||
993 | /* dev_current: fd_current, erase_current */ | |
994 | fd_current = open (DEVNAME (dev_current), mode); | |
995 | if (fd_current < 0) { | |
996 | fprintf (stderr, | |
997 | "Can't open %s: %s\n", | |
998 | DEVNAME (dev_current), strerror (errno)); | |
999 | return -1; | |
1000 | } | |
1001 | ||
1002 | if (mode == O_RDWR) { | |
d0fb80c3 | 1003 | if (HaveRedundEnv) { |
56086921 GL |
1004 | /* switch to next partition for writing */ |
1005 | dev_target = !dev_current; | |
1006 | /* dev_target: fd_target, erase_target */ | |
1007 | fd_target = open (DEVNAME (dev_target), mode); | |
1008 | if (fd_target < 0) { | |
d0fb80c3 | 1009 | fprintf (stderr, |
56086921 GL |
1010 | "Can't open %s: %s\n", |
1011 | DEVNAME (dev_target), | |
1012 | strerror (errno)); | |
1013 | rc = -1; | |
1014 | goto exit; | |
d0fb80c3 | 1015 | } |
56086921 GL |
1016 | } else { |
1017 | dev_target = dev_current; | |
1018 | fd_target = fd_current; | |
6aff3115 | 1019 | } |
56086921 GL |
1020 | |
1021 | rc = flash_write (fd_current, fd_target, dev_target); | |
1022 | ||
d0fb80c3 | 1023 | if (HaveRedundEnv) { |
56086921 | 1024 | if (close (fd_target)) { |
d0fb80c3 | 1025 | fprintf (stderr, |
4a6fd34b | 1026 | "I/O error on %s: %s\n", |
56086921 | 1027 | DEVNAME (dev_target), |
4a6fd34b | 1028 | strerror (errno)); |
56086921 | 1029 | rc = -1; |
d0fb80c3 | 1030 | } |
6aff3115 | 1031 | } |
6aff3115 | 1032 | } else { |
56086921 | 1033 | rc = flash_read (fd_current); |
6aff3115 WD |
1034 | } |
1035 | ||
56086921 GL |
1036 | exit: |
1037 | if (close (fd_current)) { | |
6aff3115 | 1038 | fprintf (stderr, |
56086921 GL |
1039 | "I/O error on %s: %s\n", |
1040 | DEVNAME (dev_current), strerror (errno)); | |
1041 | return -1; | |
6aff3115 WD |
1042 | } |
1043 | ||
56086921 | 1044 | return rc; |
6aff3115 WD |
1045 | } |
1046 | ||
1047 | /* | |
1048 | * s1 is either a simple 'name', or a 'name=value' pair. | |
1049 | * s2 is a 'name=value' pair. | |
1050 | * If the names match, return the value of s2, else NULL. | |
1051 | */ | |
1052 | ||
6de66b35 | 1053 | static char *envmatch (char * s1, char * s2) |
6aff3115 WD |
1054 | { |
1055 | ||
1056 | while (*s1 == *s2++) | |
1057 | if (*s1++ == '=') | |
56086921 | 1058 | return s2; |
4a6fd34b | 1059 | if (*s1 == '\0' && *(s2 - 1) == '=') |
56086921 GL |
1060 | return s2; |
1061 | return NULL; | |
6aff3115 WD |
1062 | } |
1063 | ||
1064 | /* | |
1065 | * Prevent confusion if running from erased flash memory | |
1066 | */ | |
bd7b26f8 | 1067 | int fw_env_open(void) |
6aff3115 | 1068 | { |
56086921 | 1069 | int crc0, crc0_ok; |
735eb0f0 | 1070 | unsigned char flag0; |
56086921 GL |
1071 | void *addr0; |
1072 | ||
6aff3115 | 1073 | int crc1, crc1_ok; |
735eb0f0 | 1074 | unsigned char flag1; |
56086921 | 1075 | void *addr1; |
d0fb80c3 | 1076 | |
56086921 GL |
1077 | struct env_image_single *single; |
1078 | struct env_image_redundant *redundant; | |
6aff3115 | 1079 | |
4a6fd34b | 1080 | if (parse_config ()) /* should fill envdevices */ |
56086921 | 1081 | return -1; |
4a6fd34b | 1082 | |
0e8d1586 | 1083 | addr0 = calloc (1, CONFIG_ENV_SIZE); |
56086921 | 1084 | if (addr0 == NULL) { |
4a6fd34b WD |
1085 | fprintf (stderr, |
1086 | "Not enough memory for environment (%ld bytes)\n", | |
0e8d1586 | 1087 | CONFIG_ENV_SIZE); |
56086921 | 1088 | return -1; |
d0fb80c3 | 1089 | } |
4a6fd34b | 1090 | |
d0fb80c3 | 1091 | /* read environment from FLASH to local buffer */ |
56086921 GL |
1092 | environment.image = addr0; |
1093 | ||
1094 | if (HaveRedundEnv) { | |
1095 | redundant = addr0; | |
1096 | environment.crc = &redundant->crc; | |
1097 | environment.flags = &redundant->flags; | |
1098 | environment.data = redundant->data; | |
1099 | } else { | |
1100 | single = addr0; | |
1101 | environment.crc = &single->crc; | |
1102 | environment.flags = NULL; | |
1103 | environment.data = single->data; | |
d0fb80c3 | 1104 | } |
4a6fd34b | 1105 | |
56086921 GL |
1106 | dev_current = 0; |
1107 | if (flash_io (O_RDONLY)) | |
1108 | return -1; | |
1109 | ||
1110 | crc0 = crc32 (0, (uint8_t *) environment.data, ENV_SIZE); | |
1111 | crc0_ok = (crc0 == *environment.crc); | |
d0fb80c3 | 1112 | if (!HaveRedundEnv) { |
56086921 | 1113 | if (!crc0_ok) { |
4a6fd34b WD |
1114 | fprintf (stderr, |
1115 | "Warning: Bad CRC, using default environment\n"); | |
f07217c9 | 1116 | memcpy(environment.data, default_environment, sizeof default_environment); |
6aff3115 | 1117 | } |
d0fb80c3 | 1118 | } else { |
56086921 | 1119 | flag0 = *environment.flags; |
4a6fd34b | 1120 | |
56086921 | 1121 | dev_current = 1; |
0e8d1586 | 1122 | addr1 = calloc (1, CONFIG_ENV_SIZE); |
56086921 | 1123 | if (addr1 == NULL) { |
4a6fd34b WD |
1124 | fprintf (stderr, |
1125 | "Not enough memory for environment (%ld bytes)\n", | |
0e8d1586 | 1126 | CONFIG_ENV_SIZE); |
56086921 | 1127 | return -1; |
4a6fd34b | 1128 | } |
56086921 | 1129 | redundant = addr1; |
4a6fd34b | 1130 | |
56086921 GL |
1131 | /* |
1132 | * have to set environment.image for flash_read(), careful - | |
1133 | * other pointers in environment still point inside addr0 | |
1134 | */ | |
1135 | environment.image = addr1; | |
1136 | if (flash_io (O_RDONLY)) | |
1137 | return -1; | |
1138 | ||
1139 | /* Check flag scheme compatibility */ | |
1140 | if (DEVTYPE(dev_current) == MTD_NORFLASH && | |
1141 | DEVTYPE(!dev_current) == MTD_NORFLASH) { | |
1142 | environment.flag_scheme = FLAG_BOOLEAN; | |
1143 | } else if (DEVTYPE(dev_current) == MTD_NANDFLASH && | |
1144 | DEVTYPE(!dev_current) == MTD_NANDFLASH) { | |
1145 | environment.flag_scheme = FLAG_INCREMENTAL; | |
9eeaa8e6 RB |
1146 | } else if (DEVTYPE(dev_current) == MTD_DATAFLASH && |
1147 | DEVTYPE(!dev_current) == MTD_DATAFLASH) { | |
1148 | environment.flag_scheme = FLAG_BOOLEAN; | |
56086921 GL |
1149 | } else { |
1150 | fprintf (stderr, "Incompatible flash types!\n"); | |
1151 | return -1; | |
6aff3115 | 1152 | } |
4a6fd34b | 1153 | |
56086921 GL |
1154 | crc1 = crc32 (0, (uint8_t *) redundant->data, ENV_SIZE); |
1155 | crc1_ok = (crc1 == redundant->crc); | |
1156 | flag1 = redundant->flags; | |
1157 | ||
1158 | if (crc0_ok && !crc1_ok) { | |
1159 | dev_current = 0; | |
1160 | } else if (!crc0_ok && crc1_ok) { | |
1161 | dev_current = 1; | |
1162 | } else if (!crc0_ok && !crc1_ok) { | |
4a6fd34b WD |
1163 | fprintf (stderr, |
1164 | "Warning: Bad CRC, using default environment\n"); | |
56086921 GL |
1165 | memcpy (environment.data, default_environment, |
1166 | sizeof default_environment); | |
1167 | dev_current = 0; | |
1168 | } else { | |
1169 | switch (environment.flag_scheme) { | |
1170 | case FLAG_BOOLEAN: | |
1171 | if (flag0 == active_flag && | |
1172 | flag1 == obsolete_flag) { | |
1173 | dev_current = 0; | |
1174 | } else if (flag0 == obsolete_flag && | |
1175 | flag1 == active_flag) { | |
1176 | dev_current = 1; | |
1177 | } else if (flag0 == flag1) { | |
1178 | dev_current = 0; | |
1179 | } else if (flag0 == 0xFF) { | |
1180 | dev_current = 0; | |
1181 | } else if (flag1 == 0xFF) { | |
1182 | dev_current = 1; | |
1183 | } else { | |
1184 | dev_current = 0; | |
1185 | } | |
1186 | break; | |
1187 | case FLAG_INCREMENTAL: | |
735eb0f0 | 1188 | if (flag0 == 255 && flag1 == 0) |
56086921 GL |
1189 | dev_current = 1; |
1190 | else if ((flag1 == 255 && flag0 == 0) || | |
735eb0f0 | 1191 | flag0 >= flag1) |
56086921 | 1192 | dev_current = 0; |
735eb0f0 JP |
1193 | else /* flag1 > flag0 */ |
1194 | dev_current = 1; | |
56086921 GL |
1195 | break; |
1196 | default: | |
1197 | fprintf (stderr, "Unknown flag scheme %u \n", | |
1198 | environment.flag_scheme); | |
1199 | return -1; | |
1200 | } | |
1201 | } | |
1202 | ||
1203 | /* | |
1204 | * If we are reading, we don't need the flag and the CRC any | |
1205 | * more, if we are writing, we will re-calculate CRC and update | |
1206 | * flags before writing out | |
1207 | */ | |
1208 | if (dev_current) { | |
1209 | environment.image = addr1; | |
1210 | environment.crc = &redundant->crc; | |
1211 | environment.flags = &redundant->flags; | |
1212 | environment.data = redundant->data; | |
1213 | free (addr0); | |
1214 | } else { | |
1215 | environment.image = addr0; | |
1216 | /* Other pointers are already set */ | |
4a6fd34b | 1217 | free (addr1); |
6aff3115 | 1218 | } |
6aff3115 | 1219 | } |
56086921 | 1220 | return 0; |
6aff3115 WD |
1221 | } |
1222 | ||
1223 | ||
4a6fd34b | 1224 | static int parse_config () |
6aff3115 WD |
1225 | { |
1226 | struct stat st; | |
1227 | ||
d0fb80c3 WD |
1228 | #if defined(CONFIG_FILE) |
1229 | /* Fills in DEVNAME(), ENVSIZE(), DEVESIZE(). Or don't. */ | |
4a6fd34b | 1230 | if (get_config (CONFIG_FILE)) { |
d0fb80c3 | 1231 | fprintf (stderr, |
4a6fd34b | 1232 | "Cannot parse config file: %s\n", strerror (errno)); |
56086921 | 1233 | return -1; |
6aff3115 | 1234 | } |
d0fb80c3 | 1235 | #else |
4a6fd34b WD |
1236 | strcpy (DEVNAME (0), DEVICE1_NAME); |
1237 | DEVOFFSET (0) = DEVICE1_OFFSET; | |
1238 | ENVSIZE (0) = ENV1_SIZE; | |
9eeaa8e6 RB |
1239 | /* Default values are: erase-size=env-size, #sectors=1 */ |
1240 | DEVESIZE (0) = ENVSIZE (0); | |
1241 | ENVSECTORS (0) = 1; | |
1242 | #ifdef DEVICE1_ESIZE | |
4a6fd34b | 1243 | DEVESIZE (0) = DEVICE1_ESIZE; |
9eeaa8e6 RB |
1244 | #endif |
1245 | #ifdef DEVICE1_ENVSECTORS | |
56086921 | 1246 | ENVSECTORS (0) = DEVICE1_ENVSECTORS; |
9eeaa8e6 RB |
1247 | #endif |
1248 | ||
6aff3115 | 1249 | #ifdef HAVE_REDUND |
4a6fd34b WD |
1250 | strcpy (DEVNAME (1), DEVICE2_NAME); |
1251 | DEVOFFSET (1) = DEVICE2_OFFSET; | |
1252 | ENVSIZE (1) = ENV2_SIZE; | |
9eeaa8e6 RB |
1253 | /* Default values are: erase-size=env-size, #sectors=1 */ |
1254 | DEVESIZE (1) = ENVSIZE (1); | |
1255 | ENVSECTORS (1) = 1; | |
1256 | #ifdef DEVICE2_ESIZE | |
4a6fd34b | 1257 | DEVESIZE (1) = DEVICE2_ESIZE; |
9eeaa8e6 RB |
1258 | #endif |
1259 | #ifdef DEVICE2_ENVSECTORS | |
56086921 | 1260 | ENVSECTORS (1) = DEVICE2_ENVSECTORS; |
9eeaa8e6 | 1261 | #endif |
d0fb80c3 | 1262 | HaveRedundEnv = 1; |
6aff3115 | 1263 | #endif |
d0fb80c3 | 1264 | #endif |
4a6fd34b WD |
1265 | if (stat (DEVNAME (0), &st)) { |
1266 | fprintf (stderr, | |
1267 | "Cannot access MTD device %s: %s\n", | |
1268 | DEVNAME (0), strerror (errno)); | |
56086921 | 1269 | return -1; |
d0fb80c3 | 1270 | } |
4a6fd34b WD |
1271 | |
1272 | if (HaveRedundEnv && stat (DEVNAME (1), &st)) { | |
1273 | fprintf (stderr, | |
1274 | "Cannot access MTD device %s: %s\n", | |
e2146b6a | 1275 | DEVNAME (1), strerror (errno)); |
56086921 | 1276 | return -1; |
d0fb80c3 | 1277 | } |
6aff3115 WD |
1278 | return 0; |
1279 | } | |
d0fb80c3 WD |
1280 | |
1281 | #if defined(CONFIG_FILE) | |
1282 | static int get_config (char *fname) | |
1283 | { | |
1284 | FILE *fp; | |
1285 | int i = 0; | |
1286 | int rc; | |
1287 | char dump[128]; | |
1288 | ||
56086921 GL |
1289 | fp = fopen (fname, "r"); |
1290 | if (fp == NULL) | |
1291 | return -1; | |
d0fb80c3 | 1292 | |
56086921 | 1293 | while (i < 2 && fgets (dump, sizeof (dump), fp)) { |
d0fb80c3 | 1294 | /* Skip incomplete conversions and comment strings */ |
56086921 | 1295 | if (dump[0] == '#') |
d0fb80c3 | 1296 | continue; |
56086921 GL |
1297 | |
1298 | rc = sscanf (dump, "%s %lx %lx %lx %lx", | |
1299 | DEVNAME (i), | |
1300 | &DEVOFFSET (i), | |
1301 | &ENVSIZE (i), | |
1302 | &DEVESIZE (i), | |
1303 | &ENVSECTORS (i)); | |
1304 | ||
9eeaa8e6 | 1305 | if (rc < 3) |
56086921 GL |
1306 | continue; |
1307 | ||
9eeaa8e6 RB |
1308 | if (rc < 4) |
1309 | /* Assume the erase size is the same as the env-size */ | |
1310 | DEVESIZE(i) = ENVSIZE(i); | |
1311 | ||
56086921 GL |
1312 | if (rc < 5) |
1313 | /* Default - 1 sector */ | |
1314 | ENVSECTORS (i) = 1; | |
d0fb80c3 WD |
1315 | |
1316 | i++; | |
1317 | } | |
4a6fd34b WD |
1318 | fclose (fp); |
1319 | ||
d0fb80c3 | 1320 | HaveRedundEnv = i - 1; |
4a6fd34b | 1321 | if (!i) { /* No valid entries found */ |
d0fb80c3 | 1322 | errno = EINVAL; |
56086921 | 1323 | return -1; |
d0fb80c3 WD |
1324 | } else |
1325 | return 0; | |
1326 | } | |
1327 | #endif |