1 /* 2 * (C) Copyright 2000-2010 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 <linux/stringify.h> 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <stddef.h> 33 #include <string.h> 34 #include <sys/types.h> 35 #include <sys/ioctl.h> 36 #include <sys/stat.h> 37 #include <unistd.h> 38 39 #ifdef MTD_OLD 40 # include <stdint.h> 41 # include <linux/mtd/mtd.h> 42 #else 43 # define __user /* nothing */ 44 # include <mtd/mtd-user.h> 45 #endif 46 47 #include "fw_env.h" 48 49 #define WHITESPACE(c) ((c == '\t') || (c == ' ')) 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 CUR_ENVSIZE 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 #define DEFAULT_ENV_INSTANCE_STATIC 123 #include <env_default.h> 124 125 static int flash_io (int mode); 126 static char *envmatch (char * s1, char * s2); 127 static int parse_config (void); 128 129 #if defined(CONFIG_FILE) 130 static int get_config (char *); 131 #endif 132 static inline ulong getenvsize (void) 133 { 134 ulong rc = CUR_ENVSIZE - sizeof(long); 135 136 if (HaveRedundEnv) 137 rc -= sizeof (char); 138 return rc; 139 } 140 141 static char *fw_string_blank(char *s, int noblank) 142 { 143 int i; 144 int len = strlen(s); 145 146 for (i = 0; i < len; i++, s++) { 147 if ((noblank && !WHITESPACE(*s)) || 148 (!noblank && WHITESPACE(*s))) 149 break; 150 } 151 if (i == len) 152 return NULL; 153 154 return s; 155 } 156 157 /* 158 * Search the environment for a variable. 159 * Return the value, if found, or NULL, if not found. 160 */ 161 char *fw_getenv (char *name) 162 { 163 char *env, *nxt; 164 165 for (env = environment.data; *env; env = nxt + 1) { 166 char *val; 167 168 for (nxt = env; *nxt; ++nxt) { 169 if (nxt >= &environment.data[ENV_SIZE]) { 170 fprintf (stderr, "## Error: " 171 "environment not terminated\n"); 172 return NULL; 173 } 174 } 175 val = envmatch (name, env); 176 if (!val) 177 continue; 178 return val; 179 } 180 return NULL; 181 } 182 183 /* 184 * Print the current definition of one, or more, or all 185 * environment variables 186 */ 187 int fw_printenv (int argc, char *argv[]) 188 { 189 char *env, *nxt; 190 int i, n_flag; 191 int rc = 0; 192 193 if (fw_env_open()) 194 return -1; 195 196 if (argc == 1) { /* Print all env variables */ 197 for (env = environment.data; *env; env = nxt + 1) { 198 for (nxt = env; *nxt; ++nxt) { 199 if (nxt >= &environment.data[ENV_SIZE]) { 200 fprintf (stderr, "## Error: " 201 "environment not terminated\n"); 202 return -1; 203 } 204 } 205 206 printf ("%s\n", env); 207 } 208 return 0; 209 } 210 211 if (strcmp (argv[1], "-n") == 0) { 212 n_flag = 1; 213 ++argv; 214 --argc; 215 if (argc != 2) { 216 fprintf (stderr, "## Error: " 217 "`-n' option requires exactly one argument\n"); 218 return -1; 219 } 220 } else { 221 n_flag = 0; 222 } 223 224 for (i = 1; i < argc; ++i) { /* print single env variables */ 225 char *name = argv[i]; 226 char *val = NULL; 227 228 for (env = environment.data; *env; env = nxt + 1) { 229 230 for (nxt = env; *nxt; ++nxt) { 231 if (nxt >= &environment.data[ENV_SIZE]) { 232 fprintf (stderr, "## Error: " 233 "environment not terminated\n"); 234 return -1; 235 } 236 } 237 val = envmatch (name, env); 238 if (val) { 239 if (!n_flag) { 240 fputs (name, stdout); 241 putc ('=', stdout); 242 } 243 puts (val); 244 break; 245 } 246 } 247 if (!val) { 248 fprintf (stderr, "## Error: \"%s\" not defined\n", name); 249 rc = -1; 250 } 251 } 252 253 return rc; 254 } 255 256 int fw_env_close(void) 257 { 258 /* 259 * Update CRC 260 */ 261 *environment.crc = crc32(0, (uint8_t *) environment.data, ENV_SIZE); 262 263 /* write environment back to flash */ 264 if (flash_io(O_RDWR)) { 265 fprintf(stderr, 266 "Error: can't write fw_env to flash\n"); 267 return -1; 268 } 269 270 return 0; 271 } 272 273 274 /* 275 * Set/Clear a single variable in the environment. 276 * This is called in sequence to update the environment 277 * in RAM without updating the copy in flash after each set 278 */ 279 int fw_env_write(char *name, char *value) 280 { 281 int len; 282 char *env, *nxt; 283 char *oldval = NULL; 284 285 /* 286 * search if variable with this name already exists 287 */ 288 for (nxt = env = environment.data; *env; env = nxt + 1) { 289 for (nxt = env; *nxt; ++nxt) { 290 if (nxt >= &environment.data[ENV_SIZE]) { 291 fprintf(stderr, "## Error: " 292 "environment not terminated\n"); 293 errno = EINVAL; 294 return -1; 295 } 296 } 297 if ((oldval = envmatch (name, env)) != NULL) 298 break; 299 } 300 301 /* 302 * Delete any existing definition 303 */ 304 if (oldval) { 305 #ifndef CONFIG_ENV_OVERWRITE 306 /* 307 * Ethernet Address and serial# can be set only once 308 */ 309 if ( 310 (strcmp(name, "serial#") == 0) || 311 ((strcmp(name, "ethaddr") == 0) 312 #if defined(CONFIG_OVERWRITE_ETHADDR_ONCE) && defined(CONFIG_ETHADDR) 313 && (strcmp(oldval, __stringify(CONFIG_ETHADDR)) != 0) 314 #endif /* CONFIG_OVERWRITE_ETHADDR_ONCE && CONFIG_ETHADDR */ 315 ) ) { 316 fprintf (stderr, "Can't overwrite \"%s\"\n", name); 317 errno = EROFS; 318 return -1; 319 } 320 #endif /* CONFIG_ENV_OVERWRITE */ 321 322 if (*++nxt == '\0') { 323 *env = '\0'; 324 } else { 325 for (;;) { 326 *env = *nxt++; 327 if ((*env == '\0') && (*nxt == '\0')) 328 break; 329 ++env; 330 } 331 } 332 *++env = '\0'; 333 } 334 335 /* Delete only ? */ 336 if (!value || !strlen(value)) 337 return 0; 338 339 /* 340 * Append new definition at the end 341 */ 342 for (env = environment.data; *env || *(env + 1); ++env); 343 if (env > environment.data) 344 ++env; 345 /* 346 * Overflow when: 347 * "name" + "=" + "val" +"\0\0" > CUR_ENVSIZE - (env-environment) 348 */ 349 len = strlen (name) + 2; 350 /* add '=' for first arg, ' ' for all others */ 351 len += strlen(value) + 1; 352 353 if (len > (&environment.data[ENV_SIZE] - env)) { 354 fprintf (stderr, 355 "Error: environment overflow, \"%s\" deleted\n", 356 name); 357 return -1; 358 } 359 360 while ((*env = *name++) != '\0') 361 env++; 362 *env = '='; 363 while ((*++env = *value++) != '\0') 364 ; 365 366 /* end is marked with double '\0' */ 367 *++env = '\0'; 368 369 return 0; 370 } 371 372 /* 373 * Deletes or sets environment variables. Returns -1 and sets errno error codes: 374 * 0 - OK 375 * EINVAL - need at least 1 argument 376 * EROFS - certain variables ("ethaddr", "serial#") cannot be 377 * modified or deleted 378 * 379 */ 380 int fw_setenv(int argc, char *argv[]) 381 { 382 int i, len; 383 char *name; 384 char *value = NULL; 385 386 if (argc < 2) { 387 errno = EINVAL; 388 return -1; 389 } 390 391 if (fw_env_open()) { 392 fprintf(stderr, "Error: environment not initialized\n"); 393 return -1; 394 } 395 396 name = argv[1]; 397 398 len = 0; 399 for (i = 2; i < argc; ++i) { 400 char *val = argv[i]; 401 size_t val_len = strlen(val); 402 403 if (value) 404 value[len - 1] = ' '; 405 value = realloc(value, len + val_len + 1); 406 if (!value) { 407 fprintf(stderr, 408 "Cannot malloc %zu bytes: %s\n", 409 len, strerror(errno)); 410 return -1; 411 } 412 413 memcpy(value + len, val, val_len); 414 len += val_len; 415 value[len++] = '\0'; 416 } 417 418 fw_env_write(name, value); 419 420 free(value); 421 422 return fw_env_close(); 423 } 424 425 /* 426 * Parse a file and configure the u-boot variables. 427 * The script file has a very simple format, as follows: 428 * 429 * Each line has a couple with name, value: 430 * <white spaces>variable_name<white spaces>variable_value 431 * 432 * Both variable_name and variable_value are interpreted as strings. 433 * Any character after <white spaces> and before ending \r\n is interpreted 434 * as variable's value (no comment allowed on these lines !) 435 * 436 * Comments are allowed if the first character in the line is # 437 * 438 * Returns -1 and sets errno error codes: 439 * 0 - OK 440 * -1 - Error 441 */ 442 int fw_parse_script(char *fname) 443 { 444 FILE *fp; 445 char dump[1024]; /* Maximum line length in the file */ 446 char *name; 447 char *val; 448 int lineno = 0; 449 int len; 450 int ret = 0; 451 452 if (fw_env_open()) { 453 fprintf(stderr, "Error: environment not initialized\n"); 454 return -1; 455 } 456 457 if (strcmp(fname, "-") == 0) 458 fp = stdin; 459 else { 460 fp = fopen(fname, "r"); 461 if (fp == NULL) { 462 fprintf(stderr, "I cannot open %s for reading\n", 463 fname); 464 return -1; 465 } 466 } 467 468 while (fgets(dump, sizeof(dump), fp)) { 469 lineno++; 470 len = strlen(dump); 471 472 /* 473 * Read a whole line from the file. If the line is too long 474 * or is not terminated, reports an error and exit. 475 */ 476 if (dump[len - 1] != '\n') { 477 fprintf(stderr, 478 "Line %d not corrected terminated or too long\n", 479 lineno); 480 ret = -1; 481 break; 482 } 483 484 /* Drop ending line feed / carriage return */ 485 while (len > 0 && (dump[len - 1] == '\n' || 486 dump[len - 1] == '\r')) { 487 dump[len - 1] = '\0'; 488 len--; 489 } 490 491 /* Skip comment or empty lines */ 492 if ((len == 0) || dump[0] == '#') 493 continue; 494 495 /* 496 * Search for variable's name, 497 * remove leading whitespaces 498 */ 499 name = fw_string_blank(dump, 1); 500 if (!name) 501 continue; 502 503 /* The first white space is the end of variable name */ 504 val = fw_string_blank(name, 0); 505 len = strlen(name); 506 if (val) { 507 *val++ = '\0'; 508 if ((val - name) < len) 509 val = fw_string_blank(val, 1); 510 else 511 val = NULL; 512 } 513 514 #ifdef DEBUG 515 fprintf(stderr, "Setting %s : %s\n", 516 name, val ? val : " removed"); 517 #endif 518 519 /* 520 * If there is an error setting a variable, 521 * try to save the environment and returns an error 522 */ 523 if (fw_env_write(name, val)) { 524 fprintf(stderr, 525 "fw_env_write returns with error : %s\n", 526 strerror(errno)); 527 ret = -1; 528 break; 529 } 530 531 } 532 533 /* Close file if not stdin */ 534 if (strcmp(fname, "-") != 0) 535 fclose(fp); 536 537 ret |= fw_env_close(); 538 539 return ret; 540 541 } 542 543 /* 544 * Test for bad block on NAND, just returns 0 on NOR, on NAND: 545 * 0 - block is good 546 * > 0 - block is bad 547 * < 0 - failed to test 548 */ 549 static int flash_bad_block (int fd, uint8_t mtd_type, loff_t *blockstart) 550 { 551 if (mtd_type == MTD_NANDFLASH) { 552 int badblock = ioctl (fd, MEMGETBADBLOCK, blockstart); 553 554 if (badblock < 0) { 555 perror ("Cannot read bad block mark"); 556 return badblock; 557 } 558 559 if (badblock) { 560 #ifdef DEBUG 561 fprintf (stderr, "Bad block at 0x%llx, " 562 "skipping\n", *blockstart); 563 #endif 564 return badblock; 565 } 566 } 567 568 return 0; 569 } 570 571 /* 572 * Read data from flash at an offset into a provided buffer. On NAND it skips 573 * bad blocks but makes sure it stays within ENVSECTORS (dev) starting from 574 * the DEVOFFSET (dev) block. On NOR the loop is only run once. 575 */ 576 static int flash_read_buf (int dev, int fd, void *buf, size_t count, 577 off_t offset, uint8_t mtd_type) 578 { 579 size_t blocklen; /* erase / write length - one block on NAND, 580 0 on NOR */ 581 size_t processed = 0; /* progress counter */ 582 size_t readlen = count; /* current read length */ 583 off_t top_of_range; /* end of the last block we may use */ 584 off_t block_seek; /* offset inside the current block to the start 585 of the data */ 586 loff_t blockstart; /* running start of the current block - 587 MEMGETBADBLOCK needs 64 bits */ 588 int rc; 589 590 blockstart = (offset / DEVESIZE (dev)) * DEVESIZE (dev); 591 592 /* Offset inside a block */ 593 block_seek = offset - blockstart; 594 595 if (mtd_type == MTD_NANDFLASH) { 596 /* 597 * NAND: calculate which blocks we are reading. We have 598 * to read one block at a time to skip bad blocks. 599 */ 600 blocklen = DEVESIZE (dev); 601 602 /* 603 * To calculate the top of the range, we have to use the 604 * global DEVOFFSET (dev), which can be different from offset 605 */ 606 top_of_range = ((DEVOFFSET(dev) / blocklen) + 607 ENVSECTORS (dev)) * blocklen; 608 609 /* Limit to one block for the first read */ 610 if (readlen > blocklen - block_seek) 611 readlen = blocklen - block_seek; 612 } else { 613 blocklen = 0; 614 top_of_range = offset + count; 615 } 616 617 /* This only runs once on NOR flash */ 618 while (processed < count) { 619 rc = flash_bad_block (fd, mtd_type, &blockstart); 620 if (rc < 0) /* block test failed */ 621 return -1; 622 623 if (blockstart + block_seek + readlen > top_of_range) { 624 /* End of range is reached */ 625 fprintf (stderr, 626 "Too few good blocks within range\n"); 627 return -1; 628 } 629 630 if (rc) { /* block is bad */ 631 blockstart += blocklen; 632 continue; 633 } 634 635 /* 636 * If a block is bad, we retry in the next block at the same 637 * offset - see common/env_nand.c::writeenv() 638 */ 639 lseek (fd, blockstart + block_seek, SEEK_SET); 640 641 rc = read (fd, buf + processed, readlen); 642 if (rc != readlen) { 643 fprintf (stderr, "Read error on %s: %s\n", 644 DEVNAME (dev), strerror (errno)); 645 return -1; 646 } 647 #ifdef DEBUG 648 fprintf(stderr, "Read 0x%x bytes at 0x%llx on %s\n", 649 rc, blockstart + block_seek, DEVNAME(dev)); 650 #endif 651 processed += readlen; 652 readlen = min (blocklen, count - processed); 653 block_seek = 0; 654 blockstart += blocklen; 655 } 656 657 return processed; 658 } 659 660 /* 661 * Write count bytes at offset, but stay within ENVSECTORS (dev) sectors of 662 * DEVOFFSET (dev). Similar to the read case above, on NOR and dataflash we 663 * erase and write the whole data at once. 664 */ 665 static int flash_write_buf (int dev, int fd, void *buf, size_t count, 666 off_t offset, uint8_t mtd_type) 667 { 668 void *data; 669 struct erase_info_user erase; 670 size_t blocklen; /* length of NAND block / NOR erase sector */ 671 size_t erase_len; /* whole area that can be erased - may include 672 bad blocks */ 673 size_t erasesize; /* erase / write length - one block on NAND, 674 whole area on NOR */ 675 size_t processed = 0; /* progress counter */ 676 size_t write_total; /* total size to actually write - excluding 677 bad blocks */ 678 off_t erase_offset; /* offset to the first erase block (aligned) 679 below offset */ 680 off_t block_seek; /* offset inside the erase block to the start 681 of the data */ 682 off_t top_of_range; /* end of the last block we may use */ 683 loff_t blockstart; /* running start of the current block - 684 MEMGETBADBLOCK needs 64 bits */ 685 int rc; 686 687 blocklen = DEVESIZE (dev); 688 689 top_of_range = ((DEVOFFSET(dev) / blocklen) + 690 ENVSECTORS (dev)) * blocklen; 691 692 erase_offset = (offset / blocklen) * blocklen; 693 694 /* Maximum area we may use */ 695 erase_len = top_of_range - erase_offset; 696 697 blockstart = erase_offset; 698 /* Offset inside a block */ 699 block_seek = offset - erase_offset; 700 701 /* 702 * Data size we actually have to write: from the start of the block 703 * to the start of the data, then count bytes of data, and to the 704 * end of the block 705 */ 706 write_total = ((block_seek + count + blocklen - 1) / 707 blocklen) * blocklen; 708 709 /* 710 * Support data anywhere within erase sectors: read out the complete 711 * area to be erased, replace the environment image, write the whole 712 * block back again. 713 */ 714 if (write_total > count) { 715 data = malloc (erase_len); 716 if (!data) { 717 fprintf (stderr, 718 "Cannot malloc %zu bytes: %s\n", 719 erase_len, strerror (errno)); 720 return -1; 721 } 722 723 rc = flash_read_buf (dev, fd, data, write_total, erase_offset, 724 mtd_type); 725 if (write_total != rc) 726 return -1; 727 728 #ifdef DEBUG 729 fprintf(stderr, "Preserving data "); 730 if (block_seek != 0) 731 fprintf(stderr, "0x%x - 0x%lx", 0, block_seek - 1); 732 if (block_seek + count != write_total) { 733 if (block_seek != 0) 734 fprintf(stderr, " and "); 735 fprintf(stderr, "0x%lx - 0x%x", 736 block_seek + count, write_total - 1); 737 } 738 fprintf(stderr, "\n"); 739 #endif 740 /* Overwrite the old environment */ 741 memcpy (data + block_seek, buf, count); 742 } else { 743 /* 744 * We get here, iff offset is block-aligned and count is a 745 * multiple of blocklen - see write_total calculation above 746 */ 747 data = buf; 748 } 749 750 if (mtd_type == MTD_NANDFLASH) { 751 /* 752 * NAND: calculate which blocks we are writing. We have 753 * to write one block at a time to skip bad blocks. 754 */ 755 erasesize = blocklen; 756 } else { 757 erasesize = erase_len; 758 } 759 760 erase.length = erasesize; 761 762 /* This only runs once on NOR flash and SPI-dataflash */ 763 while (processed < write_total) { 764 rc = flash_bad_block (fd, mtd_type, &blockstart); 765 if (rc < 0) /* block test failed */ 766 return rc; 767 768 if (blockstart + erasesize > top_of_range) { 769 fprintf (stderr, "End of range reached, aborting\n"); 770 return -1; 771 } 772 773 if (rc) { /* block is bad */ 774 blockstart += blocklen; 775 continue; 776 } 777 778 erase.start = blockstart; 779 ioctl (fd, MEMUNLOCK, &erase); 780 781 /* Dataflash does not need an explicit erase cycle */ 782 if (mtd_type != MTD_DATAFLASH) 783 if (ioctl (fd, MEMERASE, &erase) != 0) { 784 fprintf (stderr, "MTD erase error on %s: %s\n", 785 DEVNAME (dev), 786 strerror (errno)); 787 return -1; 788 } 789 790 if (lseek (fd, blockstart, SEEK_SET) == -1) { 791 fprintf (stderr, 792 "Seek error on %s: %s\n", 793 DEVNAME (dev), strerror (errno)); 794 return -1; 795 } 796 797 #ifdef DEBUG 798 fprintf(stderr, "Write 0x%x bytes at 0x%llx\n", erasesize, 799 blockstart); 800 #endif 801 if (write (fd, data + processed, erasesize) != erasesize) { 802 fprintf (stderr, "Write error on %s: %s\n", 803 DEVNAME (dev), strerror (errno)); 804 return -1; 805 } 806 807 ioctl (fd, MEMLOCK, &erase); 808 809 processed += blocklen; 810 block_seek = 0; 811 blockstart += blocklen; 812 } 813 814 if (write_total > count) 815 free (data); 816 817 return processed; 818 } 819 820 /* 821 * Set obsolete flag at offset - NOR flash only 822 */ 823 static int flash_flag_obsolete (int dev, int fd, off_t offset) 824 { 825 int rc; 826 struct erase_info_user erase; 827 828 erase.start = DEVOFFSET (dev); 829 erase.length = DEVESIZE (dev); 830 /* This relies on the fact, that obsolete_flag == 0 */ 831 rc = lseek (fd, offset, SEEK_SET); 832 if (rc < 0) { 833 fprintf (stderr, "Cannot seek to set the flag on %s \n", 834 DEVNAME (dev)); 835 return rc; 836 } 837 ioctl (fd, MEMUNLOCK, &erase); 838 rc = write (fd, &obsolete_flag, sizeof (obsolete_flag)); 839 ioctl (fd, MEMLOCK, &erase); 840 if (rc < 0) 841 perror ("Could not set obsolete flag"); 842 843 return rc; 844 } 845 846 static int flash_write (int fd_current, int fd_target, int dev_target) 847 { 848 int rc; 849 850 switch (environment.flag_scheme) { 851 case FLAG_NONE: 852 break; 853 case FLAG_INCREMENTAL: 854 (*environment.flags)++; 855 break; 856 case FLAG_BOOLEAN: 857 *environment.flags = active_flag; 858 break; 859 default: 860 fprintf (stderr, "Unimplemented flash scheme %u \n", 861 environment.flag_scheme); 862 return -1; 863 } 864 865 #ifdef DEBUG 866 fprintf(stderr, "Writing new environment at 0x%lx on %s\n", 867 DEVOFFSET (dev_target), DEVNAME (dev_target)); 868 #endif 869 rc = flash_write_buf(dev_target, fd_target, environment.image, 870 CUR_ENVSIZE, DEVOFFSET(dev_target), 871 DEVTYPE(dev_target)); 872 if (rc < 0) 873 return rc; 874 875 if (environment.flag_scheme == FLAG_BOOLEAN) { 876 /* Have to set obsolete flag */ 877 off_t offset = DEVOFFSET (dev_current) + 878 offsetof (struct env_image_redundant, flags); 879 #ifdef DEBUG 880 fprintf(stderr, 881 "Setting obsolete flag in environment at 0x%lx on %s\n", 882 DEVOFFSET (dev_current), DEVNAME (dev_current)); 883 #endif 884 flash_flag_obsolete (dev_current, fd_current, offset); 885 } 886 887 return 0; 888 } 889 890 static int flash_read (int fd) 891 { 892 struct mtd_info_user mtdinfo; 893 int rc; 894 895 rc = ioctl (fd, MEMGETINFO, &mtdinfo); 896 if (rc < 0) { 897 perror ("Cannot get MTD information"); 898 return -1; 899 } 900 901 if (mtdinfo.type != MTD_NORFLASH && 902 mtdinfo.type != MTD_NANDFLASH && 903 mtdinfo.type != MTD_DATAFLASH) { 904 fprintf (stderr, "Unsupported flash type %u\n", mtdinfo.type); 905 return -1; 906 } 907 908 DEVTYPE(dev_current) = mtdinfo.type; 909 910 rc = flash_read_buf(dev_current, fd, environment.image, CUR_ENVSIZE, 911 DEVOFFSET (dev_current), mtdinfo.type); 912 913 return (rc != CUR_ENVSIZE) ? -1 : 0; 914 } 915 916 static int flash_io (int mode) 917 { 918 int fd_current, fd_target, rc, dev_target; 919 920 /* dev_current: fd_current, erase_current */ 921 fd_current = open (DEVNAME (dev_current), mode); 922 if (fd_current < 0) { 923 fprintf (stderr, 924 "Can't open %s: %s\n", 925 DEVNAME (dev_current), strerror (errno)); 926 return -1; 927 } 928 929 if (mode == O_RDWR) { 930 if (HaveRedundEnv) { 931 /* switch to next partition for writing */ 932 dev_target = !dev_current; 933 /* dev_target: fd_target, erase_target */ 934 fd_target = open (DEVNAME (dev_target), mode); 935 if (fd_target < 0) { 936 fprintf (stderr, 937 "Can't open %s: %s\n", 938 DEVNAME (dev_target), 939 strerror (errno)); 940 rc = -1; 941 goto exit; 942 } 943 } else { 944 dev_target = dev_current; 945 fd_target = fd_current; 946 } 947 948 rc = flash_write (fd_current, fd_target, dev_target); 949 950 if (HaveRedundEnv) { 951 if (close (fd_target)) { 952 fprintf (stderr, 953 "I/O error on %s: %s\n", 954 DEVNAME (dev_target), 955 strerror (errno)); 956 rc = -1; 957 } 958 } 959 } else { 960 rc = flash_read (fd_current); 961 } 962 963 exit: 964 if (close (fd_current)) { 965 fprintf (stderr, 966 "I/O error on %s: %s\n", 967 DEVNAME (dev_current), strerror (errno)); 968 return -1; 969 } 970 971 return rc; 972 } 973 974 /* 975 * s1 is either a simple 'name', or a 'name=value' pair. 976 * s2 is a 'name=value' pair. 977 * If the names match, return the value of s2, else NULL. 978 */ 979 980 static char *envmatch (char * s1, char * s2) 981 { 982 if (s1 == NULL || s2 == NULL) 983 return NULL; 984 985 while (*s1 == *s2++) 986 if (*s1++ == '=') 987 return s2; 988 if (*s1 == '\0' && *(s2 - 1) == '=') 989 return s2; 990 return NULL; 991 } 992 993 /* 994 * Prevent confusion if running from erased flash memory 995 */ 996 int fw_env_open(void) 997 { 998 int crc0, crc0_ok; 999 unsigned char flag0; 1000 void *addr0; 1001 1002 int crc1, crc1_ok; 1003 unsigned char flag1; 1004 void *addr1; 1005 1006 struct env_image_single *single; 1007 struct env_image_redundant *redundant; 1008 1009 if (parse_config ()) /* should fill envdevices */ 1010 return -1; 1011 1012 addr0 = calloc(1, CUR_ENVSIZE); 1013 if (addr0 == NULL) { 1014 fprintf(stderr, 1015 "Not enough memory for environment (%ld bytes)\n", 1016 CUR_ENVSIZE); 1017 return -1; 1018 } 1019 1020 /* read environment from FLASH to local buffer */ 1021 environment.image = addr0; 1022 1023 if (HaveRedundEnv) { 1024 redundant = addr0; 1025 environment.crc = &redundant->crc; 1026 environment.flags = &redundant->flags; 1027 environment.data = redundant->data; 1028 } else { 1029 single = addr0; 1030 environment.crc = &single->crc; 1031 environment.flags = NULL; 1032 environment.data = single->data; 1033 } 1034 1035 dev_current = 0; 1036 if (flash_io (O_RDONLY)) 1037 return -1; 1038 1039 crc0 = crc32 (0, (uint8_t *) environment.data, ENV_SIZE); 1040 crc0_ok = (crc0 == *environment.crc); 1041 if (!HaveRedundEnv) { 1042 if (!crc0_ok) { 1043 fprintf (stderr, 1044 "Warning: Bad CRC, using default environment\n"); 1045 memcpy(environment.data, default_environment, sizeof default_environment); 1046 } 1047 } else { 1048 flag0 = *environment.flags; 1049 1050 dev_current = 1; 1051 addr1 = calloc(1, CUR_ENVSIZE); 1052 if (addr1 == NULL) { 1053 fprintf(stderr, 1054 "Not enough memory for environment (%ld bytes)\n", 1055 CUR_ENVSIZE); 1056 return -1; 1057 } 1058 redundant = addr1; 1059 1060 /* 1061 * have to set environment.image for flash_read(), careful - 1062 * other pointers in environment still point inside addr0 1063 */ 1064 environment.image = addr1; 1065 if (flash_io (O_RDONLY)) 1066 return -1; 1067 1068 /* Check flag scheme compatibility */ 1069 if (DEVTYPE(dev_current) == MTD_NORFLASH && 1070 DEVTYPE(!dev_current) == MTD_NORFLASH) { 1071 environment.flag_scheme = FLAG_BOOLEAN; 1072 } else if (DEVTYPE(dev_current) == MTD_NANDFLASH && 1073 DEVTYPE(!dev_current) == MTD_NANDFLASH) { 1074 environment.flag_scheme = FLAG_INCREMENTAL; 1075 } else if (DEVTYPE(dev_current) == MTD_DATAFLASH && 1076 DEVTYPE(!dev_current) == MTD_DATAFLASH) { 1077 environment.flag_scheme = FLAG_BOOLEAN; 1078 } else { 1079 fprintf (stderr, "Incompatible flash types!\n"); 1080 return -1; 1081 } 1082 1083 crc1 = crc32 (0, (uint8_t *) redundant->data, ENV_SIZE); 1084 crc1_ok = (crc1 == redundant->crc); 1085 flag1 = redundant->flags; 1086 1087 if (crc0_ok && !crc1_ok) { 1088 dev_current = 0; 1089 } else if (!crc0_ok && crc1_ok) { 1090 dev_current = 1; 1091 } else if (!crc0_ok && !crc1_ok) { 1092 fprintf (stderr, 1093 "Warning: Bad CRC, using default environment\n"); 1094 memcpy (environment.data, default_environment, 1095 sizeof default_environment); 1096 dev_current = 0; 1097 } else { 1098 switch (environment.flag_scheme) { 1099 case FLAG_BOOLEAN: 1100 if (flag0 == active_flag && 1101 flag1 == obsolete_flag) { 1102 dev_current = 0; 1103 } else if (flag0 == obsolete_flag && 1104 flag1 == active_flag) { 1105 dev_current = 1; 1106 } else if (flag0 == flag1) { 1107 dev_current = 0; 1108 } else if (flag0 == 0xFF) { 1109 dev_current = 0; 1110 } else if (flag1 == 0xFF) { 1111 dev_current = 1; 1112 } else { 1113 dev_current = 0; 1114 } 1115 break; 1116 case FLAG_INCREMENTAL: 1117 if (flag0 == 255 && flag1 == 0) 1118 dev_current = 1; 1119 else if ((flag1 == 255 && flag0 == 0) || 1120 flag0 >= flag1) 1121 dev_current = 0; 1122 else /* flag1 > flag0 */ 1123 dev_current = 1; 1124 break; 1125 default: 1126 fprintf (stderr, "Unknown flag scheme %u \n", 1127 environment.flag_scheme); 1128 return -1; 1129 } 1130 } 1131 1132 /* 1133 * If we are reading, we don't need the flag and the CRC any 1134 * more, if we are writing, we will re-calculate CRC and update 1135 * flags before writing out 1136 */ 1137 if (dev_current) { 1138 environment.image = addr1; 1139 environment.crc = &redundant->crc; 1140 environment.flags = &redundant->flags; 1141 environment.data = redundant->data; 1142 free (addr0); 1143 } else { 1144 environment.image = addr0; 1145 /* Other pointers are already set */ 1146 free (addr1); 1147 } 1148 #ifdef DEBUG 1149 fprintf(stderr, "Selected env in %s\n", DEVNAME(dev_current)); 1150 #endif 1151 } 1152 return 0; 1153 } 1154 1155 1156 static int parse_config () 1157 { 1158 struct stat st; 1159 1160 #if defined(CONFIG_FILE) 1161 /* Fills in DEVNAME(), ENVSIZE(), DEVESIZE(). Or don't. */ 1162 if (get_config (CONFIG_FILE)) { 1163 fprintf (stderr, 1164 "Cannot parse config file: %s\n", strerror (errno)); 1165 return -1; 1166 } 1167 #else 1168 strcpy (DEVNAME (0), DEVICE1_NAME); 1169 DEVOFFSET (0) = DEVICE1_OFFSET; 1170 ENVSIZE (0) = ENV1_SIZE; 1171 /* Default values are: erase-size=env-size, #sectors=1 */ 1172 DEVESIZE (0) = ENVSIZE (0); 1173 ENVSECTORS (0) = 1; 1174 #ifdef DEVICE1_ESIZE 1175 DEVESIZE (0) = DEVICE1_ESIZE; 1176 #endif 1177 #ifdef DEVICE1_ENVSECTORS 1178 ENVSECTORS (0) = DEVICE1_ENVSECTORS; 1179 #endif 1180 1181 #ifdef HAVE_REDUND 1182 strcpy (DEVNAME (1), DEVICE2_NAME); 1183 DEVOFFSET (1) = DEVICE2_OFFSET; 1184 ENVSIZE (1) = ENV2_SIZE; 1185 /* Default values are: erase-size=env-size, #sectors=1 */ 1186 DEVESIZE (1) = ENVSIZE (1); 1187 ENVSECTORS (1) = 1; 1188 #ifdef DEVICE2_ESIZE 1189 DEVESIZE (1) = DEVICE2_ESIZE; 1190 #endif 1191 #ifdef DEVICE2_ENVSECTORS 1192 ENVSECTORS (1) = DEVICE2_ENVSECTORS; 1193 #endif 1194 HaveRedundEnv = 1; 1195 #endif 1196 #endif 1197 if (stat (DEVNAME (0), &st)) { 1198 fprintf (stderr, 1199 "Cannot access MTD device %s: %s\n", 1200 DEVNAME (0), strerror (errno)); 1201 return -1; 1202 } 1203 1204 if (HaveRedundEnv && stat (DEVNAME (1), &st)) { 1205 fprintf (stderr, 1206 "Cannot access MTD device %s: %s\n", 1207 DEVNAME (1), strerror (errno)); 1208 return -1; 1209 } 1210 return 0; 1211 } 1212 1213 #if defined(CONFIG_FILE) 1214 static int get_config (char *fname) 1215 { 1216 FILE *fp; 1217 int i = 0; 1218 int rc; 1219 char dump[128]; 1220 1221 fp = fopen (fname, "r"); 1222 if (fp == NULL) 1223 return -1; 1224 1225 while (i < 2 && fgets (dump, sizeof (dump), fp)) { 1226 /* Skip incomplete conversions and comment strings */ 1227 if (dump[0] == '#') 1228 continue; 1229 1230 rc = sscanf (dump, "%s %lx %lx %lx %lx", 1231 DEVNAME (i), 1232 &DEVOFFSET (i), 1233 &ENVSIZE (i), 1234 &DEVESIZE (i), 1235 &ENVSECTORS (i)); 1236 1237 if (rc < 3) 1238 continue; 1239 1240 if (rc < 4) 1241 /* Assume the erase size is the same as the env-size */ 1242 DEVESIZE(i) = ENVSIZE(i); 1243 1244 if (rc < 5) 1245 /* Default - 1 sector */ 1246 ENVSECTORS (i) = 1; 1247 1248 i++; 1249 } 1250 fclose (fp); 1251 1252 HaveRedundEnv = i - 1; 1253 if (!i) { /* No valid entries found */ 1254 errno = EINVAL; 1255 return -1; 1256 } else 1257 return 0; 1258 } 1259 #endif 1260