blob: e2801f595f6f824b16af1489227b1453c6a12481 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
wdenk34b613e2002-12-17 01:51:00 +00002/*
Detlev Zundel2111d3e2010-07-30 11:22:15 +02003 * (C) Copyright 2000-2010
wdenk34b613e2002-12-17 01:51:00 +00004 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
5 *
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02006 * (C) Copyright 2008
7 * Guennadi Liakhovetski, DENX Software Engineering, lg@denx.de.
wdenk34b613e2002-12-17 01:51:00 +00008 */
9
Jörg Krause983487f2015-04-22 21:36:22 +020010#define _GNU_SOURCE
11
Peter Robinson656ee042015-12-09 07:15:33 +000012#include <compiler.h>
Simon Glass5e6201b2019-08-01 09:46:51 -060013#include <env.h>
wdenk34b613e2002-12-17 01:51:00 +000014#include <errno.h>
Joe Hershbergerdd7750a2012-12-11 22:16:32 -060015#include <env_flags.h>
wdenk34b613e2002-12-17 01:51:00 +000016#include <fcntl.h>
Alex Kiernan7150aaa2018-03-09 12:13:02 +000017#include <libgen.h>
Stefan Agnerb21e6022016-07-13 17:14:38 -070018#include <linux/fs.h>
Joe Hershbergere79a91f2012-10-12 10:23:37 +000019#include <linux/stringify.h>
Andreas Fenkartd0e12cb2016-03-11 09:39:35 +010020#include <ctype.h>
wdenk34b613e2002-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-Childsa1015c22017-11-14 23:01:26 -080029#include <dirent.h>
Markus Klotzbücher94db5572007-11-27 10:23:20 +010030
31#ifdef MTD_OLD
Wolfgang Denk4cd94a72008-09-02 21:17:36 +020032# include <stdint.h>
Markus Klotzbücher94db5572007-11-27 10:23:20 +010033# include <linux/mtd/mtd.h>
34#else
Wolfgang Denkc7d16fd2008-01-08 22:58:27 +010035# define __user /* nothing */
Markus Klotzbücher94db5572007-11-27 10:23:20 +010036# include <mtd/mtd-user.h>
37#endif
wdenk34b613e2002-12-17 01:51:00 +000038
S. Lockwood-Childsa1015c22017-11-14 23:01:26 -080039#include <mtd/ubi-user.h>
40
Stefano Babic831c4d52017-04-05 18:08:01 +020041#include "fw_env_private.h"
Markus Klotzbücher94db5572007-11-27 10:23:20 +010042#include "fw_env.h"
wdenk34b613e2002-12-17 01:51:00 +000043
Andreas Fenkarte6511302016-05-31 09:21:56 +020044struct env_opts default_opts = {
45#ifdef CONFIG_FILE
46 .config_file = CONFIG_FILE
47#endif
48};
49
Marek Vasut05ed2a52014-03-05 19:59:52 +010050#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
51
Guennadi Liakhovetskiffb379a2008-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 Rini313e9122014-03-28 12:03:33 -040059 const char *devname; /* Device name */
Stefan Agnerb21e6022016-07-13 17:14:38 -070060 long long devoff; /* Device offset */
wdenk3f9ab982003-04-12 23:38:12 +000061 ulong env_size; /* environment size */
62 ulong erase_size; /* device erase size */
Guennadi Liakhovetskiffb379a2008-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-Childsa1015c22017-11-14 23:01:26 -080065 int is_ubi; /* set if we use UBI volume */
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +020066};
wdenk34b613e2002-12-17 01:51:00 +000067
Alex Kiernane9c62202018-03-09 12:12:59 +000068static struct envdev_s envdevices[2] = {
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +020069 {
70 .mtd_type = MTD_ABSENT,
71 }, {
72 .mtd_type = MTD_ABSENT,
73 },
74};
Alex Kiernane9c62202018-03-09 12:12:59 +000075
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +020076static int dev_current;
wdenk34b613e2002-12-17 01:51:00 +000077
78#define DEVNAME(i) envdevices[(i)].devname
wdenke7f34c62003-01-11 09:48:40 +000079#define DEVOFFSET(i) envdevices[(i)].devoff
wdenk34b613e2002-12-17 01:51:00 +000080#define ENVSIZE(i) envdevices[(i)].env_size
81#define DEVESIZE(i) envdevices[(i)].erase_size
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +020082#define ENVSECTORS(i) envdevices[(i)].env_sectors
83#define DEVTYPE(i) envdevices[(i)].mtd_type
S. Lockwood-Childsa1015c22017-11-14 23:01:26 -080084#define IS_UBI(i) envdevices[(i)].is_ubi
wdenk34b613e2002-12-17 01:51:00 +000085
Joe Hershberger671adc42012-10-03 09:38:46 +000086#define CUR_ENVSIZE ENVSIZE(dev_current)
wdenk34b613e2002-12-17 01:51:00 +000087
Andreas Fenkartb8bf4042016-04-19 22:43:42 +020088static unsigned long usable_envsize;
89#define ENV_SIZE usable_envsize
wdenk34b613e2002-12-17 01:51:00 +000090
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +020091struct env_image_single {
Alex Kiernane9c62202018-03-09 12:12:59 +000092 uint32_t crc; /* CRC32 over data bytes */
93 char data[];
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +020094};
wdenk34b613e2002-12-17 01:51:00 +000095
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +020096struct env_image_redundant {
Alex Kiernane9c62202018-03-09 12:12:59 +000097 uint32_t crc; /* CRC32 over data bytes */
98 unsigned char flags; /* active or obsolete */
99 char data[];
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +0200100};
101
102enum flag_scheme {
103 FLAG_NONE,
104 FLAG_BOOLEAN,
105 FLAG_INCREMENTAL,
106};
107
108struct environment {
Alex Kiernane9c62202018-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 Liakhovetskiffb379a2008-09-04 13:01:49 +0200114};
115
116static struct environment environment = {
117 .flag_scheme = FLAG_NONE,
118};
wdenk34b613e2002-12-17 01:51:00 +0000119
Alex Kiernanf46d0ca2018-03-09 12:13:00 +0000120static int have_redund_env;
wdenke7f34c62003-01-11 09:48:40 +0000121
Joe Hershberger81321142012-10-12 08:48:51 +0000122#define DEFAULT_ENV_INSTANCE_STATIC
123#include <env_default.h>
wdenk34b613e2002-12-17 01:51:00 +0000124
S. Lockwood-Childsa1015c22017-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 Kiernane9c62202018-03-09 12:12:59 +0000347static int flash_io(int mode);
Andreas Fenkart24371902016-04-05 23:13:42 +0200348static int parse_config(struct env_opts *opts);
wdenk3f9ab982003-04-12 23:38:12 +0000349
wdenke7f34c62003-01-11 09:48:40 +0000350#if defined(CONFIG_FILE)
Alex Kiernane9c62202018-03-09 12:12:59 +0000351static int get_config(char *);
wdenke7f34c62003-01-11 09:48:40 +0000352#endif
wdenk34b613e2002-12-17 01:51:00 +0000353
Andreas Fenkart191d26f2016-03-11 09:39:37 +0100354static char *skip_chars(char *s)
Stefano Babicada628b2010-05-24 12:08:16 +0200355{
Andreas Fenkart191d26f2016-03-11 09:39:37 +0100356 for (; *s != '\0'; s++) {
Stefan Agner87833242018-03-01 14:06:32 +0100357 if (isblank(*s) || *s == '=')
Andreas Fenkart191d26f2016-03-11 09:39:37 +0100358 return s;
359 }
360 return NULL;
361}
Stefano Babicada628b2010-05-24 12:08:16 +0200362
Andreas Fenkart191d26f2016-03-11 09:39:37 +0100363static char *skip_blanks(char *s)
364{
365 for (; *s != '\0'; s++) {
366 if (!isblank(*s))
Andreas Fenkarte42c68e2016-03-11 09:39:36 +0100367 return s;
Stefano Babicada628b2010-05-24 12:08:16 +0200368 }
Andreas Fenkarte42c68e2016-03-11 09:39:36 +0100369 return NULL;
Stefano Babicada628b2010-05-24 12:08:16 +0200370}
371
wdenk34b613e2002-12-17 01:51:00 +0000372/*
Andreas Fenkart3ff10742016-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/**
wdenk34b613e2002-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 Kiernane9c62202018-03-09 12:12:59 +0000394char *fw_getenv(char *name)
wdenk34b613e2002-12-17 01:51:00 +0000395{
Markus Klotzbücher94db5572007-11-27 10:23:20 +0100396 char *env, *nxt;
wdenk34b613e2002-12-17 01:51:00 +0000397
wdenk3f9ab982003-04-12 23:38:12 +0000398 for (env = environment.data; *env; env = nxt + 1) {
Markus Klotzbücher94db5572007-11-27 10:23:20 +0100399 char *val;
wdenk34b613e2002-12-17 01:51:00 +0000400
wdenk3f9ab982003-04-12 23:38:12 +0000401 for (nxt = env; *nxt; ++nxt) {
wdenk34b613e2002-12-17 01:51:00 +0000402 if (nxt >= &environment.data[ENV_SIZE]) {
Alex Kiernane9c62202018-03-09 12:12:59 +0000403 fprintf(stderr, "## Error: "
wdenk34b613e2002-12-17 01:51:00 +0000404 "environment not terminated\n");
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +0200405 return NULL;
wdenk34b613e2002-12-17 01:51:00 +0000406 }
407 }
Alex Kiernane9c62202018-03-09 12:12:59 +0000408 val = envmatch(name, env);
wdenk34b613e2002-12-17 01:51:00 +0000409 if (!val)
410 continue;
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +0200411 return val;
wdenk34b613e2002-12-17 01:51:00 +0000412 }
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +0200413 return NULL;
wdenk34b613e2002-12-17 01:51:00 +0000414}
415
416/*
Joe Hershberger6fe26c92012-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/*
wdenk34b613e2002-12-17 01:51:00 +0000443 * Print the current definition of one, or more, or all
444 * environment variables
445 */
Andreas Fenkart24371902016-04-05 23:13:42 +0200446int fw_printenv(int argc, char *argv[], int value_only, struct env_opts *opts)
wdenk34b613e2002-12-17 01:51:00 +0000447{
Andreas Fenkartaa01f8d2015-12-09 13:13:24 +0100448 int i, rc = 0;
wdenk34b613e2002-12-17 01:51:00 +0000449
Andreas Fenkart33aaef92016-07-16 17:06:15 +0200450 if (value_only && argc != 1) {
451 fprintf(stderr,
Alex Kiernan688cee92018-02-11 17:16:46 +0000452 "## Error: `-n'/`--noheader' option requires exactly one argument\n");
Andreas Fenkart33aaef92016-07-16 17:06:15 +0200453 return -1;
454 }
455
Andreas Fenkarte6511302016-05-31 09:21:56 +0200456 if (!opts)
457 opts = &default_opts;
458
Andreas Fenkart24371902016-04-05 23:13:42 +0200459 if (fw_env_open(opts))
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +0200460 return -1;
wdenk34b613e2002-12-17 01:51:00 +0000461
Alex Kiernane9c62202018-03-09 12:12:59 +0000462 if (argc == 0) { /* Print all env variables */
Andreas Fenkart33aaef92016-07-16 17:06:15 +0200463 char *env, *nxt;
wdenk3f9ab982003-04-12 23:38:12 +0000464 for (env = environment.data; *env; env = nxt + 1) {
465 for (nxt = env; *nxt; ++nxt) {
wdenk34b613e2002-12-17 01:51:00 +0000466 if (nxt >= &environment.data[ENV_SIZE]) {
Alex Kiernane9c62202018-03-09 12:12:59 +0000467 fprintf(stderr, "## Error: "
wdenk34b613e2002-12-17 01:51:00 +0000468 "environment not terminated\n");
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +0200469 return -1;
wdenk34b613e2002-12-17 01:51:00 +0000470 }
471 }
472
Alex Kiernane9c62202018-03-09 12:12:59 +0000473 printf("%s\n", env);
wdenk34b613e2002-12-17 01:51:00 +0000474 }
Stefano Babic4b0a2932017-04-05 18:08:03 +0200475 fw_env_close(opts);
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +0200476 return 0;
wdenk34b613e2002-12-17 01:51:00 +0000477 }
478
Andreas Fenkart33aaef92016-07-16 17:06:15 +0200479 for (i = 0; i < argc; ++i) { /* print a subset of env variables */
Markus Klotzbücher94db5572007-11-27 10:23:20 +0100480 char *name = argv[i];
481 char *val = NULL;
wdenk34b613e2002-12-17 01:51:00 +0000482
Andreas Fenkart33aaef92016-07-16 17:06:15 +0200483 val = fw_getenv(name);
Grant Erickson73a4e5e2008-05-06 20:16:15 -0700484 if (!val) {
Alex Kiernane9c62202018-03-09 12:12:59 +0000485 fprintf(stderr, "## Error: \"%s\" not defined\n", name);
Grant Erickson73a4e5e2008-05-06 20:16:15 -0700486 rc = -1;
Andreas Fenkart33aaef92016-07-16 17:06:15 +0200487 continue;
Grant Erickson73a4e5e2008-05-06 20:16:15 -0700488 }
Andreas Fenkart33aaef92016-07-16 17:06:15 +0200489
490 if (value_only) {
491 puts(val);
492 break;
493 }
494
495 printf("%s=%s\n", name, val);
wdenk34b613e2002-12-17 01:51:00 +0000496 }
Grant Erickson73a4e5e2008-05-06 20:16:15 -0700497
Stefano Babic4b0a2932017-04-05 18:08:03 +0200498 fw_env_close(opts);
499
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +0200500 return rc;
wdenk34b613e2002-12-17 01:51:00 +0000501}
502
Stefano Babic4b0a2932017-04-05 18:08:03 +0200503int fw_env_flush(struct env_opts *opts)
wdenk34b613e2002-12-17 01:51:00 +0000504{
Andreas Fenkarte6511302016-05-31 09:21:56 +0200505 if (!opts)
506 opts = &default_opts;
507
Stefano Babicada628b2010-05-24 12:08:16 +0200508 /*
509 * Update CRC
510 */
511 *environment.crc = crc32(0, (uint8_t *) environment.data, ENV_SIZE);
wdenk34b613e2002-12-17 01:51:00 +0000512
Stefano Babicada628b2010-05-24 12:08:16 +0200513 /* write environment back to flash */
514 if (flash_io(O_RDWR)) {
Alex Kiernane9c62202018-03-09 12:12:59 +0000515 fprintf(stderr, "Error: can't write fw_env to flash\n");
516 return -1;
wdenk34b613e2002-12-17 01:51:00 +0000517 }
518
Stefano Babicada628b2010-05-24 12:08:16 +0200519 return 0;
520}
wdenk34b613e2002-12-17 01:51:00 +0000521
Stefano Babicada628b2010-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 Hershberger6fe26c92012-12-11 22:16:34 -0600532 int deleting, creating, overwriting;
wdenk34b613e2002-12-17 01:51:00 +0000533
534 /*
535 * search if variable with this name already exists
536 */
wdenk3f9ab982003-04-12 23:38:12 +0000537 for (nxt = env = environment.data; *env; env = nxt + 1) {
538 for (nxt = env; *nxt; ++nxt) {
wdenk34b613e2002-12-17 01:51:00 +0000539 if (nxt >= &environment.data[ENV_SIZE]) {
Stefano Babicada628b2010-05-24 12:08:16 +0200540 fprintf(stderr, "## Error: "
wdenk34b613e2002-12-17 01:51:00 +0000541 "environment not terminated\n");
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +0200542 errno = EINVAL;
543 return -1;
wdenk34b613e2002-12-17 01:51:00 +0000544 }
545 }
Alex Kiernane9c62202018-03-09 12:12:59 +0000546 oldval = envmatch(name, env);
547 if (oldval)
wdenk34b613e2002-12-17 01:51:00 +0000548 break;
549 }
550
Joe Hershberger6fe26c92012-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 Kiernane9c62202018-03-09 12:12:59 +0000570 ENV_FLAGS_VARACCESS_PREVENT_NONDEF_OVERWR)) {
Joe Hershberger6fe26c92012-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) {
wdenk34b613e2002-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 Babicada628b2010-05-24 12:08:16 +0200608 if (!value || !strlen(value))
609 return 0;
wdenk34b613e2002-12-17 01:51:00 +0000610
611 /*
612 * Append new definition at the end
613 */
Alex Kiernane9c62202018-03-09 12:12:59 +0000614 for (env = environment.data; *env || *(env + 1); ++env)
615 ;
wdenk34b613e2002-12-17 01:51:00 +0000616 if (env > environment.data)
617 ++env;
618 /*
619 * Overflow when:
Joe Hershberger671adc42012-10-03 09:38:46 +0000620 * "name" + "=" + "val" +"\0\0" > CUR_ENVSIZE - (env-environment)
wdenk34b613e2002-12-17 01:51:00 +0000621 */
Alex Kiernane9c62202018-03-09 12:12:59 +0000622 len = strlen(name) + 2;
wdenk34b613e2002-12-17 01:51:00 +0000623 /* add '=' for first arg, ' ' for all others */
Stefano Babicada628b2010-05-24 12:08:16 +0200624 len += strlen(value) + 1;
625
wdenk3f9ab982003-04-12 23:38:12 +0000626 if (len > (&environment.data[ENV_SIZE] - env)) {
Alex Kiernane9c62202018-03-09 12:12:59 +0000627 fprintf(stderr,
628 "Error: environment overflow, \"%s\" deleted\n", name);
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +0200629 return -1;
wdenk34b613e2002-12-17 01:51:00 +0000630 }
Stefano Babicada628b2010-05-24 12:08:16 +0200631
wdenk34b613e2002-12-17 01:51:00 +0000632 while ((*env = *name++) != '\0')
633 env++;
Stefano Babicada628b2010-05-24 12:08:16 +0200634 *env = '=';
635 while ((*++env = *value++) != '\0')
636 ;
637
638 /* end is marked with double '\0' */
639 *++env = '\0';
640
641 return 0;
642}
643
644/*
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 Glass6a38e412017-08-03 12:22:09 -0600652int fw_env_set(int argc, char *argv[], struct env_opts *opts)
Stefano Babicada628b2010-05-24 12:08:16 +0200653{
Andreas Fenkartaa01f8d2015-12-09 13:13:24 +0100654 int i;
Mike Frysinger178ec772012-11-10 19:47:45 +0000655 size_t len;
Andreas Fenkart4e336ee2015-12-09 13:13:21 +0100656 char *name, **valv;
xypron.glpk@gmx.de2a3325c2017-04-15 13:05:40 +0200657 char *oldval;
Stefano Babicada628b2010-05-24 12:08:16 +0200658 char *value = NULL;
Andreas Fenkart4e336ee2015-12-09 13:13:21 +0100659 int valc;
Stefano Babic4b0a2932017-04-05 18:08:03 +0200660 int ret;
Stefano Babicada628b2010-05-24 12:08:16 +0200661
Andreas Fenkarte6511302016-05-31 09:21:56 +0200662 if (!opts)
663 opts = &default_opts;
664
Andreas Fenkart14908ef2015-12-09 13:13:25 +0100665 if (argc < 1) {
666 fprintf(stderr, "## Error: variable name missing\n");
Marek Vasut05ed2a52014-03-05 19:59:52 +0100667 errno = EINVAL;
668 return -1;
669 }
670
Andreas Fenkart24371902016-04-05 23:13:42 +0200671 if (fw_env_open(opts)) {
Stefano Babicada628b2010-05-24 12:08:16 +0200672 fprintf(stderr, "Error: environment not initialized\n");
673 return -1;
674 }
675
Andreas Fenkart14908ef2015-12-09 13:13:25 +0100676 name = argv[0];
677 valv = argv + 1;
678 valc = argc - 1;
Stefano Babicada628b2010-05-24 12:08:16 +0200679
Stefano Babic4b0a2932017-04-05 18:08:03 +0200680 if (env_flags_validate_env_set_params(name, valv, valc) < 0) {
681 fw_env_close(opts);
Andreas Fenkartc4a722c2016-07-16 17:06:13 +0200682 return -1;
Stefano Babic4b0a2932017-04-05 18:08:03 +0200683 }
Joe Hershbergerdd7750a2012-12-11 22:16:32 -0600684
Joe Hershbergered97f4c2012-10-03 09:38:47 +0000685 len = 0;
Andreas Fenkart4e336ee2015-12-09 13:13:21 +0100686 for (i = 0; i < valc; ++i) {
687 char *val = valv[i];
Joe Hershbergered97f4c2012-10-03 09:38:47 +0000688 size_t val_len = strlen(val);
689
Joe Hershberger03bf21d2012-10-15 15:29:24 +0000690 if (value)
691 value[len - 1] = ' ';
xypron.glpk@gmx.de2a3325c2017-04-15 13:05:40 +0200692 oldval = value;
Joe Hershbergered97f4c2012-10-03 09:38:47 +0000693 value = realloc(value, len + val_len + 1);
Stefano Babicada628b2010-05-24 12:08:16 +0200694 if (!value) {
Joe Hershbergered97f4c2012-10-03 09:38:47 +0000695 fprintf(stderr,
Luka Perkovd5982712011-09-05 23:40:13 +0200696 "Cannot malloc %zu bytes: %s\n",
Joe Hershbergered97f4c2012-10-03 09:38:47 +0000697 len, strerror(errno));
xypron.glpk@gmx.de2a3325c2017-04-15 13:05:40 +0200698 free(oldval);
Joe Hershbergered97f4c2012-10-03 09:38:47 +0000699 return -1;
Stefano Babicada628b2010-05-24 12:08:16 +0200700 }
Joe Hershbergered97f4c2012-10-03 09:38:47 +0000701
702 memcpy(value + len, val, val_len);
703 len += val_len;
Joe Hershberger03bf21d2012-10-15 15:29:24 +0000704 value[len++] = '\0';
wdenk34b613e2002-12-17 01:51:00 +0000705 }
706
Stefano Babicada628b2010-05-24 12:08:16 +0200707 fw_env_write(name, value);
wdenk34b613e2002-12-17 01:51:00 +0000708
Joe Hershbergered97f4c2012-10-03 09:38:47 +0000709 free(value);
wdenk34b613e2002-12-17 01:51:00 +0000710
Stefano Babic4b0a2932017-04-05 18:08:03 +0200711 ret = fw_env_flush(opts);
712 fw_env_close(opts);
713
714 return ret;
Stefano Babicada628b2010-05-24 12:08:16 +0200715}
wdenk34b613e2002-12-17 01:51:00 +0000716
Stefano Babicada628b2010-05-24 12:08:16 +0200717/*
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 Fenkart24371902016-04-05 23:13:42 +0200734int fw_parse_script(char *fname, struct env_opts *opts)
Stefano Babicada628b2010-05-24 12:08:16 +0200735{
736 FILE *fp;
Alex Kiernan66df4e22018-06-07 12:20:05 +0000737 char *line = NULL;
738 size_t linesize = 0;
Stefano Babicada628b2010-05-24 12:08:16 +0200739 char *name;
740 char *val;
741 int lineno = 0;
742 int len;
743 int ret = 0;
744
Andreas Fenkarte6511302016-05-31 09:21:56 +0200745 if (!opts)
746 opts = &default_opts;
747
Andreas Fenkart24371902016-04-05 23:13:42 +0200748 if (fw_env_open(opts)) {
Stefano Babicada628b2010-05-24 12:08:16 +0200749 fprintf(stderr, "Error: environment not initialized\n");
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +0200750 return -1;
wdenk34b613e2002-12-17 01:51:00 +0000751 }
752
Stefano Babicada628b2010-05-24 12:08:16 +0200753 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 Kiernane9c62202018-03-09 12:12:59 +0000759 fname);
Stefano Babicada628b2010-05-24 12:08:16 +0200760 return -1;
761 }
762 }
763
Alex Kiernan66df4e22018-06-07 12:20:05 +0000764 while ((len = getline(&line, &linesize, fp)) != -1) {
Stefano Babicada628b2010-05-24 12:08:16 +0200765 lineno++;
Stefano Babicada628b2010-05-24 12:08:16 +0200766
767 /*
Alex Kiernan66df4e22018-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 Babicada628b2010-05-24 12:08:16 +0200770 */
Alex Kiernan66df4e22018-06-07 12:20:05 +0000771 if (line[len - 1] != '\n') {
Stefano Babicada628b2010-05-24 12:08:16 +0200772 fprintf(stderr,
Alex Kiernan66df4e22018-06-07 12:20:05 +0000773 "Line %d not correctly terminated\n",
Stefano Babicada628b2010-05-24 12:08:16 +0200774 lineno);
775 ret = -1;
776 break;
777 }
778
779 /* Drop ending line feed / carriage return */
Alex Kiernan66df4e22018-06-07 12:20:05 +0000780 line[--len] = '\0';
781 if (len && line[len - 1] == '\r')
782 line[--len] = '\0';
Stefano Babicada628b2010-05-24 12:08:16 +0200783
784 /* Skip comment or empty lines */
Alex Kiernan66df4e22018-06-07 12:20:05 +0000785 if (len == 0 || line[0] == '#')
Stefano Babicada628b2010-05-24 12:08:16 +0200786 continue;
787
788 /*
Alex Kiernan66df4e22018-06-07 12:20:05 +0000789 * Search for variable's name remove leading whitespaces
Stefano Babicada628b2010-05-24 12:08:16 +0200790 */
Alex Kiernan66df4e22018-06-07 12:20:05 +0000791 name = skip_blanks(line);
Stefano Babicada628b2010-05-24 12:08:16 +0200792 if (!name)
793 continue;
794
795 /* The first white space is the end of variable name */
Andreas Fenkart191d26f2016-03-11 09:39:37 +0100796 val = skip_chars(name);
Stefano Babicada628b2010-05-24 12:08:16 +0200797 len = strlen(name);
798 if (val) {
799 *val++ = '\0';
800 if ((val - name) < len)
Andreas Fenkart191d26f2016-03-11 09:39:37 +0100801 val = skip_blanks(val);
Stefano Babicada628b2010-05-24 12:08:16 +0200802 else
803 val = NULL;
804 }
Stefano Babicada628b2010-05-24 12:08:16 +0200805#ifdef DEBUG
806 fprintf(stderr, "Setting %s : %s\n",
807 name, val ? val : " removed");
808#endif
809
Joe Hershbergerdd7750a2012-12-11 22:16:32 -0600810 if (env_flags_validate_type(name, val) < 0) {
811 ret = -1;
812 break;
813 }
814
Stefano Babicada628b2010-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 Kiernane9c62202018-03-09 12:12:59 +0000821 "fw_env_write returns with error : %s\n",
Stefano Babicada628b2010-05-24 12:08:16 +0200822 strerror(errno));
823 ret = -1;
824 break;
825 }
826
827 }
Alex Kiernan66df4e22018-06-07 12:20:05 +0000828 free(line);
Stefano Babicada628b2010-05-24 12:08:16 +0200829
830 /* Close file if not stdin */
831 if (strcmp(fname, "-") != 0)
832 fclose(fp);
833
Stefano Babic4b0a2932017-04-05 18:08:03 +0200834 ret |= fw_env_flush(opts);
835
836 fw_env_close(opts);
Stefano Babicada628b2010-05-24 12:08:16 +0200837
838 return ret;
wdenk34b613e2002-12-17 01:51:00 +0000839}
840
Andreas Fenkart22281582016-08-29 23:16:57 +0200841/**
Shyam Sainiefe757c2018-06-07 19:47:19 +0530842 * environment_end() - compute offset of first byte right after environment
Andreas Fenkart22281582016-08-29 23:16:57 +0200843 * @dev - index of enviroment buffer
844 * Return:
Shyam Sainiefe757c2018-06-07 19:47:19 +0530845 * device offset of first byte right after environment
Andreas Fenkart22281582016-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 Liakhovetskiffb379a2008-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 Fenkartae3b3202016-08-29 23:16:58 +0200859static int flash_bad_block(int fd, uint8_t mtd_type, loff_t blockstart)
wdenk34b613e2002-12-17 01:51:00 +0000860{
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +0200861 if (mtd_type == MTD_NANDFLASH) {
Andreas Fenkartae3b3202016-08-29 23:16:58 +0200862 int badblock = ioctl(fd, MEMGETBADBLOCK, &blockstart);
wdenk34b613e2002-12-17 01:51:00 +0000863
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +0200864 if (badblock < 0) {
Alex Kiernane9c62202018-03-09 12:12:59 +0000865 perror("Cannot read bad block mark");
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +0200866 return badblock;
867 }
868
869 if (badblock) {
870#ifdef DEBUG
Alex Kiernane9c62202018-03-09 12:12:59 +0000871 fprintf(stderr, "Bad block at 0x%llx, skipping\n",
Andreas Fenkartae3b3202016-08-29 23:16:58 +0200872 (unsigned long long)blockstart);
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +0200873#endif
874 return badblock;
875 }
wdenk34b613e2002-12-17 01:51:00 +0000876 }
877
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +0200878 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 Kiernane9c62202018-03-09 12:12:59 +0000886static int flash_read_buf(int dev, int fd, void *buf, size_t count,
887 off_t offset)
Guennadi Liakhovetskiffb379a2008-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 Liakhovetskiffb379a2008-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 Kiernane9c62202018-03-09 12:12:59 +0000899 blockstart = (offset / DEVESIZE(dev)) * DEVESIZE(dev);
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +0200900
901 /* Offset inside a block */
902 block_seek = offset - blockstart;
903
Andreas Fenkart5e755c72016-08-29 23:16:59 +0200904 if (DEVTYPE(dev) == MTD_NANDFLASH) {
Guennadi Liakhovetskiffb379a2008-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 Kiernane9c62202018-03-09 12:12:59 +0000909 blocklen = DEVESIZE(dev);
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +0200910
Guennadi Liakhovetskiffb379a2008-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;
wdenke7f34c62003-01-11 09:48:40 +0000916 }
wdenk34b613e2002-12-17 01:51:00 +0000917
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +0200918 /* This only runs once on NOR flash */
919 while (processed < count) {
Andreas Fenkart5e755c72016-08-29 23:16:59 +0200920 rc = flash_bad_block(fd, DEVTYPE(dev), blockstart);
Alex Kiernane9c62202018-03-09 12:12:59 +0000921 if (rc < 0) /* block test failed */
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +0200922 return -1;
wdenk34b613e2002-12-17 01:51:00 +0000923
Andreas Fenkart22281582016-08-29 23:16:57 +0200924 if (blockstart + block_seek + readlen > environment_end(dev)) {
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +0200925 /* End of range is reached */
Alex Kiernane9c62202018-03-09 12:12:59 +0000926 fprintf(stderr, "Too few good blocks within range\n");
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +0200927 return -1;
wdenke7f34c62003-01-11 09:48:40 +0000928 }
929
Alex Kiernane9c62202018-03-09 12:12:59 +0000930 if (rc) { /* block is bad */
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +0200931 blockstart += blocklen;
932 continue;
wdenk34b613e2002-12-17 01:51:00 +0000933 }
934
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +0200935 /*
936 * If a block is bad, we retry in the next block at the same
Tom Rini282f2702017-09-05 14:01:29 -0400937 * offset - see env/nand.c::writeenv()
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +0200938 */
Alex Kiernane9c62202018-03-09 12:12:59 +0000939 lseek(fd, blockstart + block_seek, SEEK_SET);
wdenk34b613e2002-12-17 01:51:00 +0000940
Alex Kiernane9c62202018-03-09 12:12:59 +0000941 rc = read(fd, buf + processed, readlen);
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +0200942 if (rc != readlen) {
Alex Kiernane9c62202018-03-09 12:12:59 +0000943 fprintf(stderr, "Read error on %s: %s\n",
944 DEVNAME(dev), strerror(errno));
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +0200945 return -1;
wdenk34b613e2002-12-17 01:51:00 +0000946 }
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +0200947#ifdef DEBUG
Joe Hershbergera8dd2c62012-10-15 15:29:25 +0000948 fprintf(stderr, "Read 0x%x bytes at 0x%llx on %s\n",
Alex Kiernane9c62202018-03-09 12:12:59 +0000949 rc, (unsigned long long)blockstart + block_seek,
Marcin Niestroj20890732016-05-06 14:58:29 +0200950 DEVNAME(dev));
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +0200951#endif
952 processed += readlen;
Alex Kiernane9c62202018-03-09 12:12:59 +0000953 readlen = min(blocklen, count - processed);
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +0200954 block_seek = 0;
955 blockstart += blocklen;
956 }
957
958 return processed;
959}
960
961/*
Andreas Fenkart0b49b842016-08-29 23:17:00 +0200962 * Write count bytes from begin of environment, but stay within
963 * ENVSECTORS(dev) sectors of
Remy Bohmercb969eb2011-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 Liakhovetskiffb379a2008-09-04 13:01:49 +0200966 */
Andreas Fenkart0b49b842016-08-29 23:17:00 +0200967static int flash_write_buf(int dev, int fd, void *buf, size_t count)
Guennadi Liakhovetskiffb379a2008-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 Bohmercb969eb2011-02-12 19:06:26 +0100977 size_t write_total; /* total size to actually write - excluding
Guennadi Liakhovetskiffb379a2008-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 Liakhovetskiffb379a2008-09-04 13:01:49 +0200983 loff_t blockstart; /* running start of the current block -
984 MEMGETBADBLOCK needs 64 bits */
985 int rc;
986
Oliver Metze9c66ab2013-08-30 00:56:02 +0200987 /*
988 * For mtd devices only offset and size of the environment do matter
989 */
Andreas Fenkart5e755c72016-08-29 23:16:59 +0200990 if (DEVTYPE(dev) == MTD_ABSENT) {
Oliver Metze9c66ab2013-08-30 00:56:02 +0200991 blocklen = count;
Oliver Metze9c66ab2013-08-30 00:56:02 +0200992 erase_len = blocklen;
Andreas Fenkart0b49b842016-08-29 23:17:00 +0200993 blockstart = DEVOFFSET(dev);
Oliver Metze9c66ab2013-08-30 00:56:02 +0200994 block_seek = 0;
995 write_total = blocklen;
996 } else {
997 blocklen = DEVESIZE(dev);
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +0200998
Andreas Fenkart0b49b842016-08-29 23:17:00 +0200999 erase_offset = DEVOFFSET(dev);
wdenk34b613e2002-12-17 01:51:00 +00001000
Oliver Metze9c66ab2013-08-30 00:56:02 +02001001 /* Maximum area we may use */
Andreas Fenkart22281582016-08-29 23:16:57 +02001002 erase_len = environment_end(dev) - erase_offset;
wdenk34b613e2002-12-17 01:51:00 +00001003
Oliver Metze9c66ab2013-08-30 00:56:02 +02001004 blockstart = erase_offset;
Andreas Fenkart0b49b842016-08-29 23:17:00 +02001005
Oliver Metze9c66ab2013-08-30 00:56:02 +02001006 /* Offset inside a block */
Andreas Fenkart0b49b842016-08-29 23:17:00 +02001007 block_seek = DEVOFFSET(dev) - erase_offset;
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001008
Oliver Metze9c66ab2013-08-30 00:56:02 +02001009 /*
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 Kiernane9c62202018-03-09 12:12:59 +00001015 blocklen) * blocklen;
Oliver Metze9c66ab2013-08-30 00:56:02 +02001016 }
Guennadi Liakhovetskiffb379a2008-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 Kiernane9c62202018-03-09 12:12:59 +00001024 data = malloc(erase_len);
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001025 if (!data) {
Alex Kiernane9c62202018-03-09 12:12:59 +00001026 fprintf(stderr,
1027 "Cannot malloc %zu bytes: %s\n",
1028 erase_len, strerror(errno));
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001029 return -1;
wdenke7f34c62003-01-11 09:48:40 +00001030 }
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001031
Andreas Fenkart5e755c72016-08-29 23:16:59 +02001032 rc = flash_read_buf(dev, fd, data, write_total, erase_offset);
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001033 if (write_total != rc)
1034 return -1;
1035
Joe Hershbergera8dd2c62012-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 Niestroj20890732016-05-06 14:58:29 +02001043 fprintf(stderr, "0x%lx - 0x%lx",
Alex Kiernane9c62202018-03-09 12:12:59 +00001044 (unsigned long)block_seek + count,
1045 (unsigned long)write_total - 1);
Joe Hershbergera8dd2c62012-10-15 15:29:25 +00001046 }
1047 fprintf(stderr, "\n");
1048#endif
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001049 /* Overwrite the old environment */
Alex Kiernane9c62202018-03-09 12:12:59 +00001050 memcpy(data + block_seek, buf, count);
Guennadi Liakhovetskiffb379a2008-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 Fenkart5e755c72016-08-29 23:16:59 +02001059 if (DEVTYPE(dev) == MTD_NANDFLASH) {
Guennadi Liakhovetskiffb379a2008-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 Bohmercb969eb2011-02-12 19:06:26 +01001071 /* This only runs once on NOR flash and SPI-dataflash */
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001072 while (processed < write_total) {
Andreas Fenkart5e755c72016-08-29 23:16:59 +02001073 rc = flash_bad_block(fd, DEVTYPE(dev), blockstart);
Alex Kiernane9c62202018-03-09 12:12:59 +00001074 if (rc < 0) /* block test failed */
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001075 return rc;
1076
Andreas Fenkart22281582016-08-29 23:16:57 +02001077 if (blockstart + erasesize > environment_end(dev)) {
Alex Kiernane9c62202018-03-09 12:12:59 +00001078 fprintf(stderr, "End of range reached, aborting\n");
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001079 return -1;
wdenk34b613e2002-12-17 01:51:00 +00001080 }
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001081
Alex Kiernane9c62202018-03-09 12:12:59 +00001082 if (rc) { /* block is bad */
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001083 blockstart += blocklen;
1084 continue;
1085 }
1086
Andreas Fenkart5e755c72016-08-29 23:16:59 +02001087 if (DEVTYPE(dev) != MTD_ABSENT) {
Oliver Metze9c66ab2013-08-30 00:56:02 +02001088 erase.start = blockstart;
1089 ioctl(fd, MEMUNLOCK, &erase);
1090 /* These do not need an explicit erase cycle */
Andreas Fenkart5e755c72016-08-29 23:16:59 +02001091 if (DEVTYPE(dev) != MTD_DATAFLASH)
Oliver Metze9c66ab2013-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 Liakhovetskiffb379a2008-09-04 13:01:49 +02001099
Alex Kiernane9c62202018-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 Liakhovetskiffb379a2008-09-04 13:01:49 +02001104 return -1;
wdenk34b613e2002-12-17 01:51:00 +00001105 }
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001106#ifdef DEBUG
Marcin Niestroj20890732016-05-06 14:58:29 +02001107 fprintf(stderr, "Write 0x%llx bytes at 0x%llx\n",
Alex Kiernane9c62202018-03-09 12:12:59 +00001108 (unsigned long long)erasesize,
1109 (unsigned long long)blockstart);
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001110#endif
Alex Kiernane9c62202018-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 Liakhovetskiffb379a2008-09-04 13:01:49 +02001114 return -1;
wdenk34b613e2002-12-17 01:51:00 +00001115 }
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001116
Andreas Fenkart5e755c72016-08-29 23:16:59 +02001117 if (DEVTYPE(dev) != MTD_ABSENT)
Oliver Metze9c66ab2013-08-30 00:56:02 +02001118 ioctl(fd, MEMLOCK, &erase);
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001119
Alex Kiernane9c62202018-03-09 12:12:59 +00001120 processed += erasesize;
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001121 block_seek = 0;
Dustin Byford0983cad2014-03-06 20:48:23 -08001122 blockstart += erasesize;
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001123 }
1124
1125 if (write_total > count)
Alex Kiernane9c62202018-03-09 12:12:59 +00001126 free(data);
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001127
1128 return processed;
1129}
1130
1131/*
1132 * Set obsolete flag at offset - NOR flash only
1133 */
Alex Kiernane9c62202018-03-09 12:12:59 +00001134static int flash_flag_obsolete(int dev, int fd, off_t offset)
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001135{
1136 int rc;
Detlev Zundel2111d3e2010-07-30 11:22:15 +02001137 struct erase_info_user erase;
Pierre-Jean Texierd7f95452019-08-26 13:06:16 +02001138 char tmp = ENV_REDUND_OBSOLETE;
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001139
Alex Kiernane9c62202018-03-09 12:12:59 +00001140 erase.start = DEVOFFSET(dev);
1141 erase.length = DEVESIZE(dev);
Simon Glass4686d8f2019-08-01 09:47:08 -06001142 /* This relies on the fact, that ENV_REDUND_OBSOLETE == 0 */
Alex Kiernane9c62202018-03-09 12:12:59 +00001143 rc = lseek(fd, offset, SEEK_SET);
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001144 if (rc < 0) {
Alex Kiernane9c62202018-03-09 12:12:59 +00001145 fprintf(stderr, "Cannot seek to set the flag on %s\n",
1146 DEVNAME(dev));
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001147 return rc;
1148 }
Alex Kiernane9c62202018-03-09 12:12:59 +00001149 ioctl(fd, MEMUNLOCK, &erase);
Pierre-Jean Texierd7f95452019-08-26 13:06:16 +02001150 rc = write(fd, &tmp, sizeof(tmp));
Alex Kiernane9c62202018-03-09 12:12:59 +00001151 ioctl(fd, MEMLOCK, &erase);
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001152 if (rc < 0)
Alex Kiernane9c62202018-03-09 12:12:59 +00001153 perror("Could not set obsolete flag");
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001154
1155 return rc;
1156}
1157
Alex Kiernane9c62202018-03-09 12:12:59 +00001158static int flash_write(int fd_current, int fd_target, int dev_target)
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001159{
1160 int rc;
1161
1162 switch (environment.flag_scheme) {
1163 case FLAG_NONE:
1164 break;
1165 case FLAG_INCREMENTAL:
1166 (*environment.flags)++;
1167 break;
1168 case FLAG_BOOLEAN:
Simon Glass4686d8f2019-08-01 09:47:08 -06001169 *environment.flags = ENV_REDUND_ACTIVE;
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001170 break;
1171 default:
Alex Kiernane9c62202018-03-09 12:12:59 +00001172 fprintf(stderr, "Unimplemented flash scheme %u\n",
1173 environment.flag_scheme);
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001174 return -1;
1175 }
1176
1177#ifdef DEBUG
Stefan Agnerb21e6022016-07-13 17:14:38 -07001178 fprintf(stderr, "Writing new environment at 0x%llx on %s\n",
Alex Kiernane9c62202018-03-09 12:12:59 +00001179 DEVOFFSET(dev_target), DEVNAME(dev_target));
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001180#endif
Marek Vasut05ed2a52014-03-05 19:59:52 +01001181
S. Lockwood-Childsa1015c22017-11-14 23:01:26 -08001182 if (IS_UBI(dev_target)) {
1183 if (ubi_update_start(fd_target, CUR_ENVSIZE) < 0)
1184 return 0;
1185 return ubi_write(fd_target, environment.image, CUR_ENVSIZE);
1186 }
1187
Joe Hershberger671adc42012-10-03 09:38:46 +00001188 rc = flash_write_buf(dev_target, fd_target, environment.image,
Andreas Fenkart0b49b842016-08-29 23:17:00 +02001189 CUR_ENVSIZE);
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001190 if (rc < 0)
1191 return rc;
1192
1193 if (environment.flag_scheme == FLAG_BOOLEAN) {
1194 /* Have to set obsolete flag */
Alex Kiernane9c62202018-03-09 12:12:59 +00001195 off_t offset = DEVOFFSET(dev_current) +
1196 offsetof(struct env_image_redundant, flags);
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001197#ifdef DEBUG
Joe Hershbergera8dd2c62012-10-15 15:29:25 +00001198 fprintf(stderr,
Stefan Agnerb21e6022016-07-13 17:14:38 -07001199 "Setting obsolete flag in environment at 0x%llx on %s\n",
Alex Kiernane9c62202018-03-09 12:12:59 +00001200 DEVOFFSET(dev_current), DEVNAME(dev_current));
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001201#endif
Alex Kiernane9c62202018-03-09 12:12:59 +00001202 flash_flag_obsolete(dev_current, fd_current, offset);
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001203 }
1204
1205 return 0;
1206}
1207
Alex Kiernane9c62202018-03-09 12:12:59 +00001208static int flash_read(int fd)
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001209{
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001210 int rc;
1211
S. Lockwood-Childsa1015c22017-11-14 23:01:26 -08001212 if (IS_UBI(dev_current)) {
1213 DEVTYPE(dev_current) = MTD_ABSENT;
1214
1215 return ubi_read(fd, environment.image, CUR_ENVSIZE);
1216 }
1217
Joe Hershberger671adc42012-10-03 09:38:46 +00001218 rc = flash_read_buf(dev_current, fd, environment.image, CUR_ENVSIZE,
Andreas Fenkart5e755c72016-08-29 23:16:59 +02001219 DEVOFFSET(dev_current));
Marek Vasut05ed2a52014-03-05 19:59:52 +01001220 if (rc != CUR_ENVSIZE)
1221 return -1;
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001222
Marek Vasut05ed2a52014-03-05 19:59:52 +01001223 return 0;
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001224}
1225
Alex Kiernan7150aaa2018-03-09 12:13:02 +00001226static int flash_open_tempfile(const char **dname, const char **target_temp)
1227{
1228 char *dup_name = strdup(DEVNAME(dev_current));
1229 char *temp_name = NULL;
1230 int rc = -1;
1231
1232 if (!dup_name)
1233 return -1;
1234
1235 *dname = dirname(dup_name);
1236 if (!*dname)
1237 goto err;
1238
1239 rc = asprintf(&temp_name, "%s/XXXXXX", *dname);
1240 if (rc == -1)
1241 goto err;
1242
1243 rc = mkstemp(temp_name);
1244 if (rc == -1) {
1245 /* fall back to in place write */
1246 fprintf(stderr,
1247 "Can't create %s: %s\n", temp_name, strerror(errno));
1248 free(temp_name);
1249 } else {
1250 *target_temp = temp_name;
1251 /* deliberately leak dup_name as dname /might/ point into
1252 * it and we need it for our caller
1253 */
1254 dup_name = NULL;
1255 }
1256
1257err:
1258 if (dup_name)
1259 free(dup_name);
1260
1261 return rc;
1262}
1263
Alex Kiernanbe1e7882018-03-09 12:13:01 +00001264static int flash_io_write(int fd_current)
1265{
Alex Kiernan7150aaa2018-03-09 12:13:02 +00001266 int fd_target = -1, rc, dev_target;
1267 const char *dname, *target_temp = NULL;
Alex Kiernanbe1e7882018-03-09 12:13:01 +00001268
1269 if (have_redund_env) {
1270 /* switch to next partition for writing */
1271 dev_target = !dev_current;
1272 /* dev_target: fd_target, erase_target */
1273 fd_target = open(DEVNAME(dev_target), O_RDWR);
1274 if (fd_target < 0) {
1275 fprintf(stderr,
1276 "Can't open %s: %s\n",
1277 DEVNAME(dev_target), strerror(errno));
1278 rc = -1;
1279 goto exit;
1280 }
1281 } else {
Alex Kiernan7150aaa2018-03-09 12:13:02 +00001282 struct stat sb;
1283
1284 if (fstat(fd_current, &sb) == 0 && S_ISREG(sb.st_mode)) {
1285 /* if any part of flash_open_tempfile() fails we fall
1286 * back to in-place writes
1287 */
1288 fd_target = flash_open_tempfile(&dname, &target_temp);
1289 }
Alex Kiernanbe1e7882018-03-09 12:13:01 +00001290 dev_target = dev_current;
Alex Kiernan7150aaa2018-03-09 12:13:02 +00001291 if (fd_target == -1)
1292 fd_target = fd_current;
Alex Kiernanbe1e7882018-03-09 12:13:01 +00001293 }
1294
1295 rc = flash_write(fd_current, fd_target, dev_target);
1296
1297 if (fsync(fd_current) && !(errno == EINVAL || errno == EROFS)) {
1298 fprintf(stderr,
1299 "fsync failed on %s: %s\n",
1300 DEVNAME(dev_current), strerror(errno));
1301 }
1302
Alex Kiernan7150aaa2018-03-09 12:13:02 +00001303 if (fd_current != fd_target) {
Alex Kiernanbe1e7882018-03-09 12:13:01 +00001304 if (fsync(fd_target) &&
1305 !(errno == EINVAL || errno == EROFS)) {
1306 fprintf(stderr,
1307 "fsync failed on %s: %s\n",
1308 DEVNAME(dev_current), strerror(errno));
1309 }
1310
1311 if (close(fd_target)) {
1312 fprintf(stderr,
1313 "I/O error on %s: %s\n",
1314 DEVNAME(dev_target), strerror(errno));
1315 rc = -1;
1316 }
Alex Kiernan7150aaa2018-03-09 12:13:02 +00001317
Alex Kiernanad4f3d02019-06-12 15:27:55 +01001318 if (rc >= 0 && target_temp) {
Alex Kiernan7150aaa2018-03-09 12:13:02 +00001319 int dir_fd;
1320
1321 dir_fd = open(dname, O_DIRECTORY | O_RDONLY);
1322 if (dir_fd == -1)
1323 fprintf(stderr,
1324 "Can't open %s: %s\n",
1325 dname, strerror(errno));
1326
1327 if (rename(target_temp, DEVNAME(dev_target))) {
1328 fprintf(stderr,
1329 "rename failed %s => %s: %s\n",
1330 target_temp, DEVNAME(dev_target),
1331 strerror(errno));
1332 rc = -1;
1333 }
1334
1335 if (dir_fd != -1 && fsync(dir_fd))
1336 fprintf(stderr,
1337 "fsync failed on %s: %s\n",
1338 dname, strerror(errno));
1339
1340 if (dir_fd != -1 && close(dir_fd))
1341 fprintf(stderr,
1342 "I/O error on %s: %s\n",
1343 dname, strerror(errno));
1344 }
Alex Kiernanbe1e7882018-03-09 12:13:01 +00001345 }
1346 exit:
1347 return rc;
1348}
1349
Alex Kiernane9c62202018-03-09 12:12:59 +00001350static int flash_io(int mode)
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001351{
Alex Kiernanbe1e7882018-03-09 12:13:01 +00001352 int fd_current, rc;
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001353
1354 /* dev_current: fd_current, erase_current */
Alex Kiernane9c62202018-03-09 12:12:59 +00001355 fd_current = open(DEVNAME(dev_current), mode);
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001356 if (fd_current < 0) {
Alex Kiernane9c62202018-03-09 12:12:59 +00001357 fprintf(stderr,
1358 "Can't open %s: %s\n",
1359 DEVNAME(dev_current), strerror(errno));
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001360 return -1;
1361 }
1362
1363 if (mode == O_RDWR) {
Alex Kiernanbe1e7882018-03-09 12:13:01 +00001364 rc = flash_io_write(fd_current);
wdenk34b613e2002-12-17 01:51:00 +00001365 } else {
Alex Kiernane9c62202018-03-09 12:12:59 +00001366 rc = flash_read(fd_current);
wdenk34b613e2002-12-17 01:51:00 +00001367 }
1368
Alex Kiernane9c62202018-03-09 12:12:59 +00001369 if (close(fd_current)) {
1370 fprintf(stderr,
1371 "I/O error on %s: %s\n",
1372 DEVNAME(dev_current), strerror(errno));
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001373 return -1;
wdenk34b613e2002-12-17 01:51:00 +00001374 }
1375
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001376 return rc;
wdenk34b613e2002-12-17 01:51:00 +00001377}
1378
1379/*
wdenk34b613e2002-12-17 01:51:00 +00001380 * Prevent confusion if running from erased flash memory
1381 */
Andreas Fenkart24371902016-04-05 23:13:42 +02001382int fw_env_open(struct env_opts *opts)
wdenk34b613e2002-12-17 01:51:00 +00001383{
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001384 int crc0, crc0_ok;
Jon Povey93ccd472011-03-11 14:10:56 +09001385 unsigned char flag0;
Stefano Babic4b0a2932017-04-05 18:08:03 +02001386 void *addr0 = NULL;
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001387
wdenk34b613e2002-12-17 01:51:00 +00001388 int crc1, crc1_ok;
Jon Povey93ccd472011-03-11 14:10:56 +09001389 unsigned char flag1;
Stefano Babic4b0a2932017-04-05 18:08:03 +02001390 void *addr1 = NULL;
wdenke7f34c62003-01-11 09:48:40 +00001391
Marek Vasut05ed2a52014-03-05 19:59:52 +01001392 int ret;
1393
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001394 struct env_image_single *single;
1395 struct env_image_redundant *redundant;
wdenk34b613e2002-12-17 01:51:00 +00001396
Andreas Fenkarte6511302016-05-31 09:21:56 +02001397 if (!opts)
1398 opts = &default_opts;
1399
Alex Kiernane9c62202018-03-09 12:12:59 +00001400 if (parse_config(opts)) /* should fill envdevices */
Stefano Babic4b0a2932017-04-05 18:08:03 +02001401 return -EINVAL;
wdenk3f9ab982003-04-12 23:38:12 +00001402
Joe Hershberger671adc42012-10-03 09:38:46 +00001403 addr0 = calloc(1, CUR_ENVSIZE);
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001404 if (addr0 == NULL) {
Joe Hershberger671adc42012-10-03 09:38:46 +00001405 fprintf(stderr,
wdenk3f9ab982003-04-12 23:38:12 +00001406 "Not enough memory for environment (%ld bytes)\n",
Joe Hershberger671adc42012-10-03 09:38:46 +00001407 CUR_ENVSIZE);
Stefano Babic4b0a2932017-04-05 18:08:03 +02001408 ret = -ENOMEM;
1409 goto open_cleanup;
wdenke7f34c62003-01-11 09:48:40 +00001410 }
wdenk3f9ab982003-04-12 23:38:12 +00001411
wdenke7f34c62003-01-11 09:48:40 +00001412 /* read environment from FLASH to local buffer */
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001413 environment.image = addr0;
1414
Alex Kiernanf46d0ca2018-03-09 12:13:00 +00001415 if (have_redund_env) {
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001416 redundant = addr0;
Alex Kiernane9c62202018-03-09 12:12:59 +00001417 environment.crc = &redundant->crc;
1418 environment.flags = &redundant->flags;
1419 environment.data = redundant->data;
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001420 } else {
1421 single = addr0;
Alex Kiernane9c62202018-03-09 12:12:59 +00001422 environment.crc = &single->crc;
1423 environment.flags = NULL;
1424 environment.data = single->data;
wdenke7f34c62003-01-11 09:48:40 +00001425 }
wdenk3f9ab982003-04-12 23:38:12 +00001426
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001427 dev_current = 0;
Tom Rini4c1192c2018-07-01 22:10:33 -04001428 if (flash_io(O_RDONLY)) {
Stefano Babic4b0a2932017-04-05 18:08:03 +02001429 ret = -EIO;
1430 goto open_cleanup;
1431 }
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001432
Tom Rini4c1192c2018-07-01 22:10:33 -04001433 crc0 = crc32(0, (uint8_t *)environment.data, ENV_SIZE);
1434
1435 crc0_ok = (crc0 == *environment.crc);
Alex Kiernanf46d0ca2018-03-09 12:13:00 +00001436 if (!have_redund_env) {
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001437 if (!crc0_ok) {
Alex Kiernane9c62202018-03-09 12:12:59 +00001438 fprintf(stderr,
wdenk3f9ab982003-04-12 23:38:12 +00001439 "Warning: Bad CRC, using default environment\n");
Alex Kiernane9c62202018-03-09 12:12:59 +00001440 memcpy(environment.data, default_environment,
1441 sizeof(default_environment));
wdenk34b613e2002-12-17 01:51:00 +00001442 }
wdenke7f34c62003-01-11 09:48:40 +00001443 } else {
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001444 flag0 = *environment.flags;
wdenk3f9ab982003-04-12 23:38:12 +00001445
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001446 dev_current = 1;
Joe Hershberger671adc42012-10-03 09:38:46 +00001447 addr1 = calloc(1, CUR_ENVSIZE);
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001448 if (addr1 == NULL) {
Joe Hershberger671adc42012-10-03 09:38:46 +00001449 fprintf(stderr,
wdenk3f9ab982003-04-12 23:38:12 +00001450 "Not enough memory for environment (%ld bytes)\n",
Joe Hershberger671adc42012-10-03 09:38:46 +00001451 CUR_ENVSIZE);
Stefano Babic4b0a2932017-04-05 18:08:03 +02001452 ret = -ENOMEM;
1453 goto open_cleanup;
wdenk3f9ab982003-04-12 23:38:12 +00001454 }
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001455 redundant = addr1;
wdenk3f9ab982003-04-12 23:38:12 +00001456
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001457 /*
1458 * have to set environment.image for flash_read(), careful -
1459 * other pointers in environment still point inside addr0
1460 */
1461 environment.image = addr1;
Stefano Babic4b0a2932017-04-05 18:08:03 +02001462 if (flash_io(O_RDONLY)) {
Tom Rini4c1192c2018-07-01 22:10:33 -04001463 ret = -EIO;
1464 goto open_cleanup;
Stefano Babic4b0a2932017-04-05 18:08:03 +02001465 }
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001466
1467 /* Check flag scheme compatibility */
1468 if (DEVTYPE(dev_current) == MTD_NORFLASH &&
1469 DEVTYPE(!dev_current) == MTD_NORFLASH) {
1470 environment.flag_scheme = FLAG_BOOLEAN;
1471 } else if (DEVTYPE(dev_current) == MTD_NANDFLASH &&
1472 DEVTYPE(!dev_current) == MTD_NANDFLASH) {
1473 environment.flag_scheme = FLAG_INCREMENTAL;
Remy Bohmercb969eb2011-02-12 19:06:26 +01001474 } else if (DEVTYPE(dev_current) == MTD_DATAFLASH &&
1475 DEVTYPE(!dev_current) == MTD_DATAFLASH) {
1476 environment.flag_scheme = FLAG_BOOLEAN;
Joe Hershbergerdb14e862013-04-08 10:32:52 +00001477 } else if (DEVTYPE(dev_current) == MTD_UBIVOLUME &&
1478 DEVTYPE(!dev_current) == MTD_UBIVOLUME) {
1479 environment.flag_scheme = FLAG_INCREMENTAL;
Oliver Metze745d4c2013-08-30 00:56:01 +02001480 } else if (DEVTYPE(dev_current) == MTD_ABSENT &&
S. Lockwood-Childsa1015c22017-11-14 23:01:26 -08001481 DEVTYPE(!dev_current) == MTD_ABSENT &&
1482 IS_UBI(dev_current) == IS_UBI(!dev_current)) {
Oliver Metze745d4c2013-08-30 00:56:01 +02001483 environment.flag_scheme = FLAG_INCREMENTAL;
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001484 } else {
Alex Kiernane9c62202018-03-09 12:12:59 +00001485 fprintf(stderr, "Incompatible flash types!\n");
Stefano Babic4b0a2932017-04-05 18:08:03 +02001486 ret = -EINVAL;
1487 goto open_cleanup;
wdenk34b613e2002-12-17 01:51:00 +00001488 }
wdenk3f9ab982003-04-12 23:38:12 +00001489
Tom Rini4c1192c2018-07-01 22:10:33 -04001490 crc1 = crc32(0, (uint8_t *)redundant->data, ENV_SIZE);
1491
1492 crc1_ok = (crc1 == redundant->crc);
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001493 flag1 = redundant->flags;
wdenk3f9ab982003-04-12 23:38:12 +00001494
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001495 if (crc0_ok && !crc1_ok) {
1496 dev_current = 0;
1497 } else if (!crc0_ok && crc1_ok) {
1498 dev_current = 1;
1499 } else if (!crc0_ok && !crc1_ok) {
Alex Kiernane9c62202018-03-09 12:12:59 +00001500 fprintf(stderr,
wdenk3f9ab982003-04-12 23:38:12 +00001501 "Warning: Bad CRC, using default environment\n");
Alex Kiernane9c62202018-03-09 12:12:59 +00001502 memcpy(environment.data, default_environment,
1503 sizeof(default_environment));
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001504 dev_current = 0;
1505 } else {
1506 switch (environment.flag_scheme) {
1507 case FLAG_BOOLEAN:
Simon Glass4686d8f2019-08-01 09:47:08 -06001508 if (flag0 == ENV_REDUND_ACTIVE &&
1509 flag1 == ENV_REDUND_OBSOLETE) {
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001510 dev_current = 0;
Simon Glass4686d8f2019-08-01 09:47:08 -06001511 } else if (flag0 == ENV_REDUND_OBSOLETE &&
1512 flag1 == ENV_REDUND_ACTIVE) {
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001513 dev_current = 1;
1514 } else if (flag0 == flag1) {
1515 dev_current = 0;
1516 } else if (flag0 == 0xFF) {
1517 dev_current = 0;
1518 } else if (flag1 == 0xFF) {
1519 dev_current = 1;
1520 } else {
1521 dev_current = 0;
1522 }
1523 break;
1524 case FLAG_INCREMENTAL:
Jon Povey93ccd472011-03-11 14:10:56 +09001525 if (flag0 == 255 && flag1 == 0)
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001526 dev_current = 1;
1527 else if ((flag1 == 255 && flag0 == 0) ||
Jon Povey93ccd472011-03-11 14:10:56 +09001528 flag0 >= flag1)
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001529 dev_current = 0;
Alex Kiernane9c62202018-03-09 12:12:59 +00001530 else /* flag1 > flag0 */
Jon Povey93ccd472011-03-11 14:10:56 +09001531 dev_current = 1;
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001532 break;
1533 default:
Alex Kiernane9c62202018-03-09 12:12:59 +00001534 fprintf(stderr, "Unknown flag scheme %u\n",
1535 environment.flag_scheme);
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001536 return -1;
1537 }
1538 }
1539
1540 /*
1541 * If we are reading, we don't need the flag and the CRC any
1542 * more, if we are writing, we will re-calculate CRC and update
1543 * flags before writing out
1544 */
1545 if (dev_current) {
Alex Kiernane9c62202018-03-09 12:12:59 +00001546 environment.image = addr1;
1547 environment.crc = &redundant->crc;
1548 environment.flags = &redundant->flags;
1549 environment.data = redundant->data;
1550 free(addr0);
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001551 } else {
Alex Kiernane9c62202018-03-09 12:12:59 +00001552 environment.image = addr0;
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001553 /* Other pointers are already set */
Alex Kiernane9c62202018-03-09 12:12:59 +00001554 free(addr1);
wdenk34b613e2002-12-17 01:51:00 +00001555 }
Joe Hershbergera8dd2c62012-10-15 15:29:25 +00001556#ifdef DEBUG
1557 fprintf(stderr, "Selected env in %s\n", DEVNAME(dev_current));
1558#endif
wdenk34b613e2002-12-17 01:51:00 +00001559 }
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001560 return 0;
Stefano Babic4b0a2932017-04-05 18:08:03 +02001561
Alex Kiernane9c62202018-03-09 12:12:59 +00001562 open_cleanup:
Stefano Babic4b0a2932017-04-05 18:08:03 +02001563 if (addr0)
1564 free(addr0);
1565
1566 if (addr1)
Björn Stenberg4d1734d2019-04-17 16:56:54 +02001567 free(addr1);
Stefano Babic4b0a2932017-04-05 18:08:03 +02001568
1569 return ret;
1570}
1571
1572/*
1573 * Simply free allocated buffer with environment
1574 */
1575int fw_env_close(struct env_opts *opts)
1576{
1577 if (environment.image)
1578 free(environment.image);
1579
1580 environment.image = NULL;
1581
1582 return 0;
wdenk34b613e2002-12-17 01:51:00 +00001583}
1584
Stefan Agner21dbd6a2016-07-13 17:14:37 -07001585static int check_device_config(int dev)
1586{
1587 struct stat st;
S. Lockwood-Childsa1015c22017-11-14 23:01:26 -08001588 int32_t lnum = 0;
Stefan Agner21dbd6a2016-07-13 17:14:37 -07001589 int fd, rc = 0;
1590
S. Lockwood-Childsa1015c22017-11-14 23:01:26 -08001591 /* Fills in IS_UBI(), converts DEVNAME() with ubi volume name */
1592 ubi_check_dev(dev);
1593
Stefan Agner21dbd6a2016-07-13 17:14:37 -07001594 fd = open(DEVNAME(dev), O_RDONLY);
1595 if (fd < 0) {
1596 fprintf(stderr,
Alex Kiernane9c62202018-03-09 12:12:59 +00001597 "Cannot open %s: %s\n", DEVNAME(dev), strerror(errno));
Stefan Agner21dbd6a2016-07-13 17:14:37 -07001598 return -1;
1599 }
1600
1601 rc = fstat(fd, &st);
1602 if (rc < 0) {
Alex Kiernane9c62202018-03-09 12:12:59 +00001603 fprintf(stderr, "Cannot stat the file %s\n", DEVNAME(dev));
Stefan Agner21dbd6a2016-07-13 17:14:37 -07001604 goto err;
1605 }
1606
S. Lockwood-Childsa1015c22017-11-14 23:01:26 -08001607 if (IS_UBI(dev)) {
1608 rc = ioctl(fd, UBI_IOCEBISMAP, &lnum);
1609 if (rc < 0) {
1610 fprintf(stderr, "Cannot get UBI information for %s\n",
1611 DEVNAME(dev));
1612 goto err;
1613 }
1614 } else if (S_ISCHR(st.st_mode)) {
Stefan Agner21dbd6a2016-07-13 17:14:37 -07001615 struct mtd_info_user mtdinfo;
1616 rc = ioctl(fd, MEMGETINFO, &mtdinfo);
1617 if (rc < 0) {
1618 fprintf(stderr, "Cannot get MTD information for %s\n",
1619 DEVNAME(dev));
1620 goto err;
1621 }
1622 if (mtdinfo.type != MTD_NORFLASH &&
1623 mtdinfo.type != MTD_NANDFLASH &&
1624 mtdinfo.type != MTD_DATAFLASH &&
1625 mtdinfo.type != MTD_UBIVOLUME) {
1626 fprintf(stderr, "Unsupported flash type %u on %s\n",
1627 mtdinfo.type, DEVNAME(dev));
1628 goto err;
1629 }
1630 DEVTYPE(dev) = mtdinfo.type;
Max Krummenacher6c44f542016-11-19 13:58:56 +01001631 if (DEVESIZE(dev) == 0)
1632 /* Assume the erase size is the same as the env-size */
1633 DEVESIZE(dev) = ENVSIZE(dev);
Stefan Agner21dbd6a2016-07-13 17:14:37 -07001634 } else {
Stefan Agnerb21e6022016-07-13 17:14:38 -07001635 uint64_t size;
Stefan Agner21dbd6a2016-07-13 17:14:37 -07001636 DEVTYPE(dev) = MTD_ABSENT;
Max Krummenacher6c44f542016-11-19 13:58:56 +01001637 if (DEVESIZE(dev) == 0)
1638 /* Assume the erase size to be 512 bytes */
1639 DEVESIZE(dev) = 0x200;
Stefan Agnerb21e6022016-07-13 17:14:38 -07001640
1641 /*
1642 * Check for negative offsets, treat it as backwards offset
1643 * from the end of the block device
1644 */
1645 if (DEVOFFSET(dev) < 0) {
1646 rc = ioctl(fd, BLKGETSIZE64, &size);
1647 if (rc < 0) {
Alex Kiernane9c62202018-03-09 12:12:59 +00001648 fprintf(stderr,
1649 "Could not get block device size on %s\n",
Stefan Agnerb21e6022016-07-13 17:14:38 -07001650 DEVNAME(dev));
1651 goto err;
1652 }
1653
1654 DEVOFFSET(dev) = DEVOFFSET(dev) + size;
1655#ifdef DEBUG
Alex Kiernane9c62202018-03-09 12:12:59 +00001656 fprintf(stderr,
1657 "Calculated device offset 0x%llx on %s\n",
Stefan Agnerb21e6022016-07-13 17:14:38 -07001658 DEVOFFSET(dev), DEVNAME(dev));
1659#endif
1660 }
Stefan Agner21dbd6a2016-07-13 17:14:37 -07001661 }
1662
Max Krummenacher6c44f542016-11-19 13:58:56 +01001663 if (ENVSECTORS(dev) == 0)
1664 /* Assume enough sectors to cover the environment */
1665 ENVSECTORS(dev) = DIV_ROUND_UP(ENVSIZE(dev), DEVESIZE(dev));
1666
1667 if (DEVOFFSET(dev) % DEVESIZE(dev) != 0) {
Alex Kiernane9c62202018-03-09 12:12:59 +00001668 fprintf(stderr,
1669 "Environment does not start on (erase) block boundary\n");
Max Krummenacher6c44f542016-11-19 13:58:56 +01001670 errno = EINVAL;
1671 return -1;
1672 }
1673
1674 if (ENVSIZE(dev) > ENVSECTORS(dev) * DEVESIZE(dev)) {
Alex Kiernane9c62202018-03-09 12:12:59 +00001675 fprintf(stderr,
1676 "Environment does not fit into available sectors\n");
Max Krummenacher6c44f542016-11-19 13:58:56 +01001677 errno = EINVAL;
1678 return -1;
1679 }
1680
Alex Kiernane9c62202018-03-09 12:12:59 +00001681 err:
Stefan Agner21dbd6a2016-07-13 17:14:37 -07001682 close(fd);
1683 return rc;
1684}
wdenk34b613e2002-12-17 01:51:00 +00001685
Andreas Fenkart24371902016-04-05 23:13:42 +02001686static int parse_config(struct env_opts *opts)
wdenk34b613e2002-12-17 01:51:00 +00001687{
Stefan Agner21dbd6a2016-07-13 17:14:37 -07001688 int rc;
wdenk34b613e2002-12-17 01:51:00 +00001689
Andreas Fenkarte6511302016-05-31 09:21:56 +02001690 if (!opts)
1691 opts = &default_opts;
Anatolij Gustschine89ead32016-04-29 22:00:11 +02001692
Andreas Fenkarte6511302016-05-31 09:21:56 +02001693#if defined(CONFIG_FILE)
wdenke7f34c62003-01-11 09:48:40 +00001694 /* Fills in DEVNAME(), ENVSIZE(), DEVESIZE(). Or don't. */
Andreas Fenkart24371902016-04-05 23:13:42 +02001695 if (get_config(opts->config_file)) {
Andreas Fenkartaa01f8d2015-12-09 13:13:24 +01001696 fprintf(stderr, "Cannot parse config file '%s': %m\n",
Andreas Fenkart24371902016-04-05 23:13:42 +02001697 opts->config_file);
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001698 return -1;
wdenk34b613e2002-12-17 01:51:00 +00001699 }
wdenke7f34c62003-01-11 09:48:40 +00001700#else
Alex Kiernane9c62202018-03-09 12:12:59 +00001701 DEVNAME(0) = DEVICE1_NAME;
1702 DEVOFFSET(0) = DEVICE1_OFFSET;
1703 ENVSIZE(0) = ENV1_SIZE;
Max Krummenacher6c44f542016-11-19 13:58:56 +01001704
1705 /* Set defaults for DEVESIZE, ENVSECTORS later once we
1706 * know DEVTYPE
1707 */
Remy Bohmercb969eb2011-02-12 19:06:26 +01001708#ifdef DEVICE1_ESIZE
Alex Kiernane9c62202018-03-09 12:12:59 +00001709 DEVESIZE(0) = DEVICE1_ESIZE;
Remy Bohmercb969eb2011-02-12 19:06:26 +01001710#endif
1711#ifdef DEVICE1_ENVSECTORS
Alex Kiernane9c62202018-03-09 12:12:59 +00001712 ENVSECTORS(0) = DEVICE1_ENVSECTORS;
Remy Bohmercb969eb2011-02-12 19:06:26 +01001713#endif
1714
wdenk34b613e2002-12-17 01:51:00 +00001715#ifdef HAVE_REDUND
Alex Kiernane9c62202018-03-09 12:12:59 +00001716 DEVNAME(1) = DEVICE2_NAME;
1717 DEVOFFSET(1) = DEVICE2_OFFSET;
1718 ENVSIZE(1) = ENV2_SIZE;
Max Krummenacher6c44f542016-11-19 13:58:56 +01001719
1720 /* Set defaults for DEVESIZE, ENVSECTORS later once we
1721 * know DEVTYPE
1722 */
Remy Bohmercb969eb2011-02-12 19:06:26 +01001723#ifdef DEVICE2_ESIZE
Alex Kiernane9c62202018-03-09 12:12:59 +00001724 DEVESIZE(1) = DEVICE2_ESIZE;
Remy Bohmercb969eb2011-02-12 19:06:26 +01001725#endif
1726#ifdef DEVICE2_ENVSECTORS
Alex Kiernane9c62202018-03-09 12:12:59 +00001727 ENVSECTORS(1) = DEVICE2_ENVSECTORS;
Remy Bohmercb969eb2011-02-12 19:06:26 +01001728#endif
Alex Kiernanf46d0ca2018-03-09 12:13:00 +00001729 have_redund_env = 1;
wdenk34b613e2002-12-17 01:51:00 +00001730#endif
wdenke7f34c62003-01-11 09:48:40 +00001731#endif
Stefan Agner21dbd6a2016-07-13 17:14:37 -07001732 rc = check_device_config(0);
1733 if (rc < 0)
1734 return rc;
wdenk3f9ab982003-04-12 23:38:12 +00001735
Alex Kiernanf46d0ca2018-03-09 12:13:00 +00001736 if (have_redund_env) {
Stefan Agner21dbd6a2016-07-13 17:14:37 -07001737 rc = check_device_config(1);
1738 if (rc < 0)
1739 return rc;
Andreas Fenkartb8bf4042016-04-19 22:43:42 +02001740
Stefan Agner21dbd6a2016-07-13 17:14:37 -07001741 if (ENVSIZE(0) != ENVSIZE(1)) {
Stefan Agner21dbd6a2016-07-13 17:14:37 -07001742 fprintf(stderr,
Philip Molloyf60f7512019-03-31 03:44:57 +00001743 "Redundant environments have unequal size\n");
Andreas Fenkart31f2e052016-08-17 23:41:53 +02001744 return -1;
Stefan Agner21dbd6a2016-07-13 17:14:37 -07001745 }
Andreas Fenkartb8bf4042016-04-19 22:43:42 +02001746 }
1747
1748 usable_envsize = CUR_ENVSIZE - sizeof(uint32_t);
Alex Kiernanf46d0ca2018-03-09 12:13:00 +00001749 if (have_redund_env)
Andreas Fenkartb8bf4042016-04-19 22:43:42 +02001750 usable_envsize -= sizeof(char);
1751
wdenk34b613e2002-12-17 01:51:00 +00001752 return 0;
1753}
wdenke7f34c62003-01-11 09:48:40 +00001754
1755#if defined(CONFIG_FILE)
Alex Kiernane9c62202018-03-09 12:12:59 +00001756static int get_config(char *fname)
wdenke7f34c62003-01-11 09:48:40 +00001757{
1758 FILE *fp;
1759 int i = 0;
1760 int rc;
Alex Kiernan66df4e22018-06-07 12:20:05 +00001761 char *line = NULL;
1762 size_t linesize = 0;
Tom Rini313e9122014-03-28 12:03:33 -04001763 char *devname;
wdenke7f34c62003-01-11 09:48:40 +00001764
Alex Kiernane9c62202018-03-09 12:12:59 +00001765 fp = fopen(fname, "r");
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001766 if (fp == NULL)
1767 return -1;
wdenke7f34c62003-01-11 09:48:40 +00001768
Alex Kiernan66df4e22018-06-07 12:20:05 +00001769 while (i < 2 && getline(&line, &linesize, fp) != -1) {
1770 /* Skip comment strings */
1771 if (line[0] == '#')
wdenke7f34c62003-01-11 09:48:40 +00001772 continue;
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001773
Alex Kiernan66df4e22018-06-07 12:20:05 +00001774 rc = sscanf(line, "%ms %lli %lx %lx %lx",
Stefan Agnerb21e6022016-07-13 17:14:38 -07001775 &devname,
1776 &DEVOFFSET(i),
Alex Kiernane9c62202018-03-09 12:12:59 +00001777 &ENVSIZE(i), &DEVESIZE(i), &ENVSECTORS(i));
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001778
Remy Bohmercb969eb2011-02-12 19:06:26 +01001779 if (rc < 3)
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001780 continue;
1781
Tom Rini313e9122014-03-28 12:03:33 -04001782 DEVNAME(i) = devname;
1783
Max Krummenacher6c44f542016-11-19 13:58:56 +01001784 /* Set defaults for DEVESIZE, ENVSECTORS later once we
1785 * know DEVTYPE
1786 */
wdenke7f34c62003-01-11 09:48:40 +00001787
1788 i++;
1789 }
Alex Kiernan66df4e22018-06-07 12:20:05 +00001790 free(line);
Alex Kiernane9c62202018-03-09 12:12:59 +00001791 fclose(fp);
wdenk3f9ab982003-04-12 23:38:12 +00001792
Alex Kiernanf46d0ca2018-03-09 12:13:00 +00001793 have_redund_env = i - 1;
Alex Kiernane9c62202018-03-09 12:12:59 +00001794 if (!i) { /* No valid entries found */
wdenke7f34c62003-01-11 09:48:40 +00001795 errno = EINVAL;
Guennadi Liakhovetskiffb379a2008-09-04 13:01:49 +02001796 return -1;
wdenke7f34c62003-01-11 09:48:40 +00001797 } else
1798 return 0;
1799}
1800#endif