blob: 7a9c96b4078ff2fcb088eedb2a0fba13f3d23195 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Simon Glass15e5a1a2017-08-03 12:22:00 -06002/*
3 * Copyright (C) 2017 Google, Inc
4 * Written by Simon Glass <sjg@chromium.org>
Simon Glass15e5a1a2017-08-03 12:22:00 -06005 */
6
Simon Glass79fd2142019-08-01 09:46:43 -06007#include <env.h>
Simon Glass9d1f6192019-08-02 09:44:25 -06008#include <env_internal.h>
Simon Glass0f2af882020-05-10 11:40:05 -06009#include <log.h>
Simon Glass3ba929a2020-10-30 21:38:53 -060010#include <asm/global_data.h>
Simon Glass4dcacfc2020-05-10 11:40:13 -060011#include <linux/bitops.h>
Simon Glassc06c1be2020-05-10 11:40:08 -060012#include <linux/bug.h>
Tom Rinidec7ea02024-05-20 13:35:03 -060013#include <linux/errno.h>
Simon Glass15e5a1a2017-08-03 12:22:00 -060014
15DECLARE_GLOBAL_DATA_PTR;
16
Maxime Ripardb7da89a2018-01-23 21:16:51 +010017static struct env_driver *_env_driver_lookup(enum env_location loc)
Simon Glass15e5a1a2017-08-03 12:22:00 -060018{
19 struct env_driver *drv;
20 const int n_ents = ll_entry_count(struct env_driver, env_driver);
21 struct env_driver *entry;
22
23 drv = ll_entry_start(struct env_driver, env_driver);
24 for (entry = drv; entry != drv + n_ents; entry++) {
25 if (loc == entry->location)
26 return entry;
27 }
28
29 /* Not found */
30 return NULL;
31}
32
Maxime Ripard520cf802018-01-23 21:16:58 +010033static enum env_location env_locations[] = {
34#ifdef CONFIG_ENV_IS_IN_EEPROM
35 ENVL_EEPROM,
36#endif
37#ifdef CONFIG_ENV_IS_IN_EXT4
38 ENVL_EXT4,
39#endif
40#ifdef CONFIG_ENV_IS_IN_FAT
41 ENVL_FAT,
42#endif
43#ifdef CONFIG_ENV_IS_IN_FLASH
44 ENVL_FLASH,
45#endif
46#ifdef CONFIG_ENV_IS_IN_MMC
47 ENVL_MMC,
48#endif
Varadarajan Narayanan9dc5ad82025-05-13 14:47:08 +053049#ifdef CONFIG_ENV_IS_IN_SCSI
50 ENVL_SCSI,
51#endif
Maxime Ripard520cf802018-01-23 21:16:58 +010052#ifdef CONFIG_ENV_IS_IN_NAND
53 ENVL_NAND,
54#endif
55#ifdef CONFIG_ENV_IS_IN_NVRAM
56 ENVL_NVRAM,
57#endif
58#ifdef CONFIG_ENV_IS_IN_REMOTE
59 ENVL_REMOTE,
60#endif
61#ifdef CONFIG_ENV_IS_IN_SPI_FLASH
62 ENVL_SPI_FLASH,
63#endif
Christian Marangif13466e2025-04-07 20:59:51 +020064#ifdef CONFIG_ENV_IS_IN_MTD
65 ENVL_MTD,
66#endif
Maxime Ripard520cf802018-01-23 21:16:58 +010067#ifdef CONFIG_ENV_IS_IN_UBI
68 ENVL_UBI,
69#endif
70#ifdef CONFIG_ENV_IS_NOWHERE
71 ENVL_NOWHERE,
72#endif
73};
74
Maxime Ripard2131c5e2018-01-23 21:16:59 +010075static bool env_has_inited(enum env_location location)
76{
77 return gd->env_has_init & BIT(location);
78}
79
80static void env_set_inited(enum env_location location)
81{
82 /*
83 * We're using a 32-bits bitmask stored in gd (env_has_init)
84 * using the above enum value as the bit index. We need to
85 * make sure that we're not overflowing it.
86 */
Patrick Delaunay13ac85a2020-06-24 09:27:25 +020087 BUILD_BUG_ON(ENVL_COUNT > BITS_PER_LONG);
Maxime Ripard2131c5e2018-01-23 21:16:59 +010088
89 gd->env_has_init |= BIT(location);
90}
91
Maxime Ripardfd3158e2018-01-23 21:16:52 +010092/**
Marek Vasutf92cf582022-04-06 02:21:32 +020093 * arch_env_get_location() - Returns the best env location for an arch
Maxime Ripardfd3158e2018-01-23 21:16:52 +010094 * @op: operations performed on the environment
95 * @prio: priority between the multiple environments, 0 being the
96 * highest priority
97 *
98 * This will return the preferred environment for the given priority.
Marek Vasutf92cf582022-04-06 02:21:32 +020099 * This is overridable by architectures if they need to and has lower
100 * priority than board side env_get_location() override.
Maxime Ripardfd3158e2018-01-23 21:16:52 +0100101 *
102 * All implementations are free to use the operation, the priority and
103 * any other data relevant to their choice, but must take into account
104 * the fact that the lowest prority (0) is the most important location
105 * in the system. The following locations should be returned by order
106 * of descending priorities, from the highest to the lowest priority.
107 *
108 * Returns:
109 * an enum env_location value on success, a negative error code otherwise
110 */
Marek Vasutf92cf582022-04-06 02:21:32 +0200111__weak enum env_location arch_env_get_location(enum env_operation op, int prio)
Simon Glass15e5a1a2017-08-03 12:22:00 -0600112{
Nicholas Faustinia8a17f82018-07-23 10:01:07 +0200113 if (prio >= ARRAY_SIZE(env_locations))
114 return ENVL_UNKNOWN;
Maxime Ripard520cf802018-01-23 21:16:58 +0100115
Nicholas Faustinia8a17f82018-07-23 10:01:07 +0200116 return env_locations[prio];
Simon Glass15e5a1a2017-08-03 12:22:00 -0600117}
118
Marek Vasutf92cf582022-04-06 02:21:32 +0200119/**
120 * env_get_location() - Returns the best env location for a board
121 * @op: operations performed on the environment
122 * @prio: priority between the multiple environments, 0 being the
123 * highest priority
124 *
125 * This will return the preferred environment for the given priority.
126 * This is overridable by boards if they need to.
127 *
128 * All implementations are free to use the operation, the priority and
129 * any other data relevant to their choice, but must take into account
130 * the fact that the lowest prority (0) is the most important location
131 * in the system. The following locations should be returned by order
132 * of descending priorities, from the highest to the lowest priority.
133 *
134 * Returns:
135 * an enum env_location value on success, a negative error code otherwise
136 */
137__weak enum env_location env_get_location(enum env_operation op, int prio)
138{
139 return arch_env_get_location(op, prio);
140}
Maxime Ripardfd3158e2018-01-23 21:16:52 +0100141
142/**
143 * env_driver_lookup() - Finds the most suited environment location
144 * @op: operations performed on the environment
145 * @prio: priority between the multiple environments, 0 being the
146 * highest priority
147 *
148 * This will try to find the available environment with the highest
149 * priority in the system.
150 *
151 * Returns:
152 * NULL on error, a pointer to a struct env_driver otherwise
153 */
154static struct env_driver *env_driver_lookup(enum env_operation op, int prio)
Simon Glass15e5a1a2017-08-03 12:22:00 -0600155{
Maxime Ripardfd3158e2018-01-23 21:16:52 +0100156 enum env_location loc = env_get_location(op, prio);
Simon Glass15e5a1a2017-08-03 12:22:00 -0600157 struct env_driver *drv;
158
Maxime Ripardfd3158e2018-01-23 21:16:52 +0100159 if (loc == ENVL_UNKNOWN)
160 return NULL;
161
Maxime Ripardb7da89a2018-01-23 21:16:51 +0100162 drv = _env_driver_lookup(loc);
Simon Glass15e5a1a2017-08-03 12:22:00 -0600163 if (!drv) {
164 debug("%s: No environment driver for location %d\n", __func__,
165 loc);
166 return NULL;
167 }
168
169 return drv;
170}
171
Simon Glass15e5a1a2017-08-03 12:22:00 -0600172int env_load(void)
173{
Maxime Ripardfd3158e2018-01-23 21:16:52 +0100174 struct env_driver *drv;
Sam Protsenko239f3122019-01-18 21:19:04 +0200175 int best_prio = -1;
Maxime Ripardfd3158e2018-01-23 21:16:52 +0100176 int prio;
Simon Glass15e5a1a2017-08-03 12:22:00 -0600177
Jan Kiszka755703b2023-02-03 13:22:51 +0100178 if (CONFIG_IS_ENABLED(ENV_WRITEABLE_LIST)) {
179 /*
180 * When using a list of writeable variables, the baseline comes
181 * from the built-in default env. So load this first.
182 */
183 env_set_default(NULL, 0);
184 }
185
Maxime Ripardfd3158e2018-01-23 21:16:52 +0100186 for (prio = 0; (drv = env_driver_lookup(ENVOP_LOAD, prio)); prio++) {
187 int ret;
188
Maxime Ripard2131c5e2018-01-23 21:16:59 +0100189 if (!env_has_inited(drv->location))
190 continue;
191
Maxime Ripard185402a2018-01-23 21:16:54 +0100192 printf("Loading Environment from %s... ", drv->name);
Sam Protsenko051dfd82018-07-30 19:19:26 +0300193 /*
194 * In error case, the error message must be printed during
195 * drv->load() in some underlying API, and it must be exactly
196 * one message.
197 */
Maxime Ripardfd3158e2018-01-23 21:16:52 +0100198 ret = drv->load();
Sam Protsenko239f3122019-01-18 21:19:04 +0200199 if (!ret) {
Maxime Ripard185402a2018-01-23 21:16:54 +0100200 printf("OK\n");
Patrick Delaunaya639f4e2020-07-28 11:51:17 +0200201 gd->env_load_prio = prio;
202
Maxime Ripardfd3158e2018-01-23 21:16:52 +0100203 return 0;
Sam Protsenko239f3122019-01-18 21:19:04 +0200204 } else if (ret == -ENOMSG) {
205 /* Handle "bad CRC" case */
206 if (best_prio == -1)
207 best_prio = prio;
208 } else {
209 debug("Failed (%d)\n", ret);
Sam Protsenko051dfd82018-07-30 19:19:26 +0300210 }
Simon Glass15e5a1a2017-08-03 12:22:00 -0600211 }
212
Nicholas Faustinia8a17f82018-07-23 10:01:07 +0200213 /*
214 * In case of invalid environment, we set the 'default' env location
Sam Protsenko239f3122019-01-18 21:19:04 +0200215 * to the best choice, i.e.:
216 * 1. Environment location with bad CRC, if such location was found
217 * 2. Otherwise use the location with highest priority
218 *
219 * This way, next calls to env_save() will restore the environment
220 * at the right place.
Nicholas Faustinia8a17f82018-07-23 10:01:07 +0200221 */
Sam Protsenko239f3122019-01-18 21:19:04 +0200222 if (best_prio >= 0)
223 debug("Selecting environment with bad CRC\n");
224 else
225 best_prio = 0;
Patrick Delaunaya639f4e2020-07-28 11:51:17 +0200226
227 gd->env_load_prio = best_prio;
Nicholas Faustinia8a17f82018-07-23 10:01:07 +0200228
Maxime Ripardfd3158e2018-01-23 21:16:52 +0100229 return -ENODEV;
Simon Glass15e5a1a2017-08-03 12:22:00 -0600230}
231
Patrick Delaunay748e42e2020-07-28 11:51:20 +0200232int env_reload(void)
233{
234 struct env_driver *drv;
235
236 drv = env_driver_lookup(ENVOP_LOAD, gd->env_load_prio);
237 if (drv) {
238 int ret;
239
240 printf("Loading Environment from %s... ", drv->name);
241
242 if (!env_has_inited(drv->location)) {
243 printf("not initialized\n");
244 return -ENODEV;
245 }
246
247 ret = drv->load();
248 if (ret)
249 printf("Failed (%d)\n", ret);
250 else
251 printf("OK\n");
252
253 if (!ret)
254 return 0;
255 }
256
257 return -ENODEV;
258}
259
Simon Glass15e5a1a2017-08-03 12:22:00 -0600260int env_save(void)
261{
Maxime Ripardfd3158e2018-01-23 21:16:52 +0100262 struct env_driver *drv;
Simon Glass15e5a1a2017-08-03 12:22:00 -0600263
Nicholas Faustinia8a17f82018-07-23 10:01:07 +0200264 drv = env_driver_lookup(ENVOP_SAVE, gd->env_load_prio);
265 if (drv) {
Maxime Ripardfd3158e2018-01-23 21:16:52 +0100266 int ret;
Maxime Ripard3c0b2e52018-01-23 21:16:50 +0100267
Patrick Delaunay65bbec22020-06-24 10:17:50 +0200268 printf("Saving Environment to %s... ", drv->name);
269 if (!drv->save) {
270 printf("not possible\n");
Nicholas Faustinia8a17f82018-07-23 10:01:07 +0200271 return -ENODEV;
Patrick Delaunay65bbec22020-06-24 10:17:50 +0200272 }
Maxime Ripardfd3158e2018-01-23 21:16:52 +0100273
Patrick Delaunay65bbec22020-06-24 10:17:50 +0200274 if (!env_has_inited(drv->location)) {
275 printf("not initialized\n");
Nicholas Faustinia8a17f82018-07-23 10:01:07 +0200276 return -ENODEV;
Patrick Delaunay65bbec22020-06-24 10:17:50 +0200277 }
Maxime Ripard2131c5e2018-01-23 21:16:59 +0100278
Maxime Ripardfd3158e2018-01-23 21:16:52 +0100279 ret = drv->save();
Maxime Ripard185402a2018-01-23 21:16:54 +0100280 if (ret)
281 printf("Failed (%d)\n", ret);
282 else
283 printf("OK\n");
284
Maxime Ripardfd3158e2018-01-23 21:16:52 +0100285 if (!ret)
286 return 0;
Simon Glass15e5a1a2017-08-03 12:22:00 -0600287 }
288
Maxime Ripardfd3158e2018-01-23 21:16:52 +0100289 return -ENODEV;
Simon Glass15e5a1a2017-08-03 12:22:00 -0600290}
291
Frank Wunderlich33afa932019-06-29 11:36:19 +0200292int env_erase(void)
293{
294 struct env_driver *drv;
295
296 drv = env_driver_lookup(ENVOP_ERASE, gd->env_load_prio);
297 if (drv) {
298 int ret;
299
Patrick Delaunay6d6a1d02022-12-14 16:51:32 +0100300 if (!drv->erase) {
301 printf("not possible\n");
Frank Wunderlich33afa932019-06-29 11:36:19 +0200302 return -ENODEV;
Patrick Delaunay6d6a1d02022-12-14 16:51:32 +0100303 }
Frank Wunderlich33afa932019-06-29 11:36:19 +0200304
Patrick Delaunay6d6a1d02022-12-14 16:51:32 +0100305 if (!env_has_inited(drv->location)) {
306 printf("not initialized\n");
Frank Wunderlich33afa932019-06-29 11:36:19 +0200307 return -ENODEV;
Patrick Delaunay6d6a1d02022-12-14 16:51:32 +0100308 }
Frank Wunderlich33afa932019-06-29 11:36:19 +0200309
310 printf("Erasing Environment on %s... ", drv->name);
311 ret = drv->erase();
312 if (ret)
313 printf("Failed (%d)\n", ret);
314 else
315 printf("OK\n");
316
317 if (!ret)
318 return 0;
319 }
320
321 return -ENODEV;
322}
323
Simon Glass36d85812017-08-03 12:22:05 -0600324int env_init(void)
Simon Glass15e5a1a2017-08-03 12:22:00 -0600325{
Maxime Ripardfd3158e2018-01-23 21:16:52 +0100326 struct env_driver *drv;
Simon Glassd40d8042017-08-03 12:22:02 -0600327 int ret = -ENOENT;
Maxime Ripardfd3158e2018-01-23 21:16:52 +0100328 int prio;
Simon Glass15e5a1a2017-08-03 12:22:00 -0600329
Maxime Ripardfd3158e2018-01-23 21:16:52 +0100330 for (prio = 0; (drv = env_driver_lookup(ENVOP_INIT, prio)); prio++) {
Maxime Ripard2131c5e2018-01-23 21:16:59 +0100331 if (!drv->init || !(ret = drv->init()))
332 env_set_inited(drv->location);
Heiko Schocher8ba7b612020-11-03 15:22:36 +0100333 if (ret == -ENOENT)
334 env_set_inited(drv->location);
Maxime Ripardfd3158e2018-01-23 21:16:52 +0100335
Maxime Ripard2131c5e2018-01-23 21:16:59 +0100336 debug("%s: Environment %s init done (ret=%d)\n", __func__,
Maxime Ripardfd3158e2018-01-23 21:16:52 +0100337 drv->name, ret);
Felix.Vietmeyer@jila.colorado.edud0f91062021-04-20 20:04:26 -0600338
Marek Vasut8e25d0c2022-04-10 06:46:52 +0200339 if (gd->env_valid == ENV_INVALID)
340 ret = -ENOENT;
Felix.Vietmeyer@jila.colorado.edud0f91062021-04-20 20:04:26 -0600341 }
Maxime Ripardfd3158e2018-01-23 21:16:52 +0100342
Marek Vasut8e25d0c2022-04-10 06:46:52 +0200343 if (!prio)
344 return -ENODEV;
345
Simon Glassd40d8042017-08-03 12:22:02 -0600346 if (ret == -ENOENT) {
347 gd->env_addr = (ulong)&default_environment[0];
Marek Vasut8e25d0c2022-04-10 06:46:52 +0200348 gd->env_valid = ENV_VALID;
Simon Glassd40d8042017-08-03 12:22:02 -0600349
350 return 0;
Simon Glass15e5a1a2017-08-03 12:22:00 -0600351 }
352
Maxime Ripardfd3158e2018-01-23 21:16:52 +0100353 return ret;
Simon Glass15e5a1a2017-08-03 12:22:00 -0600354}
Patrick Delaunaya59f7ec2020-07-28 11:51:21 +0200355
356int env_select(const char *name)
357{
358 struct env_driver *drv;
359 const int n_ents = ll_entry_count(struct env_driver, env_driver);
360 struct env_driver *entry;
361 int prio;
362 bool found = false;
363
364 printf("Select Environment on %s: ", name);
365
366 /* search ENV driver by name */
367 drv = ll_entry_start(struct env_driver, env_driver);
368 for (entry = drv; entry != drv + n_ents; entry++) {
369 if (!strcmp(entry->name, name)) {
370 found = true;
371 break;
372 }
373 }
374
375 if (!found) {
376 printf("driver not found\n");
377 return -ENODEV;
378 }
379
380 /* search priority by driver */
381 for (prio = 0; (drv = env_driver_lookup(ENVOP_INIT, prio)); prio++) {
382 if (entry->location == env_get_location(ENVOP_LOAD, prio)) {
383 /* when priority change, reset the ENV flags */
384 if (gd->env_load_prio != prio) {
385 gd->env_load_prio = prio;
386 gd->env_valid = ENV_INVALID;
387 gd->flags &= ~GD_FLG_ENV_DEFAULT;
388 }
389 printf("OK\n");
390 return 0;
391 }
392 }
393 printf("priority not found\n");
394
395 return -ENODEV;
396}