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