Prepare v2023.10
[platform/kernel/u-boot.git] / env / env.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2017 Google, Inc
4  * Written by Simon Glass <sjg@chromium.org>
5  */
6
7 #include <common.h>
8 #include <env.h>
9 #include <env_internal.h>
10 #include <log.h>
11 #include <asm/global_data.h>
12 #include <linux/bitops.h>
13 #include <linux/bug.h>
14
15 DECLARE_GLOBAL_DATA_PTR;
16
17 #if defined(CONFIG_NEEDS_MANUAL_RELOC)
18 void env_fix_drivers(void)
19 {
20         struct env_driver *drv;
21         const int n_ents = ll_entry_count(struct env_driver, env_driver);
22         struct env_driver *entry;
23
24         drv = ll_entry_start(struct env_driver, env_driver);
25         for (entry = drv; entry != drv + n_ents; entry++) {
26                 if (entry->name)
27                         entry->name += gd->reloc_off;
28                 if (entry->load)
29                         entry->load += gd->reloc_off;
30                 if (entry->save)
31                         entry->save += gd->reloc_off;
32                 if (entry->erase)
33                         entry->erase += gd->reloc_off;
34                 if (entry->init)
35                         entry->init += gd->reloc_off;
36         }
37 }
38 #endif
39
40 static struct env_driver *_env_driver_lookup(enum env_location loc)
41 {
42         struct env_driver *drv;
43         const int n_ents = ll_entry_count(struct env_driver, env_driver);
44         struct env_driver *entry;
45
46         drv = ll_entry_start(struct env_driver, env_driver);
47         for (entry = drv; entry != drv + n_ents; entry++) {
48                 if (loc == entry->location)
49                         return entry;
50         }
51
52         /* Not found */
53         return NULL;
54 }
55
56 static enum env_location env_locations[] = {
57 #ifdef CONFIG_ENV_IS_IN_EEPROM
58         ENVL_EEPROM,
59 #endif
60 #ifdef CONFIG_ENV_IS_IN_EXT4
61         ENVL_EXT4,
62 #endif
63 #ifdef CONFIG_ENV_IS_IN_FAT
64         ENVL_FAT,
65 #endif
66 #ifdef CONFIG_ENV_IS_IN_FLASH
67         ENVL_FLASH,
68 #endif
69 #ifdef CONFIG_ENV_IS_IN_MMC
70         ENVL_MMC,
71 #endif
72 #ifdef CONFIG_ENV_IS_IN_NAND
73         ENVL_NAND,
74 #endif
75 #ifdef CONFIG_ENV_IS_IN_NVRAM
76         ENVL_NVRAM,
77 #endif
78 #ifdef CONFIG_ENV_IS_IN_REMOTE
79         ENVL_REMOTE,
80 #endif
81 #ifdef CONFIG_ENV_IS_IN_SPI_FLASH
82         ENVL_SPI_FLASH,
83 #endif
84 #ifdef CONFIG_ENV_IS_IN_UBI
85         ENVL_UBI,
86 #endif
87 #ifdef CONFIG_ENV_IS_NOWHERE
88         ENVL_NOWHERE,
89 #endif
90 };
91
92 static bool env_has_inited(enum env_location location)
93 {
94         return gd->env_has_init & BIT(location);
95 }
96
97 static void env_set_inited(enum env_location location)
98 {
99         /*
100          * We're using a 32-bits bitmask stored in gd (env_has_init)
101          * using the above enum value as the bit index. We need to
102          * make sure that we're not overflowing it.
103          */
104         BUILD_BUG_ON(ENVL_COUNT > BITS_PER_LONG);
105
106         gd->env_has_init |= BIT(location);
107 }
108
109 /**
110  * arch_env_get_location() - Returns the best env location for an arch
111  * @op: operations performed on the environment
112  * @prio: priority between the multiple environments, 0 being the
113  *        highest priority
114  *
115  * This will return the preferred environment for the given priority.
116  * This is overridable by architectures if they need to and has lower
117  * priority than board side env_get_location() override.
118  *
119  * All implementations are free to use the operation, the priority and
120  * any other data relevant to their choice, but must take into account
121  * the fact that the lowest prority (0) is the most important location
122  * in the system. The following locations should be returned by order
123  * of descending priorities, from the highest to the lowest priority.
124  *
125  * Returns:
126  * an enum env_location value on success, a negative error code otherwise
127  */
128 __weak enum env_location arch_env_get_location(enum env_operation op, int prio)
129 {
130         if (prio >= ARRAY_SIZE(env_locations))
131                 return ENVL_UNKNOWN;
132
133         return env_locations[prio];
134 }
135
136 /**
137  * env_get_location() - Returns the best env location for a board
138  * @op: operations performed on the environment
139  * @prio: priority between the multiple environments, 0 being the
140  *        highest priority
141  *
142  * This will return the preferred environment for the given priority.
143  * This is overridable by boards if they need to.
144  *
145  * All implementations are free to use the operation, the priority and
146  * any other data relevant to their choice, but must take into account
147  * the fact that the lowest prority (0) is the most important location
148  * in the system. The following locations should be returned by order
149  * of descending priorities, from the highest to the lowest priority.
150  *
151  * Returns:
152  * an enum env_location value on success, a negative error code otherwise
153  */
154 __weak enum env_location env_get_location(enum env_operation op, int prio)
155 {
156         return arch_env_get_location(op, prio);
157 }
158
159 /**
160  * env_driver_lookup() - Finds the most suited environment location
161  * @op: operations performed on the environment
162  * @prio: priority between the multiple environments, 0 being the
163  *        highest priority
164  *
165  * This will try to find the available environment with the highest
166  * priority in the system.
167  *
168  * Returns:
169  * NULL on error, a pointer to a struct env_driver otherwise
170  */
171 static struct env_driver *env_driver_lookup(enum env_operation op, int prio)
172 {
173         enum env_location loc = env_get_location(op, prio);
174         struct env_driver *drv;
175
176         if (loc == ENVL_UNKNOWN)
177                 return NULL;
178
179         drv = _env_driver_lookup(loc);
180         if (!drv) {
181                 debug("%s: No environment driver for location %d\n", __func__,
182                       loc);
183                 return NULL;
184         }
185
186         return drv;
187 }
188
189 int env_load(void)
190 {
191         struct env_driver *drv;
192         int best_prio = -1;
193         int prio;
194
195         if (CONFIG_IS_ENABLED(ENV_WRITEABLE_LIST)) {
196                 /*
197                  * When using a list of writeable variables, the baseline comes
198                  * from the built-in default env. So load this first.
199                  */
200                 env_set_default(NULL, 0);
201         }
202
203         for (prio = 0; (drv = env_driver_lookup(ENVOP_LOAD, prio)); prio++) {
204                 int ret;
205
206                 if (!env_has_inited(drv->location))
207                         continue;
208
209                 printf("Loading Environment from %s... ", drv->name);
210                 /*
211                  * In error case, the error message must be printed during
212                  * drv->load() in some underlying API, and it must be exactly
213                  * one message.
214                  */
215                 ret = drv->load();
216                 if (!ret) {
217                         printf("OK\n");
218                         gd->env_load_prio = prio;
219
220                         return 0;
221                 } else if (ret == -ENOMSG) {
222                         /* Handle "bad CRC" case */
223                         if (best_prio == -1)
224                                 best_prio = prio;
225                 } else {
226                         debug("Failed (%d)\n", ret);
227                 }
228         }
229
230         /*
231          * In case of invalid environment, we set the 'default' env location
232          * to the best choice, i.e.:
233          *   1. Environment location with bad CRC, if such location was found
234          *   2. Otherwise use the location with highest priority
235          *
236          * This way, next calls to env_save() will restore the environment
237          * at the right place.
238          */
239         if (best_prio >= 0)
240                 debug("Selecting environment with bad CRC\n");
241         else
242                 best_prio = 0;
243
244         gd->env_load_prio = best_prio;
245
246         return -ENODEV;
247 }
248
249 int env_reload(void)
250 {
251         struct env_driver *drv;
252
253         drv = env_driver_lookup(ENVOP_LOAD, gd->env_load_prio);
254         if (drv) {
255                 int ret;
256
257                 printf("Loading Environment from %s... ", drv->name);
258
259                 if (!env_has_inited(drv->location)) {
260                         printf("not initialized\n");
261                         return -ENODEV;
262                 }
263
264                 ret = drv->load();
265                 if (ret)
266                         printf("Failed (%d)\n", ret);
267                 else
268                         printf("OK\n");
269
270                 if (!ret)
271                         return 0;
272         }
273
274         return -ENODEV;
275 }
276
277 int env_save(void)
278 {
279         struct env_driver *drv;
280
281         drv = env_driver_lookup(ENVOP_SAVE, gd->env_load_prio);
282         if (drv) {
283                 int ret;
284
285                 printf("Saving Environment to %s... ", drv->name);
286                 if (!drv->save) {
287                         printf("not possible\n");
288                         return -ENODEV;
289                 }
290
291                 if (!env_has_inited(drv->location)) {
292                         printf("not initialized\n");
293                         return -ENODEV;
294                 }
295
296                 ret = drv->save();
297                 if (ret)
298                         printf("Failed (%d)\n", ret);
299                 else
300                         printf("OK\n");
301
302                 if (!ret)
303                         return 0;
304         }
305
306         return -ENODEV;
307 }
308
309 int env_erase(void)
310 {
311         struct env_driver *drv;
312
313         drv = env_driver_lookup(ENVOP_ERASE, gd->env_load_prio);
314         if (drv) {
315                 int ret;
316
317                 if (!drv->erase) {
318                         printf("not possible\n");
319                         return -ENODEV;
320                 }
321
322                 if (!env_has_inited(drv->location)) {
323                         printf("not initialized\n");
324                         return -ENODEV;
325                 }
326
327                 printf("Erasing Environment on %s... ", drv->name);
328                 ret = drv->erase();
329                 if (ret)
330                         printf("Failed (%d)\n", ret);
331                 else
332                         printf("OK\n");
333
334                 if (!ret)
335                         return 0;
336         }
337
338         return -ENODEV;
339 }
340
341 int env_init(void)
342 {
343         struct env_driver *drv;
344         int ret = -ENOENT;
345         int prio;
346
347         for (prio = 0; (drv = env_driver_lookup(ENVOP_INIT, prio)); prio++) {
348                 if (!drv->init || !(ret = drv->init()))
349                         env_set_inited(drv->location);
350                 if (ret == -ENOENT)
351                         env_set_inited(drv->location);
352
353                 debug("%s: Environment %s init done (ret=%d)\n", __func__,
354                       drv->name, ret);
355
356                 if (gd->env_valid == ENV_INVALID)
357                         ret = -ENOENT;
358         }
359
360         if (!prio)
361                 return -ENODEV;
362
363         if (ret == -ENOENT) {
364                 gd->env_addr = (ulong)&default_environment[0];
365                 gd->env_valid = ENV_VALID;
366
367                 return 0;
368         }
369
370         return ret;
371 }
372
373 int env_select(const char *name)
374 {
375         struct env_driver *drv;
376         const int n_ents = ll_entry_count(struct env_driver, env_driver);
377         struct env_driver *entry;
378         int prio;
379         bool found = false;
380
381         printf("Select Environment on %s: ", name);
382
383         /* search ENV driver by name */
384         drv = ll_entry_start(struct env_driver, env_driver);
385         for (entry = drv; entry != drv + n_ents; entry++) {
386                 if (!strcmp(entry->name, name)) {
387                         found = true;
388                         break;
389                 }
390         }
391
392         if (!found) {
393                 printf("driver not found\n");
394                 return -ENODEV;
395         }
396
397         /* search priority by driver */
398         for (prio = 0; (drv = env_driver_lookup(ENVOP_INIT, prio)); prio++) {
399                 if (entry->location == env_get_location(ENVOP_LOAD, prio)) {
400                         /* when priority change, reset the ENV flags */
401                         if (gd->env_load_prio != prio) {
402                                 gd->env_load_prio = prio;
403                                 gd->env_valid = ENV_INVALID;
404                                 gd->flags &= ~GD_FLG_ENV_DEFAULT;
405                         }
406                         printf("OK\n");
407                         return 0;
408                 }
409         }
410         printf("priority not found\n");
411
412         return -ENODEV;
413 }