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 CFG_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 = CFG_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" > CFG_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 CFG_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, CFG_ENV_SIZE, 794 DEVOFFSET (dev_current), mtdinfo.type); 795 796 return (rc != CFG_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, CFG_ENV_SIZE); 894 if (addr0 == NULL) { 895 fprintf (stderr, 896 "Not enough memory for environment (%ld bytes)\n", 897 CFG_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, CFG_ENV_SIZE); 933 if (addr1 == NULL) { 934 fprintf (stderr, 935 "Not enough memory for environment (%ld bytes)\n", 936 CFG_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 1117