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