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