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