blob: 876bf2b2daafe3a273c0a9a4211e6db3e43a421c [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
wdenk6aff3112002-12-17 01:51:00 +00002/*
Detlev Zundel0aef7bc2010-07-30 11:22:15 +02003 * (C) Copyright 2000-2010
wdenk6aff3112002-12-17 01:51:00 +00004 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
5 *
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02006 * (C) Copyright 2008
7 * Guennadi Liakhovetski, DENX Software Engineering, lg@denx.de.
wdenk6aff3112002-12-17 01:51:00 +00008 */
9
Jörg Krause26e355d2015-04-22 21:36:22 +020010#define _GNU_SOURCE
11
Peter Robinson69bf2d22015-12-09 07:15:33 +000012#include <compiler.h>
Simon Glass9fb625c2019-08-01 09:46:51 -060013#include <env.h>
wdenk6aff3112002-12-17 01:51:00 +000014#include <errno.h>
Joe Hershberger30fd4fad2012-12-11 22:16:32 -060015#include <env_flags.h>
wdenk6aff3112002-12-17 01:51:00 +000016#include <fcntl.h>
Alex Kiernandbc34322018-03-09 12:13:02 +000017#include <libgen.h>
Stefan Agnerf4742ca2016-07-13 17:14:38 -070018#include <linux/fs.h>
Joe Hershberger06134212012-10-12 10:23:37 +000019#include <linux/stringify.h>
Andreas Fenkart10667e12016-03-11 09:39:35 +010020#include <ctype.h>
wdenk6aff3112002-12-17 01:51:00 +000021#include <stdio.h>
22#include <stdlib.h>
23#include <stddef.h>
24#include <string.h>
25#include <sys/types.h>
26#include <sys/ioctl.h>
27#include <sys/stat.h>
28#include <unistd.h>
S. Lockwood-Childs34255b92017-11-14 23:01:26 -080029#include <dirent.h>
wdenk6aff3112002-12-17 01:51:00 +000030
Markus Klotzbücher6de66b32007-11-27 10:23:20 +010031#ifdef MTD_OLD
Wolfgang Denk1711f3b2008-09-02 21:17:36 +020032# include <stdint.h>
Markus Klotzbücher6de66b32007-11-27 10:23:20 +010033# include <linux/mtd/mtd.h>
34#else
Wolfgang Denkc83d7ca2008-01-08 22:58:27 +010035# define __user /* nothing */
Markus Klotzbücher6de66b32007-11-27 10:23:20 +010036# include <mtd/mtd-user.h>
37#endif
38
S. Lockwood-Childs34255b92017-11-14 23:01:26 -080039#include <mtd/ubi-user.h>
40
Stefano Babic9d80b492017-04-05 18:08:01 +020041#include "fw_env_private.h"
Markus Klotzbücher6de66b32007-11-27 10:23:20 +010042#include "fw_env.h"
wdenk6aff3112002-12-17 01:51:00 +000043
Andreas Fenkart14070e62016-05-31 09:21:56 +020044struct env_opts default_opts = {
45#ifdef CONFIG_FILE
46 .config_file = CONFIG_FILE
47#endif
48};
49
Marek Vasuta8a752c2014-03-05 19:59:52 +010050#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
51
Guennadi Liakhovetski56086922008-09-04 13:01:49 +020052#define min(x, y) ({ \
53 typeof(x) _min1 = (x); \
54 typeof(y) _min2 = (y); \
55 (void) (&_min1 == &_min2); \
56 _min1 < _min2 ? _min1 : _min2; })
57
58struct envdev_s {
Tom Rini229695f2014-03-28 12:03:33 -040059 const char *devname; /* Device name */
Stefan Agnerf4742ca2016-07-13 17:14:38 -070060 long long devoff; /* Device offset */
wdenk4a6fd342003-04-12 23:38:12 +000061 ulong env_size; /* environment size */
62 ulong erase_size; /* device erase size */
Guennadi Liakhovetski56086922008-09-04 13:01:49 +020063 ulong env_sectors; /* number of environment sectors */
64 uint8_t mtd_type; /* type of the MTD device */
S. Lockwood-Childs34255b92017-11-14 23:01:26 -080065 int is_ubi; /* set if we use UBI volume */
Guennadi Liakhovetski56086922008-09-04 13:01:49 +020066};
wdenk6aff3112002-12-17 01:51:00 +000067
Alex Kiernanc7f52c42018-03-09 12:12:59 +000068static struct envdev_s envdevices[2] = {
Guennadi Liakhovetski56086922008-09-04 13:01:49 +020069 {
70 .mtd_type = MTD_ABSENT,
71 }, {
72 .mtd_type = MTD_ABSENT,
73 },
74};
Alex Kiernanc7f52c42018-03-09 12:12:59 +000075
Guennadi Liakhovetski56086922008-09-04 13:01:49 +020076static int dev_current;
wdenk6aff3112002-12-17 01:51:00 +000077
78#define DEVNAME(i) envdevices[(i)].devname
wdenkd0fb80c2003-01-11 09:48:40 +000079#define DEVOFFSET(i) envdevices[(i)].devoff
wdenk6aff3112002-12-17 01:51:00 +000080#define ENVSIZE(i) envdevices[(i)].env_size
81#define DEVESIZE(i) envdevices[(i)].erase_size
Guennadi Liakhovetski56086922008-09-04 13:01:49 +020082#define ENVSECTORS(i) envdevices[(i)].env_sectors
83#define DEVTYPE(i) envdevices[(i)].mtd_type
S. Lockwood-Childs34255b92017-11-14 23:01:26 -080084#define IS_UBI(i) envdevices[(i)].is_ubi
wdenk6aff3112002-12-17 01:51:00 +000085
Joe Hershberger497f2052012-10-03 09:38:46 +000086#define CUR_ENVSIZE ENVSIZE(dev_current)
wdenk6aff3112002-12-17 01:51:00 +000087
Andreas Fenkartf71cee42016-04-19 22:43:42 +020088static unsigned long usable_envsize;
89#define ENV_SIZE usable_envsize
wdenk6aff3112002-12-17 01:51:00 +000090
Guennadi Liakhovetski56086922008-09-04 13:01:49 +020091struct env_image_single {
Alex Kiernanc7f52c42018-03-09 12:12:59 +000092 uint32_t crc; /* CRC32 over data bytes */
93 char data[];
Guennadi Liakhovetski56086922008-09-04 13:01:49 +020094};
wdenk6aff3112002-12-17 01:51:00 +000095
Guennadi Liakhovetski56086922008-09-04 13:01:49 +020096struct env_image_redundant {
Alex Kiernanc7f52c42018-03-09 12:12:59 +000097 uint32_t crc; /* CRC32 over data bytes */
98 unsigned char flags; /* active or obsolete */
99 char data[];
Guennadi Liakhovetski56086922008-09-04 13:01:49 +0200100};
101
102enum flag_scheme {
103 FLAG_NONE,
104 FLAG_BOOLEAN,
105 FLAG_INCREMENTAL,
106};
107
108struct environment {
Alex Kiernanc7f52c42018-03-09 12:12:59 +0000109 void *image;
110 uint32_t *crc;
111 unsigned char *flags;
112 char *data;
113 enum flag_scheme flag_scheme;
Guennadi Liakhovetski56086922008-09-04 13:01:49 +0200114};
115
116static struct environment environment = {
117 .flag_scheme = FLAG_NONE,
118};
wdenk6aff3112002-12-17 01:51:00 +0000119
Alex Kiernan2deb3ca2018-03-09 12:13:00 +0000120static int have_redund_env;
wdenkd0fb80c2003-01-11 09:48:40 +0000121
Joe Hershbergerddd84182012-10-12 08:48:51 +0000122#define DEFAULT_ENV_INSTANCE_STATIC
123#include <env_default.h>
wdenk6aff3112002-12-17 01:51:00 +0000124
S. Lockwood-Childs34255b92017-11-14 23:01:26 -0800125#define UBI_DEV_START "/dev/ubi"
126#define UBI_SYSFS "/sys/class/ubi"
127#define UBI_VOL_NAME_PATT "ubi%d_%d"
128
129static int is_ubi_devname(const char *devname)
130{
131 return !strncmp(devname, UBI_DEV_START, sizeof(UBI_DEV_START) - 1);
132}
133
134static int ubi_check_volume_sysfs_name(const char *volume_sysfs_name,
135 const char *volname)
136{
137 char path[256];
138 FILE *file;
139 char *name;
140 int ret;
141
142 strcpy(path, UBI_SYSFS "/");
143 strcat(path, volume_sysfs_name);
144 strcat(path, "/name");
145
146 file = fopen(path, "r");
147 if (!file)
148 return -1;
149
150 ret = fscanf(file, "%ms", &name);
151 fclose(file);
152 if (ret <= 0 || !name) {
153 fprintf(stderr,
154 "Failed to read from file %s, ret = %d, name = %s\n",
155 path, ret, name);
156 return -1;
157 }
158
159 if (!strcmp(name, volname)) {
160 free(name);
161 return 0;
162 }
163 free(name);
164
165 return -1;
166}
167
168static int ubi_get_volnum_by_name(int devnum, const char *volname)
169{
170 DIR *sysfs_ubi;
171 struct dirent *dirent;
172 int ret;
173 int tmp_devnum;
174 int volnum;
175
176 sysfs_ubi = opendir(UBI_SYSFS);
177 if (!sysfs_ubi)
178 return -1;
179
180#ifdef DEBUG
181 fprintf(stderr, "Looking for volume name \"%s\"\n", volname);
182#endif
183
184 while (1) {
185 dirent = readdir(sysfs_ubi);
186 if (!dirent)
187 return -1;
188
189 ret = sscanf(dirent->d_name, UBI_VOL_NAME_PATT,
190 &tmp_devnum, &volnum);
191 if (ret == 2 && devnum == tmp_devnum) {
192 if (ubi_check_volume_sysfs_name(dirent->d_name,
193 volname) == 0)
194 return volnum;
195 }
196 }
197
198 return -1;
199}
200
201static int ubi_get_devnum_by_devname(const char *devname)
202{
203 int devnum;
204 int ret;
205
206 ret = sscanf(devname + sizeof(UBI_DEV_START) - 1, "%d", &devnum);
207 if (ret != 1)
208 return -1;
209
210 return devnum;
211}
212
213static const char *ubi_get_volume_devname(const char *devname,
214 const char *volname)
215{
216 char *volume_devname;
217 int volnum;
218 int devnum;
219 int ret;
220
221 devnum = ubi_get_devnum_by_devname(devname);
222 if (devnum < 0)
223 return NULL;
224
225 volnum = ubi_get_volnum_by_name(devnum, volname);
226 if (volnum < 0)
227 return NULL;
228
229 ret = asprintf(&volume_devname, "%s_%d", devname, volnum);
230 if (ret < 0)
231 return NULL;
232
233#ifdef DEBUG
234 fprintf(stderr, "Found ubi volume \"%s:%s\" -> %s\n",
235 devname, volname, volume_devname);
236#endif
237
238 return volume_devname;
239}
240
241static void ubi_check_dev(unsigned int dev_id)
242{
243 char *devname = (char *)DEVNAME(dev_id);
244 char *pname;
245 const char *volname = NULL;
246 const char *volume_devname;
247
248 if (!is_ubi_devname(DEVNAME(dev_id)))
249 return;
250
251 IS_UBI(dev_id) = 1;
252
253 for (pname = devname; *pname != '\0'; pname++) {
254 if (*pname == ':') {
255 *pname = '\0';
256 volname = pname + 1;
257 break;
258 }
259 }
260
261 if (volname) {
262 /* Let's find real volume device name */
263 volume_devname = ubi_get_volume_devname(devname, volname);
264 if (!volume_devname) {
265 fprintf(stderr, "Didn't found ubi volume \"%s\"\n",
266 volname);
267 return;
268 }
269
270 free(devname);
271 DEVNAME(dev_id) = volume_devname;
272 }
273}
274
275static int ubi_update_start(int fd, int64_t bytes)
276{
277 if (ioctl(fd, UBI_IOCVOLUP, &bytes))
278 return -1;
279 return 0;
280}
281
282static int ubi_read(int fd, void *buf, size_t count)
283{
284 ssize_t ret;
285
286 while (count > 0) {
287 ret = read(fd, buf, count);
288 if (ret > 0) {
289 count -= ret;
290 buf += ret;
291
292 continue;
293 }
294
295 if (ret == 0) {
296 /*
297 * Happens in case of too short volume data size. If we
298 * return error status we will fail it will be treated
299 * as UBI device error.
300 *
301 * Leave catching this error to CRC check.
302 */
303 fprintf(stderr, "Warning: end of data on ubi volume\n");
304 return 0;
305 } else if (errno == EBADF) {
306 /*
307 * Happens in case of corrupted volume. The same as
308 * above, we cannot return error now, as we will still
309 * be able to successfully write environment later.
310 */
311 fprintf(stderr, "Warning: corrupted volume?\n");
312 return 0;
313 } else if (errno == EINTR) {
314 continue;
315 }
316
317 fprintf(stderr, "Cannot read %u bytes from ubi volume, %s\n",
318 (unsigned int)count, strerror(errno));
319 return -1;
320 }
321
322 return 0;
323}
324
325static int ubi_write(int fd, const void *buf, size_t count)
326{
327 ssize_t ret;
328
329 while (count > 0) {
330 ret = write(fd, buf, count);
331 if (ret <= 0) {
332 if (ret < 0 && errno == EINTR)
333 continue;
334
335 fprintf(stderr, "Cannot write %u bytes to ubi volume\n",
336 (unsigned int)count);
337 return -1;
338 }
339
340 count -= ret;
341 buf += ret;
342 }
343
344 return 0;
345}
346
Alex Kiernanc7f52c42018-03-09 12:12:59 +0000347static int flash_io(int mode);
Andreas Fenkart81974f42016-04-05 23:13:42 +0200348static int parse_config(struct env_opts *opts);
wdenk4a6fd342003-04-12 23:38:12 +0000349
wdenkd0fb80c2003-01-11 09:48:40 +0000350#if defined(CONFIG_FILE)
Alex Kiernanc7f52c42018-03-09 12:12:59 +0000351static int get_config(char *);
wdenkd0fb80c2003-01-11 09:48:40 +0000352#endif
wdenk6aff3112002-12-17 01:51:00 +0000353
Andreas Fenkart938c29f2016-03-11 09:39:37 +0100354static char *skip_chars(char *s)
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200355{
Andreas Fenkart938c29f2016-03-11 09:39:37 +0100356 for (; *s != '\0'; s++) {
Stefan Agnercd655512018-03-01 14:06:32 +0100357 if (isblank(*s) || *s == '=')
Andreas Fenkart938c29f2016-03-11 09:39:37 +0100358 return s;
359 }
360 return NULL;
361}
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200362
Andreas Fenkart938c29f2016-03-11 09:39:37 +0100363static char *skip_blanks(char *s)
364{
365 for (; *s != '\0'; s++) {
366 if (!isblank(*s))
Andreas Fenkart9583efc2016-03-11 09:39:36 +0100367 return s;
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200368 }
Andreas Fenkart9583efc2016-03-11 09:39:36 +0100369 return NULL;
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200370}
371
wdenk6aff3112002-12-17 01:51:00 +0000372/*
Andreas Fenkart1b7427c2016-07-16 17:06:14 +0200373 * s1 is either a simple 'name', or a 'name=value' pair.
374 * s2 is a 'name=value' pair.
375 * If the names match, return the value of s2, else NULL.
376 */
377static char *envmatch(char *s1, char *s2)
378{
379 if (s1 == NULL || s2 == NULL)
380 return NULL;
381
382 while (*s1 == *s2++)
383 if (*s1++ == '=')
384 return s2;
385 if (*s1 == '\0' && *(s2 - 1) == '=')
386 return s2;
387 return NULL;
388}
389
390/**
wdenk6aff3112002-12-17 01:51:00 +0000391 * Search the environment for a variable.
392 * Return the value, if found, or NULL, if not found.
393 */
Alex Kiernanc7f52c42018-03-09 12:12:59 +0000394char *fw_getenv(char *name)
wdenk6aff3112002-12-17 01:51:00 +0000395{
Markus Klotzbücher6de66b32007-11-27 10:23:20 +0100396 char *env, *nxt;
wdenk6aff3112002-12-17 01:51:00 +0000397
wdenk4a6fd342003-04-12 23:38:12 +0000398 for (env = environment.data; *env; env = nxt + 1) {
Markus Klotzbücher6de66b32007-11-27 10:23:20 +0100399 char *val;
wdenk6aff3112002-12-17 01:51:00 +0000400
wdenk4a6fd342003-04-12 23:38:12 +0000401 for (nxt = env; *nxt; ++nxt) {
wdenk6aff3112002-12-17 01:51:00 +0000402 if (nxt >= &environment.data[ENV_SIZE]) {
Alex Kiernanc7f52c42018-03-09 12:12:59 +0000403 fprintf(stderr, "## Error: "
wdenk6aff3112002-12-17 01:51:00 +0000404 "environment not terminated\n");
Guennadi Liakhovetski56086922008-09-04 13:01:49 +0200405 return NULL;
wdenk6aff3112002-12-17 01:51:00 +0000406 }
407 }
Alex Kiernanc7f52c42018-03-09 12:12:59 +0000408 val = envmatch(name, env);
wdenk6aff3112002-12-17 01:51:00 +0000409 if (!val)
410 continue;
Guennadi Liakhovetski56086922008-09-04 13:01:49 +0200411 return val;
wdenk6aff3112002-12-17 01:51:00 +0000412 }
Guennadi Liakhovetski56086922008-09-04 13:01:49 +0200413 return NULL;
wdenk6aff3112002-12-17 01:51:00 +0000414}
415
416/*
Joe Hershberger267541f2012-12-11 22:16:34 -0600417 * Search the default environment for a variable.
418 * Return the value, if found, or NULL, if not found.
419 */
420char *fw_getdefenv(char *name)
421{
422 char *env, *nxt;
423
424 for (env = default_environment; *env; env = nxt + 1) {
425 char *val;
426
427 for (nxt = env; *nxt; ++nxt) {
428 if (nxt >= &default_environment[ENV_SIZE]) {
429 fprintf(stderr, "## Error: "
430 "default environment not terminated\n");
431 return NULL;
432 }
433 }
434 val = envmatch(name, env);
435 if (!val)
436 continue;
437 return val;
438 }
439 return NULL;
440}
441
442/*
wdenk6aff3112002-12-17 01:51:00 +0000443 * Print the current definition of one, or more, or all
444 * environment variables
445 */
Andreas Fenkart81974f42016-04-05 23:13:42 +0200446int fw_printenv(int argc, char *argv[], int value_only, struct env_opts *opts)
wdenk6aff3112002-12-17 01:51:00 +0000447{
Andreas Fenkart371ee132015-12-09 13:13:24 +0100448 int i, rc = 0;
wdenk6aff3112002-12-17 01:51:00 +0000449
Andreas Fenkartc5c41c42016-07-16 17:06:15 +0200450 if (value_only && argc != 1) {
451 fprintf(stderr,
Alex Kiernand877a6c2018-02-11 17:16:46 +0000452 "## Error: `-n'/`--noheader' option requires exactly one argument\n");
Andreas Fenkartc5c41c42016-07-16 17:06:15 +0200453 return -1;
454 }
455
Andreas Fenkart14070e62016-05-31 09:21:56 +0200456 if (!opts)
457 opts = &default_opts;
458
Andreas Fenkart81974f42016-04-05 23:13:42 +0200459 if (fw_env_open(opts))
Guennadi Liakhovetski56086922008-09-04 13:01:49 +0200460 return -1;
wdenk6aff3112002-12-17 01:51:00 +0000461
Alex Kiernanc7f52c42018-03-09 12:12:59 +0000462 if (argc == 0) { /* Print all env variables */
Andreas Fenkartc5c41c42016-07-16 17:06:15 +0200463 char *env, *nxt;
wdenk4a6fd342003-04-12 23:38:12 +0000464 for (env = environment.data; *env; env = nxt + 1) {
465 for (nxt = env; *nxt; ++nxt) {
wdenk6aff3112002-12-17 01:51:00 +0000466 if (nxt >= &environment.data[ENV_SIZE]) {
Alex Kiernanc7f52c42018-03-09 12:12:59 +0000467 fprintf(stderr, "## Error: "
wdenk6aff3112002-12-17 01:51:00 +0000468 "environment not terminated\n");
Guennadi Liakhovetski56086922008-09-04 13:01:49 +0200469 return -1;
wdenk6aff3112002-12-17 01:51:00 +0000470 }
471 }
472
Alex Kiernanc7f52c42018-03-09 12:12:59 +0000473 printf("%s\n", env);
wdenk6aff3112002-12-17 01:51:00 +0000474 }
Stefano Babic33f00862017-04-05 18:08:03 +0200475 fw_env_close(opts);
Guennadi Liakhovetski56086922008-09-04 13:01:49 +0200476 return 0;
wdenk6aff3112002-12-17 01:51:00 +0000477 }
478
Andreas Fenkartc5c41c42016-07-16 17:06:15 +0200479 for (i = 0; i < argc; ++i) { /* print a subset of env variables */
Markus Klotzbücher6de66b32007-11-27 10:23:20 +0100480 char *name = argv[i];
481 char *val = NULL;
wdenk6aff3112002-12-17 01:51:00 +0000482
Andreas Fenkartc5c41c42016-07-16 17:06:15 +0200483 val = fw_getenv(name);
Grant Ericksonbc117562008-05-06 20:16:15 -0700484 if (!val) {
Alex Kiernanc7f52c42018-03-09 12:12:59 +0000485 fprintf(stderr, "## Error: \"%s\" not defined\n", name);
Grant Ericksonbc117562008-05-06 20:16:15 -0700486 rc = -1;
Andreas Fenkartc5c41c42016-07-16 17:06:15 +0200487 continue;
Grant Ericksonbc117562008-05-06 20:16:15 -0700488 }
Andreas Fenkartc5c41c42016-07-16 17:06:15 +0200489
490 if (value_only) {
491 puts(val);
492 break;
493 }
494
495 printf("%s=%s\n", name, val);
wdenk6aff3112002-12-17 01:51:00 +0000496 }
Grant Ericksonbc117562008-05-06 20:16:15 -0700497
Stefano Babic33f00862017-04-05 18:08:03 +0200498 fw_env_close(opts);
499
Guennadi Liakhovetski56086922008-09-04 13:01:49 +0200500 return rc;
wdenk6aff3112002-12-17 01:51:00 +0000501}
502
Stefano Babic33f00862017-04-05 18:08:03 +0200503int fw_env_flush(struct env_opts *opts)
wdenk6aff3112002-12-17 01:51:00 +0000504{
Andreas Fenkart14070e62016-05-31 09:21:56 +0200505 if (!opts)
506 opts = &default_opts;
507
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200508 /*
509 * Update CRC
510 */
511 *environment.crc = crc32(0, (uint8_t *) environment.data, ENV_SIZE);
wdenk6aff3112002-12-17 01:51:00 +0000512
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200513 /* write environment back to flash */
514 if (flash_io(O_RDWR)) {
Alex Kiernanc7f52c42018-03-09 12:12:59 +0000515 fprintf(stderr, "Error: can't write fw_env to flash\n");
516 return -1;
wdenk6aff3112002-12-17 01:51:00 +0000517 }
518
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200519 return 0;
520}
wdenk6aff3112002-12-17 01:51:00 +0000521
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200522/*
523 * Set/Clear a single variable in the environment.
524 * This is called in sequence to update the environment
525 * in RAM without updating the copy in flash after each set
526 */
527int fw_env_write(char *name, char *value)
528{
529 int len;
530 char *env, *nxt;
531 char *oldval = NULL;
Joe Hershberger267541f2012-12-11 22:16:34 -0600532 int deleting, creating, overwriting;
wdenk6aff3112002-12-17 01:51:00 +0000533
534 /*
535 * search if variable with this name already exists
536 */
wdenk4a6fd342003-04-12 23:38:12 +0000537 for (nxt = env = environment.data; *env; env = nxt + 1) {
538 for (nxt = env; *nxt; ++nxt) {
wdenk6aff3112002-12-17 01:51:00 +0000539 if (nxt >= &environment.data[ENV_SIZE]) {
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200540 fprintf(stderr, "## Error: "
wdenk6aff3112002-12-17 01:51:00 +0000541 "environment not terminated\n");
Guennadi Liakhovetski56086922008-09-04 13:01:49 +0200542 errno = EINVAL;
543 return -1;
wdenk6aff3112002-12-17 01:51:00 +0000544 }
545 }
Alex Kiernanc7f52c42018-03-09 12:12:59 +0000546 oldval = envmatch(name, env);
547 if (oldval)
wdenk6aff3112002-12-17 01:51:00 +0000548 break;
549 }
550
Joe Hershberger267541f2012-12-11 22:16:34 -0600551 deleting = (oldval && !(value && strlen(value)));
552 creating = (!oldval && (value && strlen(value)));
553 overwriting = (oldval && (value && strlen(value)));
554
555 /* check for permission */
556 if (deleting) {
557 if (env_flags_validate_varaccess(name,
558 ENV_FLAGS_VARACCESS_PREVENT_DELETE)) {
559 printf("Can't delete \"%s\"\n", name);
560 errno = EROFS;
561 return -1;
562 }
563 } else if (overwriting) {
564 if (env_flags_validate_varaccess(name,
565 ENV_FLAGS_VARACCESS_PREVENT_OVERWR)) {
566 printf("Can't overwrite \"%s\"\n", name);
567 errno = EROFS;
568 return -1;
569 } else if (env_flags_validate_varaccess(name,
Alex Kiernanc7f52c42018-03-09 12:12:59 +0000570 ENV_FLAGS_VARACCESS_PREVENT_NONDEF_OVERWR)) {
Joe Hershberger267541f2012-12-11 22:16:34 -0600571 const char *defval = fw_getdefenv(name);
572
573 if (defval == NULL)
574 defval = "";
575 if (strcmp(oldval, defval)
576 != 0) {
577 printf("Can't overwrite \"%s\"\n", name);
578 errno = EROFS;
579 return -1;
580 }
581 }
582 } else if (creating) {
583 if (env_flags_validate_varaccess(name,
584 ENV_FLAGS_VARACCESS_PREVENT_CREATE)) {
585 printf("Can't create \"%s\"\n", name);
586 errno = EROFS;
587 return -1;
588 }
589 } else
590 /* Nothing to do */
591 return 0;
592
593 if (deleting || overwriting) {
wdenk6aff3112002-12-17 01:51:00 +0000594 if (*++nxt == '\0') {
595 *env = '\0';
596 } else {
597 for (;;) {
598 *env = *nxt++;
599 if ((*env == '\0') && (*nxt == '\0'))
600 break;
601 ++env;
602 }
603 }
604 *++env = '\0';
605 }
606
607 /* Delete only ? */
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200608 if (!value || !strlen(value))
609 return 0;
wdenk6aff3112002-12-17 01:51:00 +0000610
611 /*
612 * Append new definition at the end
613 */
Alex Kiernanc7f52c42018-03-09 12:12:59 +0000614 for (env = environment.data; *env || *(env + 1); ++env)
615 ;
wdenk6aff3112002-12-17 01:51:00 +0000616 if (env > environment.data)
617 ++env;
618 /*
619 * Overflow when:
Joe Hershberger497f2052012-10-03 09:38:46 +0000620 * "name" + "=" + "val" +"\0\0" > CUR_ENVSIZE - (env-environment)
wdenk6aff3112002-12-17 01:51:00 +0000621 */
Alex Kiernanc7f52c42018-03-09 12:12:59 +0000622 len = strlen(name) + 2;
wdenk6aff3112002-12-17 01:51:00 +0000623 /* add '=' for first arg, ' ' for all others */
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200624 len += strlen(value) + 1;
625
wdenk4a6fd342003-04-12 23:38:12 +0000626 if (len > (&environment.data[ENV_SIZE] - env)) {
Alex Kiernanc7f52c42018-03-09 12:12:59 +0000627 fprintf(stderr,
628 "Error: environment overflow, \"%s\" deleted\n", name);
Guennadi Liakhovetski56086922008-09-04 13:01:49 +0200629 return -1;
wdenk6aff3112002-12-17 01:51:00 +0000630 }
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200631
wdenk6aff3112002-12-17 01:51:00 +0000632 while ((*env = *name++) != '\0')
633 env++;
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200634 *env = '=';
635 while ((*++env = *value++) != '\0')
636 ;
wdenk6aff3112002-12-17 01:51:00 +0000637
638 /* end is marked with double '\0' */
639 *++env = '\0';
640
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200641 return 0;
642}
wdenk6aff3112002-12-17 01:51:00 +0000643
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200644/*
645 * Deletes or sets environment variables. Returns -1 and sets errno error codes:
646 * 0 - OK
647 * EINVAL - need at least 1 argument
648 * EROFS - certain variables ("ethaddr", "serial#") cannot be
649 * modified or deleted
650 *
651 */
Simon Glass382bee52017-08-03 12:22:09 -0600652int fw_env_set(int argc, char *argv[], struct env_opts *opts)
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200653{
Andreas Fenkart371ee132015-12-09 13:13:24 +0100654 int i;
Mike Frysinger3779c8e2012-11-10 19:47:45 +0000655 size_t len;
Andreas Fenkart167f5252015-12-09 13:13:21 +0100656 char *name, **valv;
xypron.glpk@gmx.deddc6a9d2017-04-15 13:05:40 +0200657 char *oldval;
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200658 char *value = NULL;
Andreas Fenkart167f5252015-12-09 13:13:21 +0100659 int valc;
Stefano Babic33f00862017-04-05 18:08:03 +0200660 int ret;
wdenk6aff3112002-12-17 01:51:00 +0000661
Andreas Fenkart14070e62016-05-31 09:21:56 +0200662 if (!opts)
663 opts = &default_opts;
664
Andreas Fenkart1ce68692015-12-09 13:13:25 +0100665 if (argc < 1) {
666 fprintf(stderr, "## Error: variable name missing\n");
Marek Vasuta8a752c2014-03-05 19:59:52 +0100667 errno = EINVAL;
668 return -1;
669 }
670
Andreas Fenkart81974f42016-04-05 23:13:42 +0200671 if (fw_env_open(opts)) {
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200672 fprintf(stderr, "Error: environment not initialized\n");
673 return -1;
674 }
675
Andreas Fenkart1ce68692015-12-09 13:13:25 +0100676 name = argv[0];
677 valv = argv + 1;
678 valc = argc - 1;
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200679
Stefano Babic33f00862017-04-05 18:08:03 +0200680 if (env_flags_validate_env_set_params(name, valv, valc) < 0) {
681 fw_env_close(opts);
Andreas Fenkartfd4e3282016-07-16 17:06:13 +0200682 return -1;
Stefano Babic33f00862017-04-05 18:08:03 +0200683 }
Joe Hershberger30fd4fad2012-12-11 22:16:32 -0600684
Joe Hershberger62a34a02012-10-03 09:38:47 +0000685 len = 0;
Andreas Fenkart167f5252015-12-09 13:13:21 +0100686 for (i = 0; i < valc; ++i) {
687 char *val = valv[i];
Joe Hershberger62a34a02012-10-03 09:38:47 +0000688 size_t val_len = strlen(val);
689
Joe Hershbergerce2f5802012-10-15 15:29:24 +0000690 if (value)
691 value[len - 1] = ' ';
xypron.glpk@gmx.deddc6a9d2017-04-15 13:05:40 +0200692 oldval = value;
Joe Hershberger62a34a02012-10-03 09:38:47 +0000693 value = realloc(value, len + val_len + 1);
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200694 if (!value) {
Joe Hershberger62a34a02012-10-03 09:38:47 +0000695 fprintf(stderr,
Luka Perkov8603b692011-09-05 23:40:13 +0200696 "Cannot malloc %zu bytes: %s\n",
Joe Hershberger62a34a02012-10-03 09:38:47 +0000697 len, strerror(errno));
xypron.glpk@gmx.deddc6a9d2017-04-15 13:05:40 +0200698 free(oldval);
Joe Hershberger62a34a02012-10-03 09:38:47 +0000699 return -1;
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200700 }
Joe Hershberger62a34a02012-10-03 09:38:47 +0000701
702 memcpy(value + len, val, val_len);
703 len += val_len;
Joe Hershbergerce2f5802012-10-15 15:29:24 +0000704 value[len++] = '\0';
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200705 }
706
707 fw_env_write(name, value);
708
Joe Hershberger62a34a02012-10-03 09:38:47 +0000709 free(value);
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200710
Stefano Babic33f00862017-04-05 18:08:03 +0200711 ret = fw_env_flush(opts);
712 fw_env_close(opts);
713
714 return ret;
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200715}
716
717/*
718 * Parse a file and configure the u-boot variables.
719 * The script file has a very simple format, as follows:
720 *
721 * Each line has a couple with name, value:
722 * <white spaces>variable_name<white spaces>variable_value
723 *
724 * Both variable_name and variable_value are interpreted as strings.
725 * Any character after <white spaces> and before ending \r\n is interpreted
726 * as variable's value (no comment allowed on these lines !)
727 *
728 * Comments are allowed if the first character in the line is #
729 *
730 * Returns -1 and sets errno error codes:
731 * 0 - OK
732 * -1 - Error
733 */
Andreas Fenkart81974f42016-04-05 23:13:42 +0200734int fw_parse_script(char *fname, struct env_opts *opts)
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200735{
736 FILE *fp;
Alex Kiernan94b233f2018-06-07 12:20:05 +0000737 char *line = NULL;
738 size_t linesize = 0;
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200739 char *name;
740 char *val;
741 int lineno = 0;
742 int len;
743 int ret = 0;
744
Andreas Fenkart14070e62016-05-31 09:21:56 +0200745 if (!opts)
746 opts = &default_opts;
747
Andreas Fenkart81974f42016-04-05 23:13:42 +0200748 if (fw_env_open(opts)) {
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200749 fprintf(stderr, "Error: environment not initialized\n");
750 return -1;
751 }
752
753 if (strcmp(fname, "-") == 0)
754 fp = stdin;
755 else {
756 fp = fopen(fname, "r");
757 if (fp == NULL) {
758 fprintf(stderr, "I cannot open %s for reading\n",
Alex Kiernanc7f52c42018-03-09 12:12:59 +0000759 fname);
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200760 return -1;
761 }
762 }
763
Alex Kiernan94b233f2018-06-07 12:20:05 +0000764 while ((len = getline(&line, &linesize, fp)) != -1) {
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200765 lineno++;
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200766
767 /*
Alex Kiernan94b233f2018-06-07 12:20:05 +0000768 * Read a whole line from the file. If the line is not
769 * terminated, reports an error and exit.
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200770 */
Alex Kiernan94b233f2018-06-07 12:20:05 +0000771 if (line[len - 1] != '\n') {
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200772 fprintf(stderr,
Alex Kiernan94b233f2018-06-07 12:20:05 +0000773 "Line %d not correctly terminated\n",
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200774 lineno);
775 ret = -1;
776 break;
777 }
778
779 /* Drop ending line feed / carriage return */
Alex Kiernan94b233f2018-06-07 12:20:05 +0000780 line[--len] = '\0';
781 if (len && line[len - 1] == '\r')
782 line[--len] = '\0';
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200783
784 /* Skip comment or empty lines */
Alex Kiernan94b233f2018-06-07 12:20:05 +0000785 if (len == 0 || line[0] == '#')
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200786 continue;
787
788 /*
Alex Kiernan94b233f2018-06-07 12:20:05 +0000789 * Search for variable's name remove leading whitespaces
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200790 */
Alex Kiernan94b233f2018-06-07 12:20:05 +0000791 name = skip_blanks(line);
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200792 if (!name)
793 continue;
794
795 /* The first white space is the end of variable name */
Andreas Fenkart938c29f2016-03-11 09:39:37 +0100796 val = skip_chars(name);
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200797 len = strlen(name);
798 if (val) {
799 *val++ = '\0';
800 if ((val - name) < len)
Andreas Fenkart938c29f2016-03-11 09:39:37 +0100801 val = skip_blanks(val);
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200802 else
803 val = NULL;
804 }
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200805#ifdef DEBUG
806 fprintf(stderr, "Setting %s : %s\n",
807 name, val ? val : " removed");
808#endif
809
Joe Hershberger30fd4fad2012-12-11 22:16:32 -0600810 if (env_flags_validate_type(name, val) < 0) {
811 ret = -1;
812 break;
813 }
814
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200815 /*
816 * If there is an error setting a variable,
817 * try to save the environment and returns an error
818 */
819 if (fw_env_write(name, val)) {
820 fprintf(stderr,
Alex Kiernanc7f52c42018-03-09 12:12:59 +0000821 "fw_env_write returns with error : %s\n",
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200822 strerror(errno));
823 ret = -1;
824 break;
825 }
826
827 }
Alex Kiernan94b233f2018-06-07 12:20:05 +0000828 free(line);
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200829
830 /* Close file if not stdin */
831 if (strcmp(fname, "-") != 0)
832 fclose(fp);
833
Stefano Babic33f00862017-04-05 18:08:03 +0200834 ret |= fw_env_flush(opts);
835
836 fw_env_close(opts);
Stefano Babicbd7b26f2010-05-24 12:08:16 +0200837
838 return ret;
Guennadi Liakhovetski56086922008-09-04 13:01:49 +0200839}
840
Andreas Fenkarte2c93512016-08-29 23:16:57 +0200841/**
Shyam Saini919d25c2018-06-07 19:47:19 +0530842 * environment_end() - compute offset of first byte right after environment
Andreas Fenkarte2c93512016-08-29 23:16:57 +0200843 * @dev - index of enviroment buffer
844 * Return:
Shyam Saini919d25c2018-06-07 19:47:19 +0530845 * device offset of first byte right after environment
Andreas Fenkarte2c93512016-08-29 23:16:57 +0200846 */
847off_t environment_end(int dev)
848{
849 /* environment is block aligned */
850 return DEVOFFSET(dev) + ENVSECTORS(dev) * DEVESIZE(dev);
851}
852
Guennadi Liakhovetski56086922008-09-04 13:01:49 +0200853/*
854 * Test for bad block on NAND, just returns 0 on NOR, on NAND:
855 * 0 - block is good
856 * > 0 - block is bad
857 * < 0 - failed to test
858 */
Andreas Fenkartc6012bb2016-08-29 23:16:58 +0200859static int flash_bad_block(int fd, uint8_t mtd_type, loff_t blockstart)
Guennadi Liakhovetski56086922008-09-04 13:01:49 +0200860{
861 if (mtd_type == MTD_NANDFLASH) {
Andreas Fenkartc6012bb2016-08-29 23:16:58 +0200862 int badblock = ioctl(fd, MEMGETBADBLOCK, &blockstart);
Guennadi Liakhovetski56086922008-09-04 13:01:49 +0200863
864 if (badblock < 0) {
Alex Kiernanc7f52c42018-03-09 12:12:59 +0000865 perror("Cannot read bad block mark");
Guennadi Liakhovetski56086922008-09-04 13:01:49 +0200866 return badblock;
867 }
868
869 if (badblock) {
870#ifdef DEBUG
Alex Kiernanc7f52c42018-03-09 12:12:59 +0000871 fprintf(stderr, "Bad block at 0x%llx, skipping\n",
Andreas Fenkartc6012bb2016-08-29 23:16:58 +0200872 (unsigned long long)blockstart);
Guennadi Liakhovetski56086922008-09-04 13:01:49 +0200873#endif
874 return badblock;
875 }
876 }
877
878 return 0;
879}
880
881/*
882 * Read data from flash at an offset into a provided buffer. On NAND it skips
883 * bad blocks but makes sure it stays within ENVSECTORS (dev) starting from
884 * the DEVOFFSET (dev) block. On NOR the loop is only run once.
885 */
Alex Kiernanc7f52c42018-03-09 12:12:59 +0000886static int flash_read_buf(int dev, int fd, void *buf, size_t count,
887 off_t offset)
Guennadi Liakhovetski56086922008-09-04 13:01:49 +0200888{
889 size_t blocklen; /* erase / write length - one block on NAND,
890 0 on NOR */
891 size_t processed = 0; /* progress counter */
892 size_t readlen = count; /* current read length */
Guennadi Liakhovetski56086922008-09-04 13:01:49 +0200893 off_t block_seek; /* offset inside the current block to the start
894 of the data */
895 loff_t blockstart; /* running start of the current block -
896 MEMGETBADBLOCK needs 64 bits */
897 int rc;
898
Alex Kiernanc7f52c42018-03-09 12:12:59 +0000899 blockstart = (offset / DEVESIZE(dev)) * DEVESIZE(dev);
Guennadi Liakhovetski56086922008-09-04 13:01:49 +0200900
901 /* Offset inside a block */
902 block_seek = offset - blockstart;
903
Andreas Fenkartff95e572016-08-29 23:16:59 +0200904 if (DEVTYPE(dev) == MTD_NANDFLASH) {
Guennadi Liakhovetski56086922008-09-04 13:01:49 +0200905 /*
906 * NAND: calculate which blocks we are reading. We have
907 * to read one block at a time to skip bad blocks.
908 */
Alex Kiernanc7f52c42018-03-09 12:12:59 +0000909 blocklen = DEVESIZE(dev);
Guennadi Liakhovetski56086922008-09-04 13:01:49 +0200910
Guennadi Liakhovetski56086922008-09-04 13:01:49 +0200911 /* Limit to one block for the first read */
912 if (readlen > blocklen - block_seek)
913 readlen = blocklen - block_seek;
914 } else {
915 blocklen = 0;
Guennadi Liakhovetski56086922008-09-04 13:01:49 +0200916 }
917
918 /* This only runs once on NOR flash */
919 while (processed < count) {
Andreas Fenkartff95e572016-08-29 23:16:59 +0200920 rc = flash_bad_block(fd, DEVTYPE(dev), blockstart);
Alex Kiernanc7f52c42018-03-09 12:12:59 +0000921 if (rc < 0) /* block test failed */
Guennadi Liakhovetski56086922008-09-04 13:01:49 +0200922 return -1;
923
Andreas Fenkarte2c93512016-08-29 23:16:57 +0200924 if (blockstart + block_seek + readlen > environment_end(dev)) {
Guennadi Liakhovetski56086922008-09-04 13:01:49 +0200925 /* End of range is reached */
Alex Kiernanc7f52c42018-03-09 12:12:59 +0000926 fprintf(stderr, "Too few good blocks within range\n");
Guennadi Liakhovetski56086922008-09-04 13:01:49 +0200927 return -1;
928 }
929
Alex Kiernanc7f52c42018-03-09 12:12:59 +0000930 if (rc) { /* block is bad */
Guennadi Liakhovetski56086922008-09-04 13:01:49 +0200931 blockstart += blocklen;
932 continue;
933 }
934
935 /*
936 * If a block is bad, we retry in the next block at the same
Tom Rini2d3229b2017-09-05 14:01:29 -0400937 * offset - see env/nand.c::writeenv()
Guennadi Liakhovetski56086922008-09-04 13:01:49 +0200938 */
Alex Kiernanc7f52c42018-03-09 12:12:59 +0000939 lseek(fd, blockstart + block_seek, SEEK_SET);
Guennadi Liakhovetski56086922008-09-04 13:01:49 +0200940
Alex Kiernanc7f52c42018-03-09 12:12:59 +0000941 rc = read(fd, buf + processed, readlen);
Guennadi Liakhovetski56086922008-09-04 13:01:49 +0200942 if (rc != readlen) {
Alex Kiernanc7f52c42018-03-09 12:12:59 +0000943 fprintf(stderr, "Read error on %s: %s\n",
944 DEVNAME(dev), strerror(errno));
Guennadi Liakhovetski56086922008-09-04 13:01:49 +0200945 return -1;
946 }
947#ifdef DEBUG
Joe Hershberger74620e12012-10-15 15:29:25 +0000948 fprintf(stderr, "Read 0x%x bytes at 0x%llx on %s\n",
Alex Kiernanc7f52c42018-03-09 12:12:59 +0000949 rc, (unsigned long long)blockstart + block_seek,
Marcin Niestroj81c878d2016-05-06 14:58:29 +0200950 DEVNAME(dev));
Guennadi Liakhovetski56086922008-09-04 13:01:49 +0200951#endif
952 processed += readlen;
Alex Kiernanc7f52c42018-03-09 12:12:59 +0000953 readlen = min(blocklen, count - processed);
Guennadi Liakhovetski56086922008-09-04 13:01:49 +0200954 block_seek = 0;
955 blockstart += blocklen;
956 }
957
958 return processed;
959}
960
961/*
Andreas Fenkart24307d62016-08-29 23:17:00 +0200962 * Write count bytes from begin of environment, but stay within
963 * ENVSECTORS(dev) sectors of
Remy Bohmer9eeaa8e2011-02-12 19:06:26 +0100964 * DEVOFFSET (dev). Similar to the read case above, on NOR and dataflash we
965 * erase and write the whole data at once.
Guennadi Liakhovetski56086922008-09-04 13:01:49 +0200966 */
Andreas Fenkart24307d62016-08-29 23:17:00 +0200967static int flash_write_buf(int dev, int fd, void *buf, size_t count)
Guennadi Liakhovetski56086922008-09-04 13:01:49 +0200968{
969 void *data;
970 struct erase_info_user erase;
971 size_t blocklen; /* length of NAND block / NOR erase sector */
972 size_t erase_len; /* whole area that can be erased - may include
973 bad blocks */
974 size_t erasesize; /* erase / write length - one block on NAND,
975 whole area on NOR */
976 size_t processed = 0; /* progress counter */
Remy Bohmer9eeaa8e2011-02-12 19:06:26 +0100977 size_t write_total; /* total size to actually write - excluding
Guennadi Liakhovetski56086922008-09-04 13:01:49 +0200978 bad blocks */
979 off_t erase_offset; /* offset to the first erase block (aligned)
980 below offset */
981 off_t block_seek; /* offset inside the erase block to the start
982 of the data */
Guennadi Liakhovetski56086922008-09-04 13:01:49 +0200983 loff_t blockstart; /* running start of the current block -
984 MEMGETBADBLOCK needs 64 bits */
985 int rc;
986
Guennadi Liakhovetski56086922008-09-04 13:01:49 +0200987 /*
Oliver Metze387efb2013-08-30 00:56:02 +0200988 * For mtd devices only offset and size of the environment do matter
Guennadi Liakhovetski56086922008-09-04 13:01:49 +0200989 */
Andreas Fenkartff95e572016-08-29 23:16:59 +0200990 if (DEVTYPE(dev) == MTD_ABSENT) {
Oliver Metze387efb2013-08-30 00:56:02 +0200991 blocklen = count;
Oliver Metze387efb2013-08-30 00:56:02 +0200992 erase_len = blocklen;
Andreas Fenkart24307d62016-08-29 23:17:00 +0200993 blockstart = DEVOFFSET(dev);
Oliver Metze387efb2013-08-30 00:56:02 +0200994 block_seek = 0;
995 write_total = blocklen;
996 } else {
997 blocklen = DEVESIZE(dev);
998
Andreas Fenkart24307d62016-08-29 23:17:00 +0200999 erase_offset = DEVOFFSET(dev);
Oliver Metze387efb2013-08-30 00:56:02 +02001000
1001 /* Maximum area we may use */
Andreas Fenkarte2c93512016-08-29 23:16:57 +02001002 erase_len = environment_end(dev) - erase_offset;
Oliver Metze387efb2013-08-30 00:56:02 +02001003
1004 blockstart = erase_offset;
Andreas Fenkart24307d62016-08-29 23:17:00 +02001005
Oliver Metze387efb2013-08-30 00:56:02 +02001006 /* Offset inside a block */
Andreas Fenkart24307d62016-08-29 23:17:00 +02001007 block_seek = DEVOFFSET(dev) - erase_offset;
Oliver Metze387efb2013-08-30 00:56:02 +02001008
1009 /*
1010 * Data size we actually write: from the start of the block
1011 * to the start of the data, then count bytes of data, and
1012 * to the end of the block
1013 */
1014 write_total = ((block_seek + count + blocklen - 1) /
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001015 blocklen) * blocklen;
Oliver Metze387efb2013-08-30 00:56:02 +02001016 }
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001017
1018 /*
1019 * Support data anywhere within erase sectors: read out the complete
1020 * area to be erased, replace the environment image, write the whole
1021 * block back again.
1022 */
1023 if (write_total > count) {
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001024 data = malloc(erase_len);
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001025 if (!data) {
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001026 fprintf(stderr,
1027 "Cannot malloc %zu bytes: %s\n",
1028 erase_len, strerror(errno));
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001029 return -1;
1030 }
1031
Andreas Fenkartff95e572016-08-29 23:16:59 +02001032 rc = flash_read_buf(dev, fd, data, write_total, erase_offset);
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001033 if (write_total != rc)
1034 return -1;
1035
Joe Hershberger74620e12012-10-15 15:29:25 +00001036#ifdef DEBUG
1037 fprintf(stderr, "Preserving data ");
1038 if (block_seek != 0)
1039 fprintf(stderr, "0x%x - 0x%lx", 0, block_seek - 1);
1040 if (block_seek + count != write_total) {
1041 if (block_seek != 0)
1042 fprintf(stderr, " and ");
Marcin Niestroj81c878d2016-05-06 14:58:29 +02001043 fprintf(stderr, "0x%lx - 0x%lx",
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001044 (unsigned long)block_seek + count,
1045 (unsigned long)write_total - 1);
Joe Hershberger74620e12012-10-15 15:29:25 +00001046 }
1047 fprintf(stderr, "\n");
1048#endif
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001049 /* Overwrite the old environment */
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001050 memcpy(data + block_seek, buf, count);
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001051 } else {
1052 /*
1053 * We get here, iff offset is block-aligned and count is a
1054 * multiple of blocklen - see write_total calculation above
1055 */
1056 data = buf;
1057 }
1058
Andreas Fenkartff95e572016-08-29 23:16:59 +02001059 if (DEVTYPE(dev) == MTD_NANDFLASH) {
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001060 /*
1061 * NAND: calculate which blocks we are writing. We have
1062 * to write one block at a time to skip bad blocks.
1063 */
1064 erasesize = blocklen;
1065 } else {
1066 erasesize = erase_len;
1067 }
1068
1069 erase.length = erasesize;
1070
Remy Bohmer9eeaa8e2011-02-12 19:06:26 +01001071 /* This only runs once on NOR flash and SPI-dataflash */
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001072 while (processed < write_total) {
Andreas Fenkartff95e572016-08-29 23:16:59 +02001073 rc = flash_bad_block(fd, DEVTYPE(dev), blockstart);
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001074 if (rc < 0) /* block test failed */
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001075 return rc;
1076
Andreas Fenkarte2c93512016-08-29 23:16:57 +02001077 if (blockstart + erasesize > environment_end(dev)) {
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001078 fprintf(stderr, "End of range reached, aborting\n");
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001079 return -1;
1080 }
1081
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001082 if (rc) { /* block is bad */
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001083 blockstart += blocklen;
1084 continue;
1085 }
1086
Andreas Fenkartff95e572016-08-29 23:16:59 +02001087 if (DEVTYPE(dev) != MTD_ABSENT) {
Oliver Metze387efb2013-08-30 00:56:02 +02001088 erase.start = blockstart;
1089 ioctl(fd, MEMUNLOCK, &erase);
1090 /* These do not need an explicit erase cycle */
Andreas Fenkartff95e572016-08-29 23:16:59 +02001091 if (DEVTYPE(dev) != MTD_DATAFLASH)
Oliver Metze387efb2013-08-30 00:56:02 +02001092 if (ioctl(fd, MEMERASE, &erase) != 0) {
1093 fprintf(stderr,
1094 "MTD erase error on %s: %s\n",
1095 DEVNAME(dev), strerror(errno));
1096 return -1;
1097 }
1098 }
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001099
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001100 if (lseek(fd, blockstart, SEEK_SET) == -1) {
1101 fprintf(stderr,
1102 "Seek error on %s: %s\n",
1103 DEVNAME(dev), strerror(errno));
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001104 return -1;
1105 }
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001106#ifdef DEBUG
Marcin Niestroj81c878d2016-05-06 14:58:29 +02001107 fprintf(stderr, "Write 0x%llx bytes at 0x%llx\n",
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001108 (unsigned long long)erasesize,
1109 (unsigned long long)blockstart);
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001110#endif
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001111 if (write(fd, data + processed, erasesize) != erasesize) {
1112 fprintf(stderr, "Write error on %s: %s\n",
1113 DEVNAME(dev), strerror(errno));
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001114 return -1;
1115 }
1116
Andreas Fenkartff95e572016-08-29 23:16:59 +02001117 if (DEVTYPE(dev) != MTD_ABSENT)
Oliver Metze387efb2013-08-30 00:56:02 +02001118 ioctl(fd, MEMLOCK, &erase);
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001119
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001120 processed += erasesize;
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001121 block_seek = 0;
Dustin Byford4b774ff2014-03-06 20:48:23 -08001122 blockstart += erasesize;
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001123 }
1124
1125 if (write_total > count)
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001126 free(data);
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001127
1128 return processed;
1129}
1130
1131/*
1132 * Set obsolete flag at offset - NOR flash only
1133 */
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001134static int flash_flag_obsolete(int dev, int fd, off_t offset)
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001135{
1136 int rc;
Detlev Zundel0aef7bc2010-07-30 11:22:15 +02001137 struct erase_info_user erase;
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001138
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001139 erase.start = DEVOFFSET(dev);
1140 erase.length = DEVESIZE(dev);
Simon Glassd3716dd2019-08-01 09:47:08 -06001141 /* This relies on the fact, that ENV_REDUND_OBSOLETE == 0 */
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001142 rc = lseek(fd, offset, SEEK_SET);
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001143 if (rc < 0) {
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001144 fprintf(stderr, "Cannot seek to set the flag on %s\n",
1145 DEVNAME(dev));
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001146 return rc;
1147 }
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001148 ioctl(fd, MEMUNLOCK, &erase);
Simon Glassd3716dd2019-08-01 09:47:08 -06001149 rc = write(fd, &ENV_REDUND_OBSOLETE, sizeof(ENV_REDUND_OBSOLETE));
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001150 ioctl(fd, MEMLOCK, &erase);
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001151 if (rc < 0)
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001152 perror("Could not set obsolete flag");
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001153
1154 return rc;
1155}
1156
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001157static int flash_write(int fd_current, int fd_target, int dev_target)
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001158{
1159 int rc;
1160
1161 switch (environment.flag_scheme) {
1162 case FLAG_NONE:
1163 break;
1164 case FLAG_INCREMENTAL:
1165 (*environment.flags)++;
1166 break;
1167 case FLAG_BOOLEAN:
Simon Glassd3716dd2019-08-01 09:47:08 -06001168 *environment.flags = ENV_REDUND_ACTIVE;
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001169 break;
1170 default:
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001171 fprintf(stderr, "Unimplemented flash scheme %u\n",
1172 environment.flag_scheme);
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001173 return -1;
1174 }
1175
1176#ifdef DEBUG
Stefan Agnerf4742ca2016-07-13 17:14:38 -07001177 fprintf(stderr, "Writing new environment at 0x%llx on %s\n",
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001178 DEVOFFSET(dev_target), DEVNAME(dev_target));
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001179#endif
Marek Vasuta8a752c2014-03-05 19:59:52 +01001180
S. Lockwood-Childs34255b92017-11-14 23:01:26 -08001181 if (IS_UBI(dev_target)) {
1182 if (ubi_update_start(fd_target, CUR_ENVSIZE) < 0)
1183 return 0;
1184 return ubi_write(fd_target, environment.image, CUR_ENVSIZE);
1185 }
1186
Joe Hershberger497f2052012-10-03 09:38:46 +00001187 rc = flash_write_buf(dev_target, fd_target, environment.image,
Andreas Fenkart24307d62016-08-29 23:17:00 +02001188 CUR_ENVSIZE);
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001189 if (rc < 0)
1190 return rc;
1191
1192 if (environment.flag_scheme == FLAG_BOOLEAN) {
1193 /* Have to set obsolete flag */
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001194 off_t offset = DEVOFFSET(dev_current) +
1195 offsetof(struct env_image_redundant, flags);
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001196#ifdef DEBUG
Joe Hershberger74620e12012-10-15 15:29:25 +00001197 fprintf(stderr,
Stefan Agnerf4742ca2016-07-13 17:14:38 -07001198 "Setting obsolete flag in environment at 0x%llx on %s\n",
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001199 DEVOFFSET(dev_current), DEVNAME(dev_current));
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001200#endif
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001201 flash_flag_obsolete(dev_current, fd_current, offset);
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001202 }
1203
1204 return 0;
1205}
1206
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001207static int flash_read(int fd)
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001208{
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001209 int rc;
1210
S. Lockwood-Childs34255b92017-11-14 23:01:26 -08001211 if (IS_UBI(dev_current)) {
1212 DEVTYPE(dev_current) = MTD_ABSENT;
1213
1214 return ubi_read(fd, environment.image, CUR_ENVSIZE);
1215 }
1216
Joe Hershberger497f2052012-10-03 09:38:46 +00001217 rc = flash_read_buf(dev_current, fd, environment.image, CUR_ENVSIZE,
Andreas Fenkartff95e572016-08-29 23:16:59 +02001218 DEVOFFSET(dev_current));
Marek Vasuta8a752c2014-03-05 19:59:52 +01001219 if (rc != CUR_ENVSIZE)
1220 return -1;
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001221
Marek Vasuta8a752c2014-03-05 19:59:52 +01001222 return 0;
wdenk6aff3112002-12-17 01:51:00 +00001223}
1224
Alex Kiernandbc34322018-03-09 12:13:02 +00001225static int flash_open_tempfile(const char **dname, const char **target_temp)
1226{
1227 char *dup_name = strdup(DEVNAME(dev_current));
1228 char *temp_name = NULL;
1229 int rc = -1;
1230
1231 if (!dup_name)
1232 return -1;
1233
1234 *dname = dirname(dup_name);
1235 if (!*dname)
1236 goto err;
1237
1238 rc = asprintf(&temp_name, "%s/XXXXXX", *dname);
1239 if (rc == -1)
1240 goto err;
1241
1242 rc = mkstemp(temp_name);
1243 if (rc == -1) {
1244 /* fall back to in place write */
1245 fprintf(stderr,
1246 "Can't create %s: %s\n", temp_name, strerror(errno));
1247 free(temp_name);
1248 } else {
1249 *target_temp = temp_name;
1250 /* deliberately leak dup_name as dname /might/ point into
1251 * it and we need it for our caller
1252 */
1253 dup_name = NULL;
1254 }
1255
1256err:
1257 if (dup_name)
1258 free(dup_name);
1259
1260 return rc;
1261}
1262
Alex Kiernan899b5332018-03-09 12:13:01 +00001263static int flash_io_write(int fd_current)
1264{
Alex Kiernandbc34322018-03-09 12:13:02 +00001265 int fd_target = -1, rc, dev_target;
1266 const char *dname, *target_temp = NULL;
Alex Kiernan899b5332018-03-09 12:13:01 +00001267
1268 if (have_redund_env) {
1269 /* switch to next partition for writing */
1270 dev_target = !dev_current;
1271 /* dev_target: fd_target, erase_target */
1272 fd_target = open(DEVNAME(dev_target), O_RDWR);
1273 if (fd_target < 0) {
1274 fprintf(stderr,
1275 "Can't open %s: %s\n",
1276 DEVNAME(dev_target), strerror(errno));
1277 rc = -1;
1278 goto exit;
1279 }
1280 } else {
Alex Kiernandbc34322018-03-09 12:13:02 +00001281 struct stat sb;
1282
1283 if (fstat(fd_current, &sb) == 0 && S_ISREG(sb.st_mode)) {
1284 /* if any part of flash_open_tempfile() fails we fall
1285 * back to in-place writes
1286 */
1287 fd_target = flash_open_tempfile(&dname, &target_temp);
1288 }
Alex Kiernan899b5332018-03-09 12:13:01 +00001289 dev_target = dev_current;
Alex Kiernandbc34322018-03-09 12:13:02 +00001290 if (fd_target == -1)
1291 fd_target = fd_current;
Alex Kiernan899b5332018-03-09 12:13:01 +00001292 }
1293
1294 rc = flash_write(fd_current, fd_target, dev_target);
1295
1296 if (fsync(fd_current) && !(errno == EINVAL || errno == EROFS)) {
1297 fprintf(stderr,
1298 "fsync failed on %s: %s\n",
1299 DEVNAME(dev_current), strerror(errno));
1300 }
1301
Alex Kiernandbc34322018-03-09 12:13:02 +00001302 if (fd_current != fd_target) {
Alex Kiernan899b5332018-03-09 12:13:01 +00001303 if (fsync(fd_target) &&
1304 !(errno == EINVAL || errno == EROFS)) {
1305 fprintf(stderr,
1306 "fsync failed on %s: %s\n",
1307 DEVNAME(dev_current), strerror(errno));
1308 }
1309
1310 if (close(fd_target)) {
1311 fprintf(stderr,
1312 "I/O error on %s: %s\n",
1313 DEVNAME(dev_target), strerror(errno));
1314 rc = -1;
1315 }
Alex Kiernandbc34322018-03-09 12:13:02 +00001316
Alex Kiernana7623112019-06-12 15:27:55 +01001317 if (rc >= 0 && target_temp) {
Alex Kiernandbc34322018-03-09 12:13:02 +00001318 int dir_fd;
1319
1320 dir_fd = open(dname, O_DIRECTORY | O_RDONLY);
1321 if (dir_fd == -1)
1322 fprintf(stderr,
1323 "Can't open %s: %s\n",
1324 dname, strerror(errno));
1325
1326 if (rename(target_temp, DEVNAME(dev_target))) {
1327 fprintf(stderr,
1328 "rename failed %s => %s: %s\n",
1329 target_temp, DEVNAME(dev_target),
1330 strerror(errno));
1331 rc = -1;
1332 }
1333
1334 if (dir_fd != -1 && fsync(dir_fd))
1335 fprintf(stderr,
1336 "fsync failed on %s: %s\n",
1337 dname, strerror(errno));
1338
1339 if (dir_fd != -1 && close(dir_fd))
1340 fprintf(stderr,
1341 "I/O error on %s: %s\n",
1342 dname, strerror(errno));
1343 }
Alex Kiernan899b5332018-03-09 12:13:01 +00001344 }
1345 exit:
1346 return rc;
1347}
1348
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001349static int flash_io(int mode)
wdenk6aff3112002-12-17 01:51:00 +00001350{
Alex Kiernan899b5332018-03-09 12:13:01 +00001351 int fd_current, rc;
wdenk6aff3112002-12-17 01:51:00 +00001352
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001353 /* dev_current: fd_current, erase_current */
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001354 fd_current = open(DEVNAME(dev_current), mode);
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001355 if (fd_current < 0) {
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001356 fprintf(stderr,
1357 "Can't open %s: %s\n",
1358 DEVNAME(dev_current), strerror(errno));
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001359 return -1;
wdenkd0fb80c2003-01-11 09:48:40 +00001360 }
wdenk6aff3112002-12-17 01:51:00 +00001361
1362 if (mode == O_RDWR) {
Alex Kiernan899b5332018-03-09 12:13:01 +00001363 rc = flash_io_write(fd_current);
wdenk6aff3112002-12-17 01:51:00 +00001364 } else {
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001365 rc = flash_read(fd_current);
wdenk6aff3112002-12-17 01:51:00 +00001366 }
1367
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001368 if (close(fd_current)) {
1369 fprintf(stderr,
1370 "I/O error on %s: %s\n",
1371 DEVNAME(dev_current), strerror(errno));
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001372 return -1;
wdenk6aff3112002-12-17 01:51:00 +00001373 }
1374
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001375 return rc;
wdenk6aff3112002-12-17 01:51:00 +00001376}
1377
1378/*
wdenk6aff3112002-12-17 01:51:00 +00001379 * Prevent confusion if running from erased flash memory
1380 */
Andreas Fenkart81974f42016-04-05 23:13:42 +02001381int fw_env_open(struct env_opts *opts)
wdenk6aff3112002-12-17 01:51:00 +00001382{
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001383 int crc0, crc0_ok;
Jon Povey735eb0f2011-03-11 14:10:56 +09001384 unsigned char flag0;
Stefano Babic33f00862017-04-05 18:08:03 +02001385 void *addr0 = NULL;
wdenkd0fb80c2003-01-11 09:48:40 +00001386
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001387 int crc1, crc1_ok;
Jon Povey735eb0f2011-03-11 14:10:56 +09001388 unsigned char flag1;
Stefano Babic33f00862017-04-05 18:08:03 +02001389 void *addr1 = NULL;
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001390
Marek Vasuta8a752c2014-03-05 19:59:52 +01001391 int ret;
1392
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001393 struct env_image_single *single;
1394 struct env_image_redundant *redundant;
wdenk6aff3112002-12-17 01:51:00 +00001395
Andreas Fenkart14070e62016-05-31 09:21:56 +02001396 if (!opts)
1397 opts = &default_opts;
1398
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001399 if (parse_config(opts)) /* should fill envdevices */
Stefano Babic33f00862017-04-05 18:08:03 +02001400 return -EINVAL;
wdenk4a6fd342003-04-12 23:38:12 +00001401
Joe Hershberger497f2052012-10-03 09:38:46 +00001402 addr0 = calloc(1, CUR_ENVSIZE);
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001403 if (addr0 == NULL) {
Joe Hershberger497f2052012-10-03 09:38:46 +00001404 fprintf(stderr,
wdenk4a6fd342003-04-12 23:38:12 +00001405 "Not enough memory for environment (%ld bytes)\n",
Joe Hershberger497f2052012-10-03 09:38:46 +00001406 CUR_ENVSIZE);
Stefano Babic33f00862017-04-05 18:08:03 +02001407 ret = -ENOMEM;
1408 goto open_cleanup;
wdenkd0fb80c2003-01-11 09:48:40 +00001409 }
wdenk4a6fd342003-04-12 23:38:12 +00001410
wdenkd0fb80c2003-01-11 09:48:40 +00001411 /* read environment from FLASH to local buffer */
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001412 environment.image = addr0;
1413
Alex Kiernan2deb3ca2018-03-09 12:13:00 +00001414 if (have_redund_env) {
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001415 redundant = addr0;
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001416 environment.crc = &redundant->crc;
1417 environment.flags = &redundant->flags;
1418 environment.data = redundant->data;
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001419 } else {
1420 single = addr0;
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001421 environment.crc = &single->crc;
1422 environment.flags = NULL;
1423 environment.data = single->data;
wdenkd0fb80c2003-01-11 09:48:40 +00001424 }
wdenk4a6fd342003-04-12 23:38:12 +00001425
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001426 dev_current = 0;
Tom Rini168de202018-07-01 22:10:33 -04001427 if (flash_io(O_RDONLY)) {
Stefano Babic33f00862017-04-05 18:08:03 +02001428 ret = -EIO;
1429 goto open_cleanup;
1430 }
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001431
Tom Rini168de202018-07-01 22:10:33 -04001432 crc0 = crc32(0, (uint8_t *)environment.data, ENV_SIZE);
1433
1434 crc0_ok = (crc0 == *environment.crc);
Alex Kiernan2deb3ca2018-03-09 12:13:00 +00001435 if (!have_redund_env) {
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001436 if (!crc0_ok) {
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001437 fprintf(stderr,
wdenk4a6fd342003-04-12 23:38:12 +00001438 "Warning: Bad CRC, using default environment\n");
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001439 memcpy(environment.data, default_environment,
1440 sizeof(default_environment));
wdenk6aff3112002-12-17 01:51:00 +00001441 }
wdenkd0fb80c2003-01-11 09:48:40 +00001442 } else {
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001443 flag0 = *environment.flags;
wdenk4a6fd342003-04-12 23:38:12 +00001444
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001445 dev_current = 1;
Joe Hershberger497f2052012-10-03 09:38:46 +00001446 addr1 = calloc(1, CUR_ENVSIZE);
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001447 if (addr1 == NULL) {
Joe Hershberger497f2052012-10-03 09:38:46 +00001448 fprintf(stderr,
wdenk4a6fd342003-04-12 23:38:12 +00001449 "Not enough memory for environment (%ld bytes)\n",
Joe Hershberger497f2052012-10-03 09:38:46 +00001450 CUR_ENVSIZE);
Stefano Babic33f00862017-04-05 18:08:03 +02001451 ret = -ENOMEM;
1452 goto open_cleanup;
wdenk4a6fd342003-04-12 23:38:12 +00001453 }
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001454 redundant = addr1;
wdenk4a6fd342003-04-12 23:38:12 +00001455
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001456 /*
1457 * have to set environment.image for flash_read(), careful -
1458 * other pointers in environment still point inside addr0
1459 */
1460 environment.image = addr1;
Stefano Babic33f00862017-04-05 18:08:03 +02001461 if (flash_io(O_RDONLY)) {
Tom Rini168de202018-07-01 22:10:33 -04001462 ret = -EIO;
1463 goto open_cleanup;
Stefano Babic33f00862017-04-05 18:08:03 +02001464 }
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001465
1466 /* Check flag scheme compatibility */
1467 if (DEVTYPE(dev_current) == MTD_NORFLASH &&
1468 DEVTYPE(!dev_current) == MTD_NORFLASH) {
1469 environment.flag_scheme = FLAG_BOOLEAN;
1470 } else if (DEVTYPE(dev_current) == MTD_NANDFLASH &&
1471 DEVTYPE(!dev_current) == MTD_NANDFLASH) {
1472 environment.flag_scheme = FLAG_INCREMENTAL;
Remy Bohmer9eeaa8e2011-02-12 19:06:26 +01001473 } else if (DEVTYPE(dev_current) == MTD_DATAFLASH &&
1474 DEVTYPE(!dev_current) == MTD_DATAFLASH) {
1475 environment.flag_scheme = FLAG_BOOLEAN;
Joe Hershberger785881f2013-04-08 10:32:52 +00001476 } else if (DEVTYPE(dev_current) == MTD_UBIVOLUME &&
1477 DEVTYPE(!dev_current) == MTD_UBIVOLUME) {
1478 environment.flag_scheme = FLAG_INCREMENTAL;
Oliver Metz23ef62d2013-08-30 00:56:01 +02001479 } else if (DEVTYPE(dev_current) == MTD_ABSENT &&
S. Lockwood-Childs34255b92017-11-14 23:01:26 -08001480 DEVTYPE(!dev_current) == MTD_ABSENT &&
1481 IS_UBI(dev_current) == IS_UBI(!dev_current)) {
Oliver Metz23ef62d2013-08-30 00:56:01 +02001482 environment.flag_scheme = FLAG_INCREMENTAL;
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001483 } else {
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001484 fprintf(stderr, "Incompatible flash types!\n");
Stefano Babic33f00862017-04-05 18:08:03 +02001485 ret = -EINVAL;
1486 goto open_cleanup;
wdenk6aff3112002-12-17 01:51:00 +00001487 }
wdenk4a6fd342003-04-12 23:38:12 +00001488
Tom Rini168de202018-07-01 22:10:33 -04001489 crc1 = crc32(0, (uint8_t *)redundant->data, ENV_SIZE);
1490
1491 crc1_ok = (crc1 == redundant->crc);
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001492 flag1 = redundant->flags;
wdenk4a6fd342003-04-12 23:38:12 +00001493
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001494 if (crc0_ok && !crc1_ok) {
1495 dev_current = 0;
1496 } else if (!crc0_ok && crc1_ok) {
1497 dev_current = 1;
1498 } else if (!crc0_ok && !crc1_ok) {
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001499 fprintf(stderr,
wdenk4a6fd342003-04-12 23:38:12 +00001500 "Warning: Bad CRC, using default environment\n");
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001501 memcpy(environment.data, default_environment,
1502 sizeof(default_environment));
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001503 dev_current = 0;
1504 } else {
1505 switch (environment.flag_scheme) {
1506 case FLAG_BOOLEAN:
Simon Glassd3716dd2019-08-01 09:47:08 -06001507 if (flag0 == ENV_REDUND_ACTIVE &&
1508 flag1 == ENV_REDUND_OBSOLETE) {
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001509 dev_current = 0;
Simon Glassd3716dd2019-08-01 09:47:08 -06001510 } else if (flag0 == ENV_REDUND_OBSOLETE &&
1511 flag1 == ENV_REDUND_ACTIVE) {
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001512 dev_current = 1;
1513 } else if (flag0 == flag1) {
1514 dev_current = 0;
1515 } else if (flag0 == 0xFF) {
1516 dev_current = 0;
1517 } else if (flag1 == 0xFF) {
1518 dev_current = 1;
1519 } else {
1520 dev_current = 0;
1521 }
1522 break;
1523 case FLAG_INCREMENTAL:
Jon Povey735eb0f2011-03-11 14:10:56 +09001524 if (flag0 == 255 && flag1 == 0)
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001525 dev_current = 1;
1526 else if ((flag1 == 255 && flag0 == 0) ||
Jon Povey735eb0f2011-03-11 14:10:56 +09001527 flag0 >= flag1)
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001528 dev_current = 0;
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001529 else /* flag1 > flag0 */
Jon Povey735eb0f2011-03-11 14:10:56 +09001530 dev_current = 1;
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001531 break;
1532 default:
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001533 fprintf(stderr, "Unknown flag scheme %u\n",
1534 environment.flag_scheme);
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001535 return -1;
1536 }
1537 }
1538
1539 /*
1540 * If we are reading, we don't need the flag and the CRC any
1541 * more, if we are writing, we will re-calculate CRC and update
1542 * flags before writing out
1543 */
1544 if (dev_current) {
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001545 environment.image = addr1;
1546 environment.crc = &redundant->crc;
1547 environment.flags = &redundant->flags;
1548 environment.data = redundant->data;
1549 free(addr0);
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001550 } else {
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001551 environment.image = addr0;
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001552 /* Other pointers are already set */
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001553 free(addr1);
wdenk6aff3112002-12-17 01:51:00 +00001554 }
Joe Hershberger74620e12012-10-15 15:29:25 +00001555#ifdef DEBUG
1556 fprintf(stderr, "Selected env in %s\n", DEVNAME(dev_current));
1557#endif
wdenk6aff3112002-12-17 01:51:00 +00001558 }
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001559 return 0;
Stefano Babic33f00862017-04-05 18:08:03 +02001560
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001561 open_cleanup:
Stefano Babic33f00862017-04-05 18:08:03 +02001562 if (addr0)
1563 free(addr0);
1564
1565 if (addr1)
Björn Stenberg2b4ea2b2019-04-17 16:56:54 +02001566 free(addr1);
Stefano Babic33f00862017-04-05 18:08:03 +02001567
1568 return ret;
1569}
1570
1571/*
1572 * Simply free allocated buffer with environment
1573 */
1574int fw_env_close(struct env_opts *opts)
1575{
1576 if (environment.image)
1577 free(environment.image);
1578
1579 environment.image = NULL;
1580
1581 return 0;
wdenk6aff3112002-12-17 01:51:00 +00001582}
1583
Stefan Agner14fb5b22016-07-13 17:14:37 -07001584static int check_device_config(int dev)
1585{
1586 struct stat st;
S. Lockwood-Childs34255b92017-11-14 23:01:26 -08001587 int32_t lnum = 0;
Stefan Agner14fb5b22016-07-13 17:14:37 -07001588 int fd, rc = 0;
1589
S. Lockwood-Childs34255b92017-11-14 23:01:26 -08001590 /* Fills in IS_UBI(), converts DEVNAME() with ubi volume name */
1591 ubi_check_dev(dev);
1592
Stefan Agner14fb5b22016-07-13 17:14:37 -07001593 fd = open(DEVNAME(dev), O_RDONLY);
1594 if (fd < 0) {
1595 fprintf(stderr,
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001596 "Cannot open %s: %s\n", DEVNAME(dev), strerror(errno));
Stefan Agner14fb5b22016-07-13 17:14:37 -07001597 return -1;
1598 }
1599
1600 rc = fstat(fd, &st);
1601 if (rc < 0) {
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001602 fprintf(stderr, "Cannot stat the file %s\n", DEVNAME(dev));
Stefan Agner14fb5b22016-07-13 17:14:37 -07001603 goto err;
1604 }
1605
S. Lockwood-Childs34255b92017-11-14 23:01:26 -08001606 if (IS_UBI(dev)) {
1607 rc = ioctl(fd, UBI_IOCEBISMAP, &lnum);
1608 if (rc < 0) {
1609 fprintf(stderr, "Cannot get UBI information for %s\n",
1610 DEVNAME(dev));
1611 goto err;
1612 }
1613 } else if (S_ISCHR(st.st_mode)) {
Stefan Agner14fb5b22016-07-13 17:14:37 -07001614 struct mtd_info_user mtdinfo;
1615 rc = ioctl(fd, MEMGETINFO, &mtdinfo);
1616 if (rc < 0) {
1617 fprintf(stderr, "Cannot get MTD information for %s\n",
1618 DEVNAME(dev));
1619 goto err;
1620 }
1621 if (mtdinfo.type != MTD_NORFLASH &&
1622 mtdinfo.type != MTD_NANDFLASH &&
1623 mtdinfo.type != MTD_DATAFLASH &&
1624 mtdinfo.type != MTD_UBIVOLUME) {
1625 fprintf(stderr, "Unsupported flash type %u on %s\n",
1626 mtdinfo.type, DEVNAME(dev));
1627 goto err;
1628 }
1629 DEVTYPE(dev) = mtdinfo.type;
Max Krummenacher333ee162016-11-19 13:58:56 +01001630 if (DEVESIZE(dev) == 0)
1631 /* Assume the erase size is the same as the env-size */
1632 DEVESIZE(dev) = ENVSIZE(dev);
Stefan Agner14fb5b22016-07-13 17:14:37 -07001633 } else {
Stefan Agnerf4742ca2016-07-13 17:14:38 -07001634 uint64_t size;
Stefan Agner14fb5b22016-07-13 17:14:37 -07001635 DEVTYPE(dev) = MTD_ABSENT;
Max Krummenacher333ee162016-11-19 13:58:56 +01001636 if (DEVESIZE(dev) == 0)
1637 /* Assume the erase size to be 512 bytes */
1638 DEVESIZE(dev) = 0x200;
Stefan Agnerf4742ca2016-07-13 17:14:38 -07001639
1640 /*
1641 * Check for negative offsets, treat it as backwards offset
1642 * from the end of the block device
1643 */
1644 if (DEVOFFSET(dev) < 0) {
1645 rc = ioctl(fd, BLKGETSIZE64, &size);
1646 if (rc < 0) {
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001647 fprintf(stderr,
1648 "Could not get block device size on %s\n",
Stefan Agnerf4742ca2016-07-13 17:14:38 -07001649 DEVNAME(dev));
1650 goto err;
1651 }
1652
1653 DEVOFFSET(dev) = DEVOFFSET(dev) + size;
1654#ifdef DEBUG
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001655 fprintf(stderr,
1656 "Calculated device offset 0x%llx on %s\n",
Stefan Agnerf4742ca2016-07-13 17:14:38 -07001657 DEVOFFSET(dev), DEVNAME(dev));
1658#endif
1659 }
Stefan Agner14fb5b22016-07-13 17:14:37 -07001660 }
1661
Max Krummenacher333ee162016-11-19 13:58:56 +01001662 if (ENVSECTORS(dev) == 0)
1663 /* Assume enough sectors to cover the environment */
1664 ENVSECTORS(dev) = DIV_ROUND_UP(ENVSIZE(dev), DEVESIZE(dev));
1665
1666 if (DEVOFFSET(dev) % DEVESIZE(dev) != 0) {
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001667 fprintf(stderr,
1668 "Environment does not start on (erase) block boundary\n");
Max Krummenacher333ee162016-11-19 13:58:56 +01001669 errno = EINVAL;
1670 return -1;
1671 }
1672
1673 if (ENVSIZE(dev) > ENVSECTORS(dev) * DEVESIZE(dev)) {
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001674 fprintf(stderr,
1675 "Environment does not fit into available sectors\n");
Max Krummenacher333ee162016-11-19 13:58:56 +01001676 errno = EINVAL;
1677 return -1;
1678 }
1679
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001680 err:
Stefan Agner14fb5b22016-07-13 17:14:37 -07001681 close(fd);
1682 return rc;
1683}
wdenk6aff3112002-12-17 01:51:00 +00001684
Andreas Fenkart81974f42016-04-05 23:13:42 +02001685static int parse_config(struct env_opts *opts)
wdenk6aff3112002-12-17 01:51:00 +00001686{
Stefan Agner14fb5b22016-07-13 17:14:37 -07001687 int rc;
wdenk6aff3112002-12-17 01:51:00 +00001688
Andreas Fenkart14070e62016-05-31 09:21:56 +02001689 if (!opts)
1690 opts = &default_opts;
Anatolij Gustschin925c97c2016-04-29 22:00:11 +02001691
Andreas Fenkart14070e62016-05-31 09:21:56 +02001692#if defined(CONFIG_FILE)
wdenkd0fb80c2003-01-11 09:48:40 +00001693 /* Fills in DEVNAME(), ENVSIZE(), DEVESIZE(). Or don't. */
Andreas Fenkart81974f42016-04-05 23:13:42 +02001694 if (get_config(opts->config_file)) {
Andreas Fenkart371ee132015-12-09 13:13:24 +01001695 fprintf(stderr, "Cannot parse config file '%s': %m\n",
Andreas Fenkart81974f42016-04-05 23:13:42 +02001696 opts->config_file);
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001697 return -1;
wdenk6aff3112002-12-17 01:51:00 +00001698 }
wdenkd0fb80c2003-01-11 09:48:40 +00001699#else
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001700 DEVNAME(0) = DEVICE1_NAME;
1701 DEVOFFSET(0) = DEVICE1_OFFSET;
1702 ENVSIZE(0) = ENV1_SIZE;
Max Krummenacher333ee162016-11-19 13:58:56 +01001703
1704 /* Set defaults for DEVESIZE, ENVSECTORS later once we
1705 * know DEVTYPE
1706 */
Remy Bohmer9eeaa8e2011-02-12 19:06:26 +01001707#ifdef DEVICE1_ESIZE
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001708 DEVESIZE(0) = DEVICE1_ESIZE;
Remy Bohmer9eeaa8e2011-02-12 19:06:26 +01001709#endif
1710#ifdef DEVICE1_ENVSECTORS
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001711 ENVSECTORS(0) = DEVICE1_ENVSECTORS;
Remy Bohmer9eeaa8e2011-02-12 19:06:26 +01001712#endif
1713
wdenk6aff3112002-12-17 01:51:00 +00001714#ifdef HAVE_REDUND
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001715 DEVNAME(1) = DEVICE2_NAME;
1716 DEVOFFSET(1) = DEVICE2_OFFSET;
1717 ENVSIZE(1) = ENV2_SIZE;
Max Krummenacher333ee162016-11-19 13:58:56 +01001718
1719 /* Set defaults for DEVESIZE, ENVSECTORS later once we
1720 * know DEVTYPE
1721 */
Remy Bohmer9eeaa8e2011-02-12 19:06:26 +01001722#ifdef DEVICE2_ESIZE
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001723 DEVESIZE(1) = DEVICE2_ESIZE;
Remy Bohmer9eeaa8e2011-02-12 19:06:26 +01001724#endif
1725#ifdef DEVICE2_ENVSECTORS
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001726 ENVSECTORS(1) = DEVICE2_ENVSECTORS;
Remy Bohmer9eeaa8e2011-02-12 19:06:26 +01001727#endif
Alex Kiernan2deb3ca2018-03-09 12:13:00 +00001728 have_redund_env = 1;
wdenk6aff3112002-12-17 01:51:00 +00001729#endif
wdenkd0fb80c2003-01-11 09:48:40 +00001730#endif
Stefan Agner14fb5b22016-07-13 17:14:37 -07001731 rc = check_device_config(0);
1732 if (rc < 0)
1733 return rc;
wdenk4a6fd342003-04-12 23:38:12 +00001734
Alex Kiernan2deb3ca2018-03-09 12:13:00 +00001735 if (have_redund_env) {
Stefan Agner14fb5b22016-07-13 17:14:37 -07001736 rc = check_device_config(1);
1737 if (rc < 0)
1738 return rc;
Andreas Fenkartf71cee42016-04-19 22:43:42 +02001739
Stefan Agner14fb5b22016-07-13 17:14:37 -07001740 if (ENVSIZE(0) != ENVSIZE(1)) {
Stefan Agner14fb5b22016-07-13 17:14:37 -07001741 fprintf(stderr,
Philip Molloy9337a082019-03-31 03:44:57 +00001742 "Redundant environments have unequal size\n");
Andreas Fenkart490365c2016-08-17 23:41:53 +02001743 return -1;
Stefan Agner14fb5b22016-07-13 17:14:37 -07001744 }
Andreas Fenkartf71cee42016-04-19 22:43:42 +02001745 }
1746
1747 usable_envsize = CUR_ENVSIZE - sizeof(uint32_t);
Alex Kiernan2deb3ca2018-03-09 12:13:00 +00001748 if (have_redund_env)
Andreas Fenkartf71cee42016-04-19 22:43:42 +02001749 usable_envsize -= sizeof(char);
1750
wdenk6aff3112002-12-17 01:51:00 +00001751 return 0;
1752}
wdenkd0fb80c2003-01-11 09:48:40 +00001753
1754#if defined(CONFIG_FILE)
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001755static int get_config(char *fname)
wdenkd0fb80c2003-01-11 09:48:40 +00001756{
1757 FILE *fp;
1758 int i = 0;
1759 int rc;
Alex Kiernan94b233f2018-06-07 12:20:05 +00001760 char *line = NULL;
1761 size_t linesize = 0;
Tom Rini229695f2014-03-28 12:03:33 -04001762 char *devname;
wdenkd0fb80c2003-01-11 09:48:40 +00001763
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001764 fp = fopen(fname, "r");
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001765 if (fp == NULL)
1766 return -1;
wdenkd0fb80c2003-01-11 09:48:40 +00001767
Alex Kiernan94b233f2018-06-07 12:20:05 +00001768 while (i < 2 && getline(&line, &linesize, fp) != -1) {
1769 /* Skip comment strings */
1770 if (line[0] == '#')
wdenkd0fb80c2003-01-11 09:48:40 +00001771 continue;
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001772
Alex Kiernan94b233f2018-06-07 12:20:05 +00001773 rc = sscanf(line, "%ms %lli %lx %lx %lx",
Stefan Agnerf4742ca2016-07-13 17:14:38 -07001774 &devname,
1775 &DEVOFFSET(i),
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001776 &ENVSIZE(i), &DEVESIZE(i), &ENVSECTORS(i));
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001777
Remy Bohmer9eeaa8e2011-02-12 19:06:26 +01001778 if (rc < 3)
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001779 continue;
1780
Tom Rini229695f2014-03-28 12:03:33 -04001781 DEVNAME(i) = devname;
1782
Max Krummenacher333ee162016-11-19 13:58:56 +01001783 /* Set defaults for DEVESIZE, ENVSECTORS later once we
1784 * know DEVTYPE
1785 */
wdenkd0fb80c2003-01-11 09:48:40 +00001786
1787 i++;
1788 }
Alex Kiernan94b233f2018-06-07 12:20:05 +00001789 free(line);
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001790 fclose(fp);
wdenk4a6fd342003-04-12 23:38:12 +00001791
Alex Kiernan2deb3ca2018-03-09 12:13:00 +00001792 have_redund_env = i - 1;
Alex Kiernanc7f52c42018-03-09 12:12:59 +00001793 if (!i) { /* No valid entries found */
wdenkd0fb80c2003-01-11 09:48:40 +00001794 errno = EINVAL;
Guennadi Liakhovetski56086922008-09-04 13:01:49 +02001795 return -1;
wdenkd0fb80c2003-01-11 09:48:40 +00001796 } else
1797 return 0;
1798}
1799#endif