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