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 } 663