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