]> Git Repo - J-u-boot.git/blame - tools/env/fw_env.c
rename CFG_ENV macros to CONFIG_ENV
[J-u-boot.git] / tools / env / fw_env.c
CommitLineData
6aff3115 1/*
bc11756d 2 * (C) Copyright 2000-2008
6aff3115
WD
3 * Wolfgang Denk, DENX Software Engineering, [email protected].
4 *
56086921
GL
5 * (C) Copyright 2008
6 * Guennadi Liakhovetski, DENX Software Engineering, [email protected].
7 *
6aff3115
WD
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
4a6fd34b 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
6aff3115
WD
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>
6aff3115 37
6de66b35 38#ifdef MTD_OLD
1711f3bd 39# include <stdint.h>
6de66b35
MK
40# include <linux/mtd/mtd.h>
41#else
c83d7ca4 42# define __user /* nothing */
6de66b35
MK
43# include <mtd/mtd-user.h>
44#endif
45
46#include "fw_env.h"
6aff3115
WD
47
48#define CMD_GETENV "fw_printenv"
49#define CMD_SETENV "fw_setenv"
50
56086921
GL
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
57struct envdev_s {
6de66b35 58 char devname[16]; /* Device name */
4a6fd34b
WD
59 ulong devoff; /* Device offset */
60 ulong env_size; /* environment size */
61 ulong erase_size; /* device erase size */
56086921
GL
62 ulong env_sectors; /* number of environment sectors */
63 uint8_t mtd_type; /* type of the MTD device */
64};
6aff3115 65
56086921
GL
66static struct envdev_s envdevices[2] =
67{
68 {
69 .mtd_type = MTD_ABSENT,
70 }, {
71 .mtd_type = MTD_ABSENT,
72 },
73};
74static int dev_current;
6aff3115
WD
75
76#define DEVNAME(i) envdevices[(i)].devname
d0fb80c3 77#define DEVOFFSET(i) envdevices[(i)].devoff
6aff3115
WD
78#define ENVSIZE(i) envdevices[(i)].env_size
79#define DEVESIZE(i) envdevices[(i)].erase_size
56086921
GL
80#define ENVSECTORS(i) envdevices[(i)].env_sectors
81#define DEVTYPE(i) envdevices[(i)].mtd_type
6aff3115 82
0e8d1586 83#define CONFIG_ENV_SIZE ENVSIZE(dev_current)
6aff3115 84
d0fb80c3 85#define ENV_SIZE getenvsize()
6aff3115 86
56086921
GL
87struct env_image_single {
88 uint32_t crc; /* CRC32 over data bytes */
89 char data[];
90};
6aff3115 91
56086921
GL
92struct env_image_redundant {
93 uint32_t crc; /* CRC32 over data bytes */
94 unsigned char flags; /* active or obsolete */
95 char data[];
96};
97
98enum flag_scheme {
99 FLAG_NONE,
100 FLAG_BOOLEAN,
101 FLAG_INCREMENTAL,
102};
103
104struct environment {
105 void *image;
106 uint32_t *crc;
107 unsigned char *flags;
108 char *data;
109 enum flag_scheme flag_scheme;
110};
111
112static struct environment environment = {
113 .flag_scheme = FLAG_NONE,
114};
6aff3115 115
d0fb80c3
WD
116static int HaveRedundEnv = 0;
117
6de66b35 118static unsigned char active_flag = 1;
56086921 119/* obsolete_flag must be 0 to efficiently set it on NOR flash without erasing */
6de66b35 120static unsigned char obsolete_flag = 0;
d0fb80c3 121
6aff3115
WD
122
123#define XMK_STR(x) #x
124#define MK_STR(x) XMK_STR(x)
125
6de66b35 126static char default_environment[] = {
d0fb80c3 127#if defined(CONFIG_BOOTARGS)
4a6fd34b 128 "bootargs=" CONFIG_BOOTARGS "\0"
6aff3115 129#endif
d0fb80c3 130#if defined(CONFIG_BOOTCOMMAND)
4a6fd34b 131 "bootcmd=" CONFIG_BOOTCOMMAND "\0"
6aff3115 132#endif
d0fb80c3 133#if defined(CONFIG_RAMBOOTCOMMAND)
4a6fd34b 134 "ramboot=" CONFIG_RAMBOOTCOMMAND "\0"
d0fb80c3
WD
135#endif
136#if defined(CONFIG_NFSBOOTCOMMAND)
4a6fd34b 137 "nfsboot=" CONFIG_NFSBOOTCOMMAND "\0"
d0fb80c3
WD
138#endif
139#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
4a6fd34b 140 "bootdelay=" MK_STR (CONFIG_BOOTDELAY) "\0"
6aff3115 141#endif
d0fb80c3 142#if defined(CONFIG_BAUDRATE) && (CONFIG_BAUDRATE >= 0)
4a6fd34b 143 "baudrate=" MK_STR (CONFIG_BAUDRATE) "\0"
6aff3115 144#endif
d0fb80c3 145#ifdef CONFIG_LOADS_ECHO
4a6fd34b 146 "loads_echo=" MK_STR (CONFIG_LOADS_ECHO) "\0"
d0fb80c3 147#endif
6aff3115 148#ifdef CONFIG_ETHADDR
4a6fd34b 149 "ethaddr=" MK_STR (CONFIG_ETHADDR) "\0"
6aff3115 150#endif
d0fb80c3 151#ifdef CONFIG_ETH1ADDR
4a6fd34b 152 "eth1addr=" MK_STR (CONFIG_ETH1ADDR) "\0"
d0fb80c3
WD
153#endif
154#ifdef CONFIG_ETH2ADDR
4a6fd34b 155 "eth2addr=" MK_STR (CONFIG_ETH2ADDR) "\0"
d0fb80c3 156#endif
e2ffd59b
WD
157#ifdef CONFIG_ETH3ADDR
158 "eth3addr=" MK_STR (CONFIG_ETH3ADDR) "\0"
159#endif
d0fb80c3 160#ifdef CONFIG_ETHPRIME
4a6fd34b 161 "ethprime=" CONFIG_ETHPRIME "\0"
d0fb80c3 162#endif
6aff3115 163#ifdef CONFIG_IPADDR
4a6fd34b 164 "ipaddr=" MK_STR (CONFIG_IPADDR) "\0"
6aff3115
WD
165#endif
166#ifdef CONFIG_SERVERIP
4a6fd34b 167 "serverip=" MK_STR (CONFIG_SERVERIP) "\0"
6aff3115 168#endif
d0fb80c3 169#ifdef CFG_AUTOLOAD
4a6fd34b 170 "autoload=" CFG_AUTOLOAD "\0"
d0fb80c3
WD
171#endif
172#ifdef CONFIG_ROOTPATH
4a6fd34b 173 "rootpath=" MK_STR (CONFIG_ROOTPATH) "\0"
d0fb80c3
WD
174#endif
175#ifdef CONFIG_GATEWAYIP
4a6fd34b 176 "gatewayip=" MK_STR (CONFIG_GATEWAYIP) "\0"
d0fb80c3
WD
177#endif
178#ifdef CONFIG_NETMASK
4a6fd34b 179 "netmask=" MK_STR (CONFIG_NETMASK) "\0"
d0fb80c3
WD
180#endif
181#ifdef CONFIG_HOSTNAME
4a6fd34b 182 "hostname=" MK_STR (CONFIG_HOSTNAME) "\0"
d0fb80c3
WD
183#endif
184#ifdef CONFIG_BOOTFILE
4a6fd34b 185 "bootfile=" MK_STR (CONFIG_BOOTFILE) "\0"
d0fb80c3
WD
186#endif
187#ifdef CONFIG_LOADADDR
4a6fd34b 188 "loadaddr=" MK_STR (CONFIG_LOADADDR) "\0"
d0fb80c3
WD
189#endif
190#ifdef CONFIG_PREBOOT
4a6fd34b 191 "preboot=" CONFIG_PREBOOT "\0"
d0fb80c3
WD
192#endif
193#ifdef CONFIG_CLOCKS_IN_MHZ
4a6fd34b 194 "clocks_in_mhz=" "1" "\0"
d0fb80c3 195#endif
ad10dd9a 196#if defined(CONFIG_PCI_BOOTDELAY) && (CONFIG_PCI_BOOTDELAY > 0)
4a6fd34b 197 "pcidelay=" MK_STR (CONFIG_PCI_BOOTDELAY) "\0"
ad10dd9a 198#endif
d0fb80c3
WD
199#ifdef CONFIG_EXTRA_ENV_SETTINGS
200 CONFIG_EXTRA_ENV_SETTINGS
201#endif
56086921 202 "\0" /* Termimate struct environment data with 2 NULs */
6aff3115
WD
203};
204
4a6fd34b 205static int flash_io (int mode);
6de66b35 206static char *envmatch (char * s1, char * s2);
4a6fd34b
WD
207static int env_init (void);
208static int parse_config (void);
209
d0fb80c3 210#if defined(CONFIG_FILE)
4a6fd34b 211static int get_config (char *);
d0fb80c3 212#endif
4a6fd34b 213static inline ulong getenvsize (void)
d0fb80c3 214{
0e8d1586 215 ulong rc = CONFIG_ENV_SIZE - sizeof (long);
4a6fd34b 216
d0fb80c3 217 if (HaveRedundEnv)
4a6fd34b 218 rc -= sizeof (char);
d0fb80c3
WD
219 return rc;
220}
6aff3115
WD
221
222/*
223 * Search the environment for a variable.
224 * Return the value, if found, or NULL, if not found.
225 */
6de66b35 226char *fw_getenv (char *name)
6aff3115 227{
6de66b35 228 char *env, *nxt;
6aff3115 229
4a6fd34b 230 if (env_init ())
56086921 231 return NULL;
6aff3115 232
4a6fd34b 233 for (env = environment.data; *env; env = nxt + 1) {
6de66b35 234 char *val;
6aff3115 235
4a6fd34b 236 for (nxt = env; *nxt; ++nxt) {
6aff3115
WD
237 if (nxt >= &environment.data[ENV_SIZE]) {
238 fprintf (stderr, "## Error: "
239 "environment not terminated\n");
56086921 240 return NULL;
6aff3115
WD
241 }
242 }
4a6fd34b 243 val = envmatch (name, env);
6aff3115
WD
244 if (!val)
245 continue;
56086921 246 return val;
6aff3115 247 }
56086921 248 return NULL;
6aff3115
WD
249}
250
251/*
252 * Print the current definition of one, or more, or all
253 * environment variables
254 */
bc11756d 255int fw_printenv (int argc, char *argv[])
6aff3115 256{
6de66b35 257 char *env, *nxt;
6aff3115 258 int i, n_flag;
bc11756d 259 int rc = 0;
6aff3115 260
4a6fd34b 261 if (env_init ())
56086921 262 return -1;
6aff3115 263
4a6fd34b
WD
264 if (argc == 1) { /* Print all env variables */
265 for (env = environment.data; *env; env = nxt + 1) {
266 for (nxt = env; *nxt; ++nxt) {
6aff3115
WD
267 if (nxt >= &environment.data[ENV_SIZE]) {
268 fprintf (stderr, "## Error: "
269 "environment not terminated\n");
56086921 270 return -1;
6aff3115
WD
271 }
272 }
273
4a6fd34b 274 printf ("%s\n", env);
6aff3115 275 }
56086921 276 return 0;
6aff3115
WD
277 }
278
4a6fd34b 279 if (strcmp (argv[1], "-n") == 0) {
6aff3115
WD
280 n_flag = 1;
281 ++argv;
282 --argc;
283 if (argc != 2) {
284 fprintf (stderr, "## Error: "
285 "`-n' option requires exactly one argument\n");
56086921 286 return -1;
6aff3115
WD
287 }
288 } else {
289 n_flag = 0;
290 }
291
4a6fd34b 292 for (i = 1; i < argc; ++i) { /* print single env variables */
6de66b35
MK
293 char *name = argv[i];
294 char *val = NULL;
6aff3115 295
4a6fd34b 296 for (env = environment.data; *env; env = nxt + 1) {
6aff3115 297
4a6fd34b 298 for (nxt = env; *nxt; ++nxt) {
6aff3115
WD
299 if (nxt >= &environment.data[ENV_SIZE]) {
300 fprintf (stderr, "## Error: "
301 "environment not terminated\n");
56086921 302 return -1;
6aff3115
WD
303 }
304 }
4a6fd34b 305 val = envmatch (name, env);
6aff3115
WD
306 if (val) {
307 if (!n_flag) {
308 fputs (name, stdout);
4a6fd34b 309 putc ('=', stdout);
6aff3115 310 }
4a6fd34b 311 puts (val);
6aff3115
WD
312 break;
313 }
314 }
bc11756d 315 if (!val) {
4a6fd34b 316 fprintf (stderr, "## Error: \"%s\" not defined\n", name);
bc11756d
GE
317 rc = -1;
318 }
6aff3115 319 }
bc11756d 320
56086921 321 return rc;
6aff3115
WD
322}
323
324/*
56086921 325 * Deletes or sets environment variables. Returns -1 and sets errno error codes:
6aff3115
WD
326 * 0 - OK
327 * EINVAL - need at least 1 argument
328 * EROFS - certain variables ("ethaddr", "serial#") cannot be
329 * modified or deleted
330 *
331 */
332int fw_setenv (int argc, char *argv[])
333{
4a6fd34b 334 int i, len;
6de66b35
MK
335 char *env, *nxt;
336 char *oldval = NULL;
337 char *name;
6aff3115
WD
338
339 if (argc < 2) {
56086921
GL
340 errno = EINVAL;
341 return -1;
6aff3115
WD
342 }
343
4a6fd34b 344 if (env_init ())
56086921 345 return -1;
6aff3115
WD
346
347 name = argv[1];
348
349 /*
350 * search if variable with this name already exists
351 */
4a6fd34b
WD
352 for (nxt = env = environment.data; *env; env = nxt + 1) {
353 for (nxt = env; *nxt; ++nxt) {
6aff3115
WD
354 if (nxt >= &environment.data[ENV_SIZE]) {
355 fprintf (stderr, "## Error: "
356 "environment not terminated\n");
56086921
GL
357 errno = EINVAL;
358 return -1;
6aff3115
WD
359 }
360 }
4a6fd34b 361 if ((oldval = envmatch (name, env)) != NULL)
6aff3115
WD
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) ||
4a6fd34b 373 (strcmp (name, "serial#") == 0)) {
6aff3115 374 fprintf (stderr, "Can't overwrite \"%s\"\n", name);
56086921
GL
375 errno = EROFS;
376 return -1;
6aff3115
WD
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 */
4a6fd34b 399 for (env = environment.data; *env || *(env + 1); ++env);
6aff3115
WD
400 if (env > environment.data)
401 ++env;
402 /*
403 * Overflow when:
0e8d1586 404 * "name" + "=" + "val" +"\0\0" > CONFIG_ENV_SIZE - (env-environment)
6aff3115 405 */
4a6fd34b 406 len = strlen (name) + 2;
6aff3115 407 /* add '=' for first arg, ' ' for all others */
4a6fd34b
WD
408 for (i = 2; i < argc; ++i) {
409 len += strlen (argv[i]) + 1;
6aff3115 410 }
4a6fd34b 411 if (len > (&environment.data[ENV_SIZE] - env)) {
6aff3115
WD
412 fprintf (stderr,
413 "Error: environment overflow, \"%s\" deleted\n",
414 name);
56086921 415 return -1;
6aff3115
WD
416 }
417 while ((*env = *name++) != '\0')
418 env++;
4a6fd34b 419 for (i = 2; i < argc; ++i) {
6de66b35 420 char *val = argv[i];
6aff3115 421
4a6fd34b
WD
422 *env = (i == 2) ? '=' : ' ';
423 while ((*++env = *val++) != '\0');
6aff3115
WD
424 }
425
426 /* end is marked with double '\0' */
427 *++env = '\0';
428
4a6fd34b 429 WRITE_FLASH:
6aff3115 430
56086921
GL
431 /*
432 * Update CRC
433 */
434 *environment.crc = crc32 (0, (uint8_t *) environment.data, ENV_SIZE);
6aff3115
WD
435
436 /* write environment back to flash */
437 if (flash_io (O_RDWR)) {
4a6fd34b 438 fprintf (stderr, "Error: can't write fw_env to flash\n");
56086921 439 return -1;
6aff3115
WD
440 }
441
56086921 442 return 0;
6aff3115
WD
443}
444
56086921
GL
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 */
451static int flash_bad_block (int fd, uint8_t mtd_type, loff_t *blockstart)
6aff3115 452{
56086921
GL
453 if (mtd_type == MTD_NANDFLASH) {
454 int badblock = ioctl (fd, MEMGETBADBLOCK, blockstart);
6aff3115 455
56086921
GL
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 }
6aff3115
WD
468 }
469
56086921
GL
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 */
478static 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;
d0fb80c3 521 }
6aff3115 522
56086921
GL
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;
6aff3115 528
56086921
GL
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;
d0fb80c3
WD
534 }
535
56086921
GL
536 if (rc) { /* block is bad */
537 blockstart += blocklen;
538 continue;
6aff3115
WD
539 }
540
56086921
GL
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);
6aff3115 546
56086921
GL
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;
6aff3115 552 }
56086921
GL
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 */
571static 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;
6aff3115 598
56086921 599 erase_offset = offset & ~(blocklen - 1);
6aff3115 600
56086921
GL
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) {
d0fb80c3 623 fprintf (stderr,
56086921
GL
624 "Cannot malloc %u bytes: %s\n",
625 erase_len, strerror (errno));
626 return -1;
d0fb80c3 627 }
56086921
GL
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;
6aff3115 665 }
56086921
GL
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) {
6aff3115 683 fprintf (stderr,
56086921
GL
684 "Seek error on %s: %s\n",
685 DEVNAME (dev), strerror (errno));
686 return -1;
6aff3115 687 }
56086921
GL
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;
6aff3115 696 }
56086921
GL
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 */
714static 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
732static 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,
0e8d1586 756 CONFIG_ENV_SIZE, DEVOFFSET (dev_target),
56086921
GL
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
775static 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
0e8d1586 793 rc = flash_read_buf (dev_current, fd, environment.image, CONFIG_ENV_SIZE,
56086921
GL
794 DEVOFFSET (dev_current), mtdinfo.type);
795
0e8d1586 796 return (rc != CONFIG_ENV_SIZE) ? -1 : 0;
56086921
GL
797}
798
799static 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) {
d0fb80c3 813 if (HaveRedundEnv) {
56086921
GL
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) {
d0fb80c3 819 fprintf (stderr,
56086921
GL
820 "Can't open %s: %s\n",
821 DEVNAME (dev_target),
822 strerror (errno));
823 rc = -1;
824 goto exit;
d0fb80c3 825 }
56086921
GL
826 } else {
827 dev_target = dev_current;
828 fd_target = fd_current;
6aff3115 829 }
56086921
GL
830
831 rc = flash_write (fd_current, fd_target, dev_target);
832
d0fb80c3 833 if (HaveRedundEnv) {
56086921 834 if (close (fd_target)) {
d0fb80c3 835 fprintf (stderr,
4a6fd34b 836 "I/O error on %s: %s\n",
56086921 837 DEVNAME (dev_target),
4a6fd34b 838 strerror (errno));
56086921 839 rc = -1;
d0fb80c3 840 }
6aff3115 841 }
6aff3115 842 } else {
56086921 843 rc = flash_read (fd_current);
6aff3115
WD
844 }
845
56086921
GL
846exit:
847 if (close (fd_current)) {
6aff3115 848 fprintf (stderr,
56086921
GL
849 "I/O error on %s: %s\n",
850 DEVNAME (dev_current), strerror (errno));
851 return -1;
6aff3115
WD
852 }
853
56086921 854 return rc;
6aff3115
WD
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
6de66b35 863static char *envmatch (char * s1, char * s2)
6aff3115
WD
864{
865
866 while (*s1 == *s2++)
867 if (*s1++ == '=')
56086921 868 return s2;
4a6fd34b 869 if (*s1 == '\0' && *(s2 - 1) == '=')
56086921
GL
870 return s2;
871 return NULL;
6aff3115
WD
872}
873
874/*
875 * Prevent confusion if running from erased flash memory
876 */
4a6fd34b 877static int env_init (void)
6aff3115 878{
56086921
GL
879 int crc0, crc0_ok;
880 char flag0;
881 void *addr0;
882
6aff3115 883 int crc1, crc1_ok;
56086921
GL
884 char flag1;
885 void *addr1;
d0fb80c3 886
56086921
GL
887 struct env_image_single *single;
888 struct env_image_redundant *redundant;
6aff3115 889
4a6fd34b 890 if (parse_config ()) /* should fill envdevices */
56086921 891 return -1;
4a6fd34b 892
0e8d1586 893 addr0 = calloc (1, CONFIG_ENV_SIZE);
56086921 894 if (addr0 == NULL) {
4a6fd34b
WD
895 fprintf (stderr,
896 "Not enough memory for environment (%ld bytes)\n",
0e8d1586 897 CONFIG_ENV_SIZE);
56086921 898 return -1;
d0fb80c3 899 }
4a6fd34b 900
d0fb80c3 901 /* read environment from FLASH to local buffer */
56086921
GL
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;
d0fb80c3 914 }
4a6fd34b 915
56086921
GL
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);
d0fb80c3 922 if (!HaveRedundEnv) {
56086921 923 if (!crc0_ok) {
4a6fd34b
WD
924 fprintf (stderr,
925 "Warning: Bad CRC, using default environment\n");
f07217c9 926 memcpy(environment.data, default_environment, sizeof default_environment);
6aff3115 927 }
d0fb80c3 928 } else {
56086921 929 flag0 = *environment.flags;
4a6fd34b 930
56086921 931 dev_current = 1;
0e8d1586 932 addr1 = calloc (1, CONFIG_ENV_SIZE);
56086921 933 if (addr1 == NULL) {
4a6fd34b
WD
934 fprintf (stderr,
935 "Not enough memory for environment (%ld bytes)\n",
0e8d1586 936 CONFIG_ENV_SIZE);
56086921 937 return -1;
4a6fd34b 938 }
56086921 939 redundant = addr1;
4a6fd34b 940
56086921
GL
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;
6aff3115 959 }
4a6fd34b 960
56086921
GL
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) {
4a6fd34b
WD
970 fprintf (stderr,
971 "Warning: Bad CRC, using default environment\n");
56086921
GL
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 */
4a6fd34b 1025 free (addr1);
6aff3115 1026 }
6aff3115 1027 }
56086921 1028 return 0;
6aff3115
WD
1029}
1030
1031
4a6fd34b 1032static int parse_config ()
6aff3115
WD
1033{
1034 struct stat st;
1035
d0fb80c3
WD
1036#if defined(CONFIG_FILE)
1037 /* Fills in DEVNAME(), ENVSIZE(), DEVESIZE(). Or don't. */
4a6fd34b 1038 if (get_config (CONFIG_FILE)) {
d0fb80c3 1039 fprintf (stderr,
4a6fd34b 1040 "Cannot parse config file: %s\n", strerror (errno));
56086921 1041 return -1;
6aff3115 1042 }
d0fb80c3 1043#else
4a6fd34b
WD
1044 strcpy (DEVNAME (0), DEVICE1_NAME);
1045 DEVOFFSET (0) = DEVICE1_OFFSET;
1046 ENVSIZE (0) = ENV1_SIZE;
1047 DEVESIZE (0) = DEVICE1_ESIZE;
56086921 1048 ENVSECTORS (0) = DEVICE1_ENVSECTORS;
6aff3115 1049#ifdef HAVE_REDUND
4a6fd34b
WD
1050 strcpy (DEVNAME (1), DEVICE2_NAME);
1051 DEVOFFSET (1) = DEVICE2_OFFSET;
1052 ENVSIZE (1) = ENV2_SIZE;
1053 DEVESIZE (1) = DEVICE2_ESIZE;
56086921 1054 ENVSECTORS (1) = DEVICE2_ENVSECTORS;
d0fb80c3 1055 HaveRedundEnv = 1;
6aff3115 1056#endif
d0fb80c3 1057#endif
4a6fd34b
WD
1058 if (stat (DEVNAME (0), &st)) {
1059 fprintf (stderr,
1060 "Cannot access MTD device %s: %s\n",
1061 DEVNAME (0), strerror (errno));
56086921 1062 return -1;
d0fb80c3 1063 }
4a6fd34b
WD
1064
1065 if (HaveRedundEnv && stat (DEVNAME (1), &st)) {
1066 fprintf (stderr,
1067 "Cannot access MTD device %s: %s\n",
e2146b6a 1068 DEVNAME (1), strerror (errno));
56086921 1069 return -1;
d0fb80c3 1070 }
6aff3115
WD
1071 return 0;
1072}
d0fb80c3
WD
1073
1074#if defined(CONFIG_FILE)
1075static int get_config (char *fname)
1076{
1077 FILE *fp;
1078 int i = 0;
1079 int rc;
1080 char dump[128];
1081
56086921
GL
1082 fp = fopen (fname, "r");
1083 if (fp == NULL)
1084 return -1;
d0fb80c3 1085
56086921 1086 while (i < 2 && fgets (dump, sizeof (dump), fp)) {
d0fb80c3 1087 /* Skip incomplete conversions and comment strings */
56086921 1088 if (dump[0] == '#')
d0fb80c3 1089 continue;
56086921
GL
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;
d0fb80c3
WD
1104
1105 i++;
1106 }
4a6fd34b
WD
1107 fclose (fp);
1108
d0fb80c3 1109 HaveRedundEnv = i - 1;
4a6fd34b 1110 if (!i) { /* No valid entries found */
d0fb80c3 1111 errno = EINVAL;
56086921 1112 return -1;
d0fb80c3
WD
1113 } else
1114 return 0;
1115}
1116#endif
This page took 0.302818 seconds and 4 git commands to generate.