blob: d3cbe2f9158429c95c095c9bed6564d9285702ef [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Simon Glassc9d728d2017-08-03 12:22:00 -06002/*
3 * Copyright (C) 2017 Google, Inc
4 * Written by Simon Glass <sjg@chromium.org>
Simon Glassc9d728d2017-08-03 12:22:00 -06005 */
6
7#include <common.h>
8#include <environment.h>
9
10DECLARE_GLOBAL_DATA_PTR;
11
Siva Durga Prasad Paladugu7bcdf192018-04-13 07:57:21 +020012#if defined(CONFIG_NEEDS_MANUAL_RELOC)
13void env_fix_drivers(void)
14{
15 struct env_driver *drv;
16 const int n_ents = ll_entry_count(struct env_driver, env_driver);
17 struct env_driver *entry;
18
19 drv = ll_entry_start(struct env_driver, env_driver);
20 for (entry = drv; entry != drv + n_ents; entry++) {
21 if (entry->name)
22 entry->name += gd->reloc_off;
23 if (entry->load)
24 entry->load += gd->reloc_off;
25 if (entry->save)
26 entry->save += gd->reloc_off;
Frank Wunderlichcd121bd2019-06-29 11:36:19 +020027 if (entry->erase)
28 entry->erase += gd->reloc_off;
Siva Durga Prasad Paladugu7bcdf192018-04-13 07:57:21 +020029 if (entry->init)
30 entry->init += gd->reloc_off;
31 }
32}
33#endif
34
Maxime Ripard52746c42018-01-23 21:16:51 +010035static struct env_driver *_env_driver_lookup(enum env_location loc)
Simon Glassc9d728d2017-08-03 12:22:00 -060036{
37 struct env_driver *drv;
38 const int n_ents = ll_entry_count(struct env_driver, env_driver);
39 struct env_driver *entry;
40
41 drv = ll_entry_start(struct env_driver, env_driver);
42 for (entry = drv; entry != drv + n_ents; entry++) {
43 if (loc == entry->location)
44 return entry;
45 }
46
47 /* Not found */
48 return NULL;
49}
50
Maxime Ripard7d714a22018-01-23 21:16:58 +010051static enum env_location env_locations[] = {
52#ifdef CONFIG_ENV_IS_IN_EEPROM
53 ENVL_EEPROM,
54#endif
55#ifdef CONFIG_ENV_IS_IN_EXT4
56 ENVL_EXT4,
57#endif
58#ifdef CONFIG_ENV_IS_IN_FAT
59 ENVL_FAT,
60#endif
61#ifdef CONFIG_ENV_IS_IN_FLASH
62 ENVL_FLASH,
63#endif
64#ifdef CONFIG_ENV_IS_IN_MMC
65 ENVL_MMC,
66#endif
67#ifdef CONFIG_ENV_IS_IN_NAND
68 ENVL_NAND,
69#endif
70#ifdef CONFIG_ENV_IS_IN_NVRAM
71 ENVL_NVRAM,
72#endif
73#ifdef CONFIG_ENV_IS_IN_REMOTE
74 ENVL_REMOTE,
75#endif
Ye Li9a6a3112019-01-04 09:34:24 +000076#ifdef CONFIG_ENV_IS_IN_SATA
77 ENVL_ESATA,
78#endif
Maxime Ripard7d714a22018-01-23 21:16:58 +010079#ifdef CONFIG_ENV_IS_IN_SPI_FLASH
80 ENVL_SPI_FLASH,
81#endif
82#ifdef CONFIG_ENV_IS_IN_UBI
83 ENVL_UBI,
84#endif
85#ifdef CONFIG_ENV_IS_NOWHERE
86 ENVL_NOWHERE,
87#endif
88};
89
Maxime Ripard1d446082018-01-23 21:16:59 +010090static bool env_has_inited(enum env_location location)
91{
92 return gd->env_has_init & BIT(location);
93}
94
95static void env_set_inited(enum env_location location)
96{
97 /*
98 * We're using a 32-bits bitmask stored in gd (env_has_init)
99 * using the above enum value as the bit index. We need to
100 * make sure that we're not overflowing it.
101 */
102 BUILD_BUG_ON(ARRAY_SIZE(env_locations) > BITS_PER_LONG);
103
104 gd->env_has_init |= BIT(location);
105}
106
Maxime Ripard8a3a7e22018-01-23 21:16:52 +0100107/**
108 * env_get_location() - Returns the best env location for a board
109 * @op: operations performed on the environment
110 * @prio: priority between the multiple environments, 0 being the
111 * highest priority
112 *
113 * This will return the preferred environment for the given priority.
Maxime Ripard40c08a62018-01-23 21:17:02 +0100114 * This is overridable by boards if they need to.
Maxime Ripard8a3a7e22018-01-23 21:16:52 +0100115 *
116 * All implementations are free to use the operation, the priority and
117 * any other data relevant to their choice, but must take into account
118 * the fact that the lowest prority (0) is the most important location
119 * in the system. The following locations should be returned by order
120 * of descending priorities, from the highest to the lowest priority.
121 *
122 * Returns:
123 * an enum env_location value on success, a negative error code otherwise
124 */
Maxime Ripard40c08a62018-01-23 21:17:02 +0100125__weak enum env_location env_get_location(enum env_operation op, int prio)
Simon Glassc9d728d2017-08-03 12:22:00 -0600126{
Nicholas Faustinid30ba232018-07-23 10:01:07 +0200127 if (prio >= ARRAY_SIZE(env_locations))
128 return ENVL_UNKNOWN;
Maxime Ripard8a3a7e22018-01-23 21:16:52 +0100129
Nicholas Faustinid30ba232018-07-23 10:01:07 +0200130 gd->env_load_prio = prio;
Maxime Ripard7d714a22018-01-23 21:16:58 +0100131
Nicholas Faustinid30ba232018-07-23 10:01:07 +0200132 return env_locations[prio];
Simon Glassc9d728d2017-08-03 12:22:00 -0600133}
134
Maxime Ripard8a3a7e22018-01-23 21:16:52 +0100135
136/**
137 * env_driver_lookup() - Finds the most suited environment location
138 * @op: operations performed on the environment
139 * @prio: priority between the multiple environments, 0 being the
140 * highest priority
141 *
142 * This will try to find the available environment with the highest
143 * priority in the system.
144 *
145 * Returns:
146 * NULL on error, a pointer to a struct env_driver otherwise
147 */
148static struct env_driver *env_driver_lookup(enum env_operation op, int prio)
Simon Glassc9d728d2017-08-03 12:22:00 -0600149{
Maxime Ripard8a3a7e22018-01-23 21:16:52 +0100150 enum env_location loc = env_get_location(op, prio);
Simon Glassc9d728d2017-08-03 12:22:00 -0600151 struct env_driver *drv;
152
Maxime Ripard8a3a7e22018-01-23 21:16:52 +0100153 if (loc == ENVL_UNKNOWN)
154 return NULL;
155
Maxime Ripard52746c42018-01-23 21:16:51 +0100156 drv = _env_driver_lookup(loc);
Simon Glassc9d728d2017-08-03 12:22:00 -0600157 if (!drv) {
158 debug("%s: No environment driver for location %d\n", __func__,
159 loc);
160 return NULL;
161 }
162
163 return drv;
164}
165
Goldschmidt Simonb2cdef42018-02-09 20:23:17 +0000166__weak int env_get_char_spec(int index)
167{
168 return *(uchar *)(gd->env_addr + index);
169}
170
Simon Glassa69d0f62017-08-03 12:22:06 -0600171int env_get_char(int index)
Simon Glassc9d728d2017-08-03 12:22:00 -0600172{
Simon Glass2d7cb5b2017-08-20 04:45:15 -0600173 if (gd->env_valid == ENV_INVALID)
Simon Glassa69d0f62017-08-03 12:22:06 -0600174 return default_environment[index];
Goldschmidt Simonb2cdef42018-02-09 20:23:17 +0000175 else
176 return env_get_char_spec(index);
Simon Glassc9d728d2017-08-03 12:22:00 -0600177}
178
179int env_load(void)
180{
Maxime Ripard8a3a7e22018-01-23 21:16:52 +0100181 struct env_driver *drv;
Sam Protsenko293a1722019-01-18 21:19:04 +0200182 int best_prio = -1;
Maxime Ripard8a3a7e22018-01-23 21:16:52 +0100183 int prio;
Simon Glassc9d728d2017-08-03 12:22:00 -0600184
Maxime Ripard8a3a7e22018-01-23 21:16:52 +0100185 for (prio = 0; (drv = env_driver_lookup(ENVOP_LOAD, prio)); prio++) {
186 int ret;
187
188 if (!drv->load)
189 continue;
190
Maxime Ripard1d446082018-01-23 21:16:59 +0100191 if (!env_has_inited(drv->location))
192 continue;
193
Maxime Ripard3574ba02018-01-23 21:16:54 +0100194 printf("Loading Environment from %s... ", drv->name);
Sam Protsenko13bbfb42018-07-30 19:19:26 +0300195 /*
196 * In error case, the error message must be printed during
197 * drv->load() in some underlying API, and it must be exactly
198 * one message.
199 */
Maxime Ripard8a3a7e22018-01-23 21:16:52 +0100200 ret = drv->load();
Sam Protsenko293a1722019-01-18 21:19:04 +0200201 if (!ret) {
Maxime Ripard3574ba02018-01-23 21:16:54 +0100202 printf("OK\n");
Maxime Ripard8a3a7e22018-01-23 21:16:52 +0100203 return 0;
Sam Protsenko293a1722019-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 Protsenko13bbfb42018-07-30 19:19:26 +0300210 }
Simon Glassc9d728d2017-08-03 12:22:00 -0600211 }
212
Nicholas Faustinid30ba232018-07-23 10:01:07 +0200213 /*
214 * In case of invalid environment, we set the 'default' env location
Sam Protsenko293a1722019-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 Faustinid30ba232018-07-23 10:01:07 +0200221 */
Sam Protsenko293a1722019-01-18 21:19:04 +0200222 if (best_prio >= 0)
223 debug("Selecting environment with bad CRC\n");
224 else
225 best_prio = 0;
226 env_get_location(ENVOP_LOAD, best_prio);
Nicholas Faustinid30ba232018-07-23 10:01:07 +0200227
Maxime Ripard8a3a7e22018-01-23 21:16:52 +0100228 return -ENODEV;
Simon Glassc9d728d2017-08-03 12:22:00 -0600229}
230
231int env_save(void)
232{
Maxime Ripard8a3a7e22018-01-23 21:16:52 +0100233 struct env_driver *drv;
Simon Glassc9d728d2017-08-03 12:22:00 -0600234
Nicholas Faustinid30ba232018-07-23 10:01:07 +0200235 drv = env_driver_lookup(ENVOP_SAVE, gd->env_load_prio);
236 if (drv) {
Maxime Ripard8a3a7e22018-01-23 21:16:52 +0100237 int ret;
Maxime Ripard9c24dfb2018-01-23 21:16:50 +0100238
Maxime Ripard8a3a7e22018-01-23 21:16:52 +0100239 if (!drv->save)
Nicholas Faustinid30ba232018-07-23 10:01:07 +0200240 return -ENODEV;
Maxime Ripard8a3a7e22018-01-23 21:16:52 +0100241
Maxime Ripard1d446082018-01-23 21:16:59 +0100242 if (!env_has_inited(drv->location))
Nicholas Faustinid30ba232018-07-23 10:01:07 +0200243 return -ENODEV;
Maxime Ripard1d446082018-01-23 21:16:59 +0100244
Maxime Ripard9efac3c2018-01-23 21:16:53 +0100245 printf("Saving Environment to %s... ", drv->name);
Maxime Ripard8a3a7e22018-01-23 21:16:52 +0100246 ret = drv->save();
Maxime Ripard3574ba02018-01-23 21:16:54 +0100247 if (ret)
248 printf("Failed (%d)\n", ret);
249 else
250 printf("OK\n");
251
Maxime Ripard8a3a7e22018-01-23 21:16:52 +0100252 if (!ret)
253 return 0;
Simon Glassc9d728d2017-08-03 12:22:00 -0600254 }
255
Maxime Ripard8a3a7e22018-01-23 21:16:52 +0100256 return -ENODEV;
Simon Glassc9d728d2017-08-03 12:22:00 -0600257}
258
Frank Wunderlichcd121bd2019-06-29 11:36:19 +0200259int env_erase(void)
260{
261 struct env_driver *drv;
262
263 drv = env_driver_lookup(ENVOP_ERASE, gd->env_load_prio);
264 if (drv) {
265 int ret;
266
267 if (!drv->erase)
268 return -ENODEV;
269
270 if (!env_has_inited(drv->location))
271 return -ENODEV;
272
273 printf("Erasing Environment on %s... ", drv->name);
274 ret = drv->erase();
275 if (ret)
276 printf("Failed (%d)\n", ret);
277 else
278 printf("OK\n");
279
280 if (!ret)
281 return 0;
282 }
283
284 return -ENODEV;
285}
286
Simon Glass6eeae422017-08-03 12:22:05 -0600287int env_init(void)
Simon Glassc9d728d2017-08-03 12:22:00 -0600288{
Maxime Ripard8a3a7e22018-01-23 21:16:52 +0100289 struct env_driver *drv;
Simon Glass79388222017-08-03 12:22:02 -0600290 int ret = -ENOENT;
Maxime Ripard8a3a7e22018-01-23 21:16:52 +0100291 int prio;
Simon Glassc9d728d2017-08-03 12:22:00 -0600292
Maxime Ripard8a3a7e22018-01-23 21:16:52 +0100293 for (prio = 0; (drv = env_driver_lookup(ENVOP_INIT, prio)); prio++) {
Maxime Ripard1d446082018-01-23 21:16:59 +0100294 if (!drv->init || !(ret = drv->init()))
295 env_set_inited(drv->location);
Maxime Ripard8a3a7e22018-01-23 21:16:52 +0100296
Maxime Ripard1d446082018-01-23 21:16:59 +0100297 debug("%s: Environment %s init done (ret=%d)\n", __func__,
Maxime Ripard8a3a7e22018-01-23 21:16:52 +0100298 drv->name, ret);
299 }
300
301 if (!prio)
302 return -ENODEV;
303
Simon Glass79388222017-08-03 12:22:02 -0600304 if (ret == -ENOENT) {
305 gd->env_addr = (ulong)&default_environment[0];
Tom Rinieeba55c2017-08-19 22:27:57 -0400306 gd->env_valid = ENV_VALID;
Simon Glass79388222017-08-03 12:22:02 -0600307
308 return 0;
Simon Glassc9d728d2017-08-03 12:22:00 -0600309 }
310
Maxime Ripard8a3a7e22018-01-23 21:16:52 +0100311 return ret;
Simon Glassc9d728d2017-08-03 12:22:00 -0600312}