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