]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - tools/env/fw_env.c
* Use 1-byte-read instead of -write for iprobe() function
[karo-tx-uboot.git] / tools / env / fw_env.c
1 /*
2  * (C) Copyright 2000
3  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
4  *
5  * See file CREDITS for list of people who contributed to this
6  * project.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of
11  * the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21  * MA 02111-1307 USA
22  */
23
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <stddef.h>
29 #include <string.h>
30 #include <sys/types.h>
31 #include <sys/ioctl.h>
32 #include <sys/stat.h>
33 #include <unistd.h>
34 #include <linux/mtd/mtd.h>
35 #include "fw_env.h"
36
37 typedef unsigned char   uchar;
38
39 #define CMD_GETENV      "fw_printenv"
40 #define CMD_SETENV      "fw_setenv"
41
42 typedef struct envdev_s {
43         uchar devname[16]; /* Device name */
44         ulong env_size;    /* environment size */
45         ulong erase_size;  /* device erase size */
46 } envdev_t;
47
48 static envdev_t envdevices[2];
49 static int curdev;
50
51 #define DEVNAME(i)    envdevices[(i)].devname
52 #define ENVSIZE(i)    envdevices[(i)].env_size
53 #define DEVESIZE(i)   envdevices[(i)].erase_size
54
55 #define CFG_ENV_SIZE ENVSIZE(curdev)
56
57 #ifdef HAVE_REDUND
58 #define ENV_SIZE (CFG_ENV_SIZE - sizeof(long) - 1)
59 #else
60 #define ENV_SIZE (CFG_ENV_SIZE - sizeof(long))
61 #endif
62
63 typedef struct environment_s {
64         ulong   crc;            /* CRC32 over data bytes        */
65         uchar   flags;      /* active or obsolete */
66         uchar *data;
67 } env_t;
68
69 static env_t environment;
70 static int valid = 0;
71
72 #ifdef HAVE_REDUND
73 static uchar active_flag = 1;
74 static uchar obsolete_flag = 0;
75 #endif
76
77 #define XMK_STR(x)      #x
78 #define MK_STR(x)       XMK_STR(x)
79
80 static uchar default_environment[] = {
81 #ifdef  CONFIG_BOOTARGS
82         "bootargs="     CONFIG_BOOTARGS                 "\0"
83 #endif
84 #ifdef  CONFIG_BOOTCOMMAND
85         "bootcmd="      CONFIG_BOOTCOMMAND              "\0"
86 #endif
87 #if (CONFIG_BOOTDELAY >= 0)
88         "bootdelay="    MK_STR(CONFIG_BOOTDELAY)        "\0"
89 #endif
90 #if (CONFIG_BAUDRATE >= 0)
91         "baudrate="     MK_STR(CONFIG_BAUDRATE)         "\0"
92 #endif
93 #ifdef  CONFIG_ETHADDR
94         "ethaddr="      MK_STR(CONFIG_ETHADDR)          "\0"
95 #endif
96 #ifdef  CONFIG_IPADDR
97         "ipaddr="       MK_STR(CONFIG_IPADDR)           "\0"
98 #endif
99 #ifdef  CONFIG_SERVERIP
100         "serverip="     MK_STR(CONFIG_SERVERIP)         "\0"
101 #endif
102         "\0"
103 };
104
105 static int  flash_io (int mode);
106 static uchar *envmatch(uchar *s1, uchar *s2);
107 static int env_init(void);
108 static int parse_config(void);
109
110
111 /*
112  * Search the environment for a variable.
113  * Return the value, if found, or NULL, if not found.
114  */
115 unsigned char *fw_getenv (unsigned char *name)
116 {
117         uchar *env, *nxt;
118
119         if (env_init())
120                 return (NULL);
121
122         for (env=environment.data; *env; env=nxt+1) {
123                 uchar *val;
124
125                 for (nxt=env; *nxt; ++nxt) {
126                         if (nxt >= &environment.data[ENV_SIZE]) {
127                                 fprintf (stderr, "## Error: "
128                                         "environment not terminated\n");
129                                 return (NULL);
130                         }
131                 }
132                 val=envmatch(name, env);
133                 if (!val)
134                         continue;
135                 return (val);
136         }
137         return (NULL);
138 }
139
140 /*
141  * Print the current definition of one, or more, or all
142  * environment variables
143  */
144 void fw_printenv(int argc, char *argv[])
145 {
146         uchar *env, *nxt;
147         int i, n_flag;
148
149         if (env_init())
150                 return;
151
152         if (argc == 1) {                /* Print all env variables      */
153                 for (env=environment.data; *env; env=nxt+1) {
154                         for (nxt=env; *nxt; ++nxt) {
155                                 if (nxt >= &environment.data[ENV_SIZE]) {
156                                         fprintf (stderr, "## Error: "
157                                                 "environment not terminated\n");
158                                         return;
159                                 }
160                         }
161
162                         printf("%s\n", env);
163                 }
164                 return;
165         }
166
167         if (strcmp(argv[1], "-n") == 0) {
168                 n_flag = 1;
169                 ++argv;
170                 --argc;
171                 if (argc != 2) {
172                         fprintf (stderr, "## Error: "
173                                 "`-n' option requires exactly one argument\n");
174                         return;
175                 }
176         } else {
177                 n_flag = 0;
178         }
179
180         for (i=1; i<argc; ++i) {        /* print single env variables   */
181                 uchar *name = argv[i];
182                 uchar *val = NULL;
183
184                 for (env=environment.data; *env; env=nxt+1) {
185
186                         for (nxt=env; *nxt; ++nxt) {
187                                 if (nxt >= &environment.data[ENV_SIZE]) {
188                                         fprintf (stderr, "## Error: "
189                                                 "environment not terminated\n");
190                                         return;
191                                 }
192                         }
193                         val=envmatch(name, env);
194                         if (val) {
195                                 if (!n_flag) {
196                                         fputs (name, stdout);
197                                         putc  ('=',  stdout);
198                                 }
199                                 puts  (val);
200                                 break;
201                         }
202                 }
203                 if (!val)
204                         fprintf (stderr, "## Error: \"%s\" not defined\n",
205                          name);
206         }
207 }
208
209 /*
210  * Deletes or sets environment variables. Returns errno style error codes:
211  * 0      - OK
212  * EINVAL - need at least 1 argument
213  * EROFS  - certain variables ("ethaddr", "serial#") cannot be
214  *          modified or deleted
215  *
216  */
217 int fw_setenv (int argc, char *argv[])
218 {
219         int  i, len;
220         uchar *env, *nxt;
221         uchar *oldval = NULL;
222         uchar *name;
223
224         if (argc < 2) {
225                 return (EINVAL);
226         }
227
228         if (env_init())
229                 return (errno);
230
231         name = argv[1];
232
233         /*
234          * search if variable with this name already exists
235          */
236         for (env=environment.data; *env; env=nxt+1) {
237                 for (nxt=env; *nxt; ++nxt) {
238                         if (nxt >= &environment.data[ENV_SIZE]) {
239                                 fprintf (stderr, "## Error: "
240                                         "environment not terminated\n");
241                                 return (EINVAL);
242                         }
243                 }
244                 if ((oldval=envmatch(name, env)) != NULL)
245                         break;
246         }
247
248         /*
249          * Delete any existing definition
250          */
251         if (oldval) {
252                 /*
253                  * Ethernet Address and serial# can be set only once
254                  */
255                 if ((strcmp (name, "ethaddr") == 0) ||
256                     (strcmp (name, "serial#") == 0) ) {
257                         fprintf (stderr, "Can't overwrite \"%s\"\n", name);
258                         return (EROFS);
259                 }
260
261                 if (*++nxt == '\0') {
262                         *env = '\0';
263                 } else {
264                         for (;;) {
265                                 *env = *nxt++;
266                                 if ((*env == '\0') && (*nxt == '\0'))
267                                         break;
268                                 ++env;
269                         }
270                 }
271                 *++env = '\0';
272         }
273
274         /* Delete only ? */
275         if (argc < 3)
276                 goto WRITE_FLASH;
277
278         /*
279          * Append new definition at the end
280          */
281         for (env=environment.data; *env || *(env+1); ++env)
282                 ;
283         if (env > environment.data)
284                 ++env;
285         /*
286          * Overflow when:
287          * "name" + "=" + "val" +"\0\0"  > CFG_ENV_SIZE - (env-environment)
288          */
289         len = strlen(name) + 2;
290         /* add '=' for first arg, ' ' for all others */
291         for (i=2; i<argc; ++i) {
292                 len += strlen(argv[i]) + 1;
293         }
294         if (len > (&environment.data[ENV_SIZE]-env)) {
295                 fprintf (stderr,
296                         "Error: environment overflow, \"%s\" deleted\n",
297                         name);
298                 return (-1);
299         }
300         while ((*env = *name++) != '\0')
301                 env++;
302         for (i=2; i<argc; ++i) {
303                 uchar *val = argv[i];
304
305                 *env = (i==2) ? '=' : ' ';
306                 while ((*++env = *val++) != '\0')
307                         ;
308         }
309
310         /* end is marked with double '\0' */
311         *++env = '\0';
312
313 WRITE_FLASH:
314
315         /* Update CRC */
316         environment.crc = crc32(0, environment.data, ENV_SIZE);
317
318         /* write environment back to flash */
319         if (flash_io (O_RDWR)) {
320                 fprintf (stderr,
321                         "Error: can't write fw_env to flash\n");
322                 return (-1);
323         }
324
325         return (0);
326 }
327
328 static int flash_io (int mode)
329 {
330         int fd, fdr, rc, otherdev, len, resid;
331         erase_info_t erase;
332         char *data;
333
334         if ((fd = open(DEVNAME(curdev), mode)) < 0) {
335                 fprintf (stderr, 
336                                  "Can't open %s: %s\n", 
337                                  DEVNAME(curdev), strerror(errno));
338                 return (-1);
339         }
340
341         len = sizeof(environment.crc) + sizeof(environment.flags);
342
343         if (mode == O_RDWR) {
344 #ifdef HAVE_REDUND
345                 /* switch to next partition for writing */
346                 otherdev = !curdev;
347                 if ((fdr = open(DEVNAME(otherdev), mode)) < 0) {
348                         fprintf (stderr, 
349                                          "Can't open %s: %s\n", 
350                                          DEVNAME(otherdev), strerror(errno));
351                         return (-1);
352                 }
353 #else
354                 otherdev = curdev;
355                 fdr = fd;
356                 len = sizeof(environment.crc);
357 #endif
358                 printf("Unlocking flash...\n");
359                 erase.length = DEVESIZE(otherdev);
360                 erase.start = 0;
361                 ioctl (fdr, MEMUNLOCK, &erase);
362
363 #ifdef HAVE_REDUND
364                 erase.length = DEVESIZE(curdev);
365                 erase.start = 0;
366                 ioctl (fd, MEMUNLOCK, &erase);
367                 environment.flags = active_flag;
368 #endif
369                 printf("Done\n");
370                 resid = DEVESIZE(otherdev) - CFG_ENV_SIZE;
371                 if (resid) {
372                         if ((data = malloc(resid)) == NULL) {
373                                 fprintf(stderr, 
374                                   "Cannot malloc %d bytes: %s\n",
375                                   resid, strerror(errno));
376                                 return (-1);
377                         }
378                         if (lseek (fdr, CFG_ENV_SIZE, SEEK_SET) == -1) {
379                                 fprintf (stderr,
380                                   "seek error on %s: %s\n", 
381                                    DEVNAME(curdev), strerror(errno));
382                                 return (-1);
383                         }
384                         if ((rc = read (fdr, data, resid)) != resid) {
385                                 fprintf (stderr,
386                                   "read error on %s: %s\n", 
387                                   DEVNAME(curdev), strerror(errno));
388                                 return (-1);
389                         }
390                         if (lseek (fdr, 0, SEEK_SET) == -1) {
391                                 fprintf (stderr,
392                                   "seek error on %s: %s\n", 
393                                   DEVNAME(curdev), strerror(errno));
394                                 return (-1);
395                         }
396                 }
397
398                 printf("Erasing old environment...\n");
399
400                 erase.length = DEVESIZE(otherdev);
401                 if (ioctl (fdr, MEMERASE, &erase) != 0) {
402                         fprintf (stderr, "MTD erase error on %s: %s\n",
403                           DEVNAME(otherdev), strerror(errno));
404                         return (-1);
405                 }
406
407                 printf("Done\n");
408
409                 printf("Writing environment to %s...\n",DEVNAME(otherdev));
410                 if (write(fdr, &environment, len) != len) {
411                         fprintf (stderr,
412                           "CRC write error on %s: %s\n", 
413                           DEVNAME(otherdev), strerror(errno));
414                         return (-1);
415                 }
416                 if (write(fdr, environment.data, ENV_SIZE) != ENV_SIZE) {
417                         fprintf (stderr,
418                           "Write error on %s: %s\n", 
419                           DEVNAME(otherdev), strerror(errno));
420                         return (-1);
421                 }
422                 if (resid) {
423                         if (write (fdr, data, resid) != resid) {
424                                 fprintf (stderr,
425                                  "write error on %s: %s\n", 
426                                   DEVNAME(curdev), strerror(errno));
427                                 return (-1);
428                         }
429                         free(data);
430                 }
431 #ifdef HAVE_REDUND
432                 /* change flag on current active env partition */
433                 if (lseek (fd, sizeof(ulong), SEEK_SET) == -1) {
434                         fprintf (stderr,
435                           "seek error on %s: %s\n", 
436                           DEVNAME(curdev), strerror(errno));
437                         return (-1);
438                 }
439                 if (write (fd, &obsolete_flag, sizeof(obsolete_flag)) != 
440                         sizeof(obsolete_flag)) {
441                         fprintf (stderr,
442                          "Write error on %s: %s\n", 
443                          DEVNAME(curdev), strerror(errno));
444                         return (-1);
445                 }
446 #endif
447                 printf("Done\n");
448                 printf("Locking ...\n");
449                 erase.length = DEVESIZE(otherdev);
450                 erase.start = 0;
451                 ioctl (fdr, MEMLOCK, &erase);
452 #ifdef HAVE_REDUND
453                 erase.length = DEVESIZE(curdev);
454                 erase.start = 0;
455                 ioctl (fd, MEMLOCK, &erase);
456                 if (close(fdr)) {
457                         fprintf (stderr,
458                          "I/O error on %s: %s\n", 
459                          DEVNAME(otherdev), strerror(errno));
460                         return (-1);
461                 }
462 #endif
463                 printf("Done\n");
464         } else {
465 #ifndef HAVE_REDUND
466                 len = sizeof(environment.crc);
467 #endif
468                 if (read (fd, &environment, len) != len) {
469                         fprintf (stderr,
470                          "CRC read error on %s: %s\n", 
471                          DEVNAME(curdev), strerror(errno));
472                         return (-1);
473                 }
474                 if ((rc = read (fd, environment.data, ENV_SIZE)) != ENV_SIZE) {
475                         fprintf (stderr,
476                          "Read error on %s: %s\n", 
477                           DEVNAME(curdev), strerror(errno));
478                         return (-1);
479                 }
480         }
481
482         if (close(fd)) {
483                 fprintf (stderr,
484                   "I/O error on %s: %s\n", 
485                   DEVNAME(curdev), strerror(errno));
486                 return (-1);
487         }
488
489         /* everything ok */
490         return (0);
491 }
492
493 /*
494  * s1 is either a simple 'name', or a 'name=value' pair.
495  * s2 is a 'name=value' pair.
496  * If the names match, return the value of s2, else NULL.
497  */
498
499 static uchar *
500 envmatch (uchar *s1, uchar *s2)
501 {
502
503         while (*s1 == *s2++)
504                 if (*s1++ == '=')
505                         return(s2);
506         if (*s1 == '\0' && *(s2-1) == '=')
507                 return(s2);
508         return(NULL);
509 }
510
511 /*
512  * Prevent confusion if running from erased flash memory
513  */
514 static int env_init(void)
515 {
516         int crc1, crc1_ok;
517         uchar *addr1;
518 #ifdef HAVE_REDUND
519         int crc2, crc2_ok;
520         uchar flag1, flag2, *addr2;
521 #endif
522
523         
524         if (!valid) {
525
526                 if (parse_config()) /* should fill envdevices */
527                         return 1;
528
529                 if ((addr1 = calloc (1, ENV_SIZE)) == NULL) {
530                         fprintf (stderr, 
531                          "Not enough memory for environment (%ld bytes)\n",
532                          ENV_SIZE);
533                         return (errno);
534                 }
535
536                 /* read environment from FLASH to local buffer */
537                 environment.data = addr1;
538                 curdev = 0;
539                 if (flash_io (O_RDONLY)) {
540                         return (errno);
541                 }
542
543                 crc1_ok = ((crc1 = crc32(0, environment.data, ENV_SIZE)) 
544                          == environment.crc);
545 #ifndef HAVE_REDUND
546                 if (!crc1_ok) {
547                         fprintf (stderr, 
548                          "Warning: Bad CRC, using default environment\n");
549                         environment.data = default_environment;
550                         free(addr1);
551                 }
552 #else
553                 flag1 = environment.flags;
554                 
555                 curdev = 1;
556                 if ((addr2 = calloc (1, ENV_SIZE)) == NULL) {
557                         fprintf (stderr, 
558                          "Not enough memory for environment (%ld bytes)\n",
559                          ENV_SIZE);
560                         return (errno);
561                 }               
562                 environment.data = addr2;
563
564                 if (flash_io (O_RDONLY)) {
565                         return (errno);
566                 }
567
568                 crc2_ok = ((crc2 = crc32(0, environment.data, ENV_SIZE)) 
569                                    == environment.crc);
570                 flag2 = environment.flags;
571                 
572                 if (crc1_ok && ! crc2_ok) {
573                         environment.data  = addr1;
574                         environment.flags = flag1;
575                         environment.crc = crc1;
576                         curdev = 0;
577                         free(addr2);
578                 }
579                 else if (! crc1_ok && crc2_ok) {
580                         environment.data  = addr2;
581                         environment.flags = flag2;
582                         environment.crc = crc2;
583                         curdev = 1;
584                         free(addr1);
585                 }
586                 else if (! crc1_ok && ! crc2_ok) {
587                         fprintf (stderr, 
588                          "Warning: Bad CRC, using default environment\n");
589                         environment.data = default_environment;
590                         curdev = 0;
591                         free(addr2);
592                         free(addr1);
593                 }
594                 else if (flag1 == active_flag && flag2 == obsolete_flag) {
595                         environment.data  = addr1;
596                         environment.flags = flag1;
597                         environment.crc = crc1;
598                         curdev = 0;
599                         free(addr2);
600                 }
601                 else if (flag1 == obsolete_flag && flag2 == active_flag) {
602                         environment.data  = addr2;
603                         environment.flags = flag2;
604                         environment.crc = crc2;
605                         curdev = 1;
606                         free(addr1);
607                 }
608                 else if (flag1 == flag2) {
609                         environment.data  = addr1;
610                         environment.flags = flag1;
611                         environment.crc = crc1;
612                         curdev = 0;
613                         free(addr2);
614                 }
615                 else if (flag1 == 0xFF) {
616                         environment.data  = addr1;
617                         environment.flags = flag1;
618                         environment.crc = crc1;
619                         curdev = 0;
620                         free(addr2);
621                 }
622                 else if (flag2 == 0xFF) {
623                         environment.data  = addr2;
624                         environment.flags = flag2;
625                         environment.crc = crc2;
626                         curdev = 1;
627                         free(addr1);
628                 }
629 #endif          
630                 valid = 1;
631         }
632         return (0);
633 }
634
635
636 static int parse_config()
637 {
638         struct stat st;
639
640         if (stat (DEVICE1_NAME, &st)) {
641                 fprintf (stderr, 
642                  "Cannot access MTD device %s: %s\n", 
643                  DEVICE1_NAME, strerror(errno));
644                 return 1;
645         }
646
647         strcpy(DEVNAME(0), DEVICE1_NAME);
648         ENVSIZE(0) = ENV1_SIZE;
649         DEVESIZE(0) = DEVICE1_ESIZE;
650 #ifdef HAVE_REDUND
651         if (stat (DEVICE2_NAME, &st)) {
652                 fprintf (stderr, 
653                  "Cannot access MTD device %s: %s\n", 
654                  DEVICE2_NAME, strerror(errno));
655                 return 1;
656         }
657         strcpy(DEVNAME(1), DEVICE2_NAME);
658         ENVSIZE(1) = ENV2_SIZE;
659         DEVESIZE(1) = DEVICE2_ESIZE;
660 #endif
661         return 0;
662 }