env: register erase 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 <environment.h>
9
10 DECLARE_GLOBAL_DATA_PTR;
11
12 #if defined(CONFIG_NEEDS_MANUAL_RELOC)
13 void 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;
27                 if (entry->erase)
28                         entry->erase += gd->reloc_off;
29                 if (entry->init)
30                         entry->init += gd->reloc_off;
31         }
32 }
33 #endif
34
35 static struct env_driver *_env_driver_lookup(enum env_location loc)
36 {
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
51 static 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
76 #ifdef CONFIG_ENV_IS_IN_SATA
77         ENVL_ESATA,
78 #endif
79 #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
90 static bool env_has_inited(enum env_location location)
91 {
92         return gd->env_has_init & BIT(location);
93 }
94
95 static 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
107 /**
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.
114  * This is overridable by boards if they need to.
115  *
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  */
125 __weak enum env_location env_get_location(enum env_operation op, int prio)
126 {
127         if (prio >= ARRAY_SIZE(env_locations))
128                 return ENVL_UNKNOWN;
129
130         gd->env_load_prio = prio;
131
132         return env_locations[prio];
133 }
134
135
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  */
148 static struct env_driver *env_driver_lookup(enum env_operation op, int prio)
149 {
150         enum env_location loc = env_get_location(op, prio);
151         struct env_driver *drv;
152
153         if (loc == ENVL_UNKNOWN)
154                 return NULL;
155
156         drv = _env_driver_lookup(loc);
157         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
166 __weak int env_get_char_spec(int index)
167 {
168         return *(uchar *)(gd->env_addr + index);
169 }
170
171 int env_get_char(int index)
172 {
173         if (gd->env_valid == ENV_INVALID)
174                 return default_environment[index];
175         else
176                 return env_get_char_spec(index);
177 }
178
179 int env_load(void)
180 {
181         struct env_driver *drv;
182         int best_prio = -1;
183         int prio;
184
185         for (prio = 0; (drv = env_driver_lookup(ENVOP_LOAD, prio)); prio++) {
186                 int ret;
187
188                 if (!drv->load)
189                         continue;
190
191                 if (!env_has_inited(drv->location))
192                         continue;
193
194                 printf("Loading Environment from %s... ", drv->name);
195                 /*
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                  */
200                 ret = drv->load();
201                 if (!ret) {
202                         printf("OK\n");
203                         return 0;
204                 } 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);
210                 }
211         }
212
213         /*
214          * In case of invalid environment, we set the 'default' env location
215          * 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.
221          */
222         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);
227
228         return -ENODEV;
229 }
230
231 int env_save(void)
232 {
233         struct env_driver *drv;
234
235         drv = env_driver_lookup(ENVOP_SAVE, gd->env_load_prio);
236         if (drv) {
237                 int ret;
238
239                 if (!drv->save)
240                         return -ENODEV;
241
242                 if (!env_has_inited(drv->location))
243                         return -ENODEV;
244
245                 printf("Saving Environment to %s... ", drv->name);
246                 ret = drv->save();
247                 if (ret)
248                         printf("Failed (%d)\n", ret);
249                 else
250                         printf("OK\n");
251
252                 if (!ret)
253                         return 0;
254         }
255
256         return -ENODEV;
257 }
258
259 int 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
287 int env_init(void)
288 {
289         struct env_driver *drv;
290         int ret = -ENOENT;
291         int prio;
292
293         for (prio = 0; (drv = env_driver_lookup(ENVOP_INIT, prio)); prio++) {
294                 if (!drv->init || !(ret = drv->init()))
295                         env_set_inited(drv->location);
296
297                 debug("%s: Environment %s init done (ret=%d)\n", __func__,
298                       drv->name, ret);
299         }
300
301         if (!prio)
302                 return -ENODEV;
303
304         if (ret == -ENOENT) {
305                 gd->env_addr = (ulong)&default_environment[0];
306                 gd->env_valid = ENV_VALID;
307
308                 return 0;
309         }
310
311         return ret;
312 }