common: Drop log.h from common header
[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
12 DECLARE_GLOBAL_DATA_PTR;
13
14 #if defined(CONFIG_NEEDS_MANUAL_RELOC)
15 void env_fix_drivers(void)
16 {
17         struct env_driver *drv;
18         const int n_ents = ll_entry_count(struct env_driver, env_driver);
19         struct env_driver *entry;
20
21         drv = ll_entry_start(struct env_driver, env_driver);
22         for (entry = drv; entry != drv + n_ents; entry++) {
23                 if (entry->name)
24                         entry->name += gd->reloc_off;
25                 if (entry->load)
26                         entry->load += gd->reloc_off;
27                 if (entry->save)
28                         entry->save += gd->reloc_off;
29                 if (entry->erase)
30                         entry->erase += gd->reloc_off;
31                 if (entry->init)
32                         entry->init += gd->reloc_off;
33         }
34 }
35 #endif
36
37 static struct env_driver *_env_driver_lookup(enum env_location loc)
38 {
39         struct env_driver *drv;
40         const int n_ents = ll_entry_count(struct env_driver, env_driver);
41         struct env_driver *entry;
42
43         drv = ll_entry_start(struct env_driver, env_driver);
44         for (entry = drv; entry != drv + n_ents; entry++) {
45                 if (loc == entry->location)
46                         return entry;
47         }
48
49         /* Not found */
50         return NULL;
51 }
52
53 static enum env_location env_locations[] = {
54 #ifdef CONFIG_ENV_IS_IN_EEPROM
55         ENVL_EEPROM,
56 #endif
57 #ifdef CONFIG_ENV_IS_IN_EXT4
58         ENVL_EXT4,
59 #endif
60 #ifdef CONFIG_ENV_IS_IN_FAT
61         ENVL_FAT,
62 #endif
63 #ifdef CONFIG_ENV_IS_IN_FLASH
64         ENVL_FLASH,
65 #endif
66 #ifdef CONFIG_ENV_IS_IN_MMC
67         ENVL_MMC,
68 #endif
69 #ifdef CONFIG_ENV_IS_IN_NAND
70         ENVL_NAND,
71 #endif
72 #ifdef CONFIG_ENV_IS_IN_NVRAM
73         ENVL_NVRAM,
74 #endif
75 #ifdef CONFIG_ENV_IS_IN_REMOTE
76         ENVL_REMOTE,
77 #endif
78 #ifdef CONFIG_ENV_IS_IN_SATA
79         ENVL_ESATA,
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(ARRAY_SIZE(env_locations) > BITS_PER_LONG);
105
106         gd->env_has_init |= BIT(location);
107 }
108
109 /**
110  * env_get_location() - Returns the best env location for a board
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 boards if they need to.
117  *
118  * All implementations are free to use the operation, the priority and
119  * any other data relevant to their choice, but must take into account
120  * the fact that the lowest prority (0) is the most important location
121  * in the system. The following locations should be returned by order
122  * of descending priorities, from the highest to the lowest priority.
123  *
124  * Returns:
125  * an enum env_location value on success, a negative error code otherwise
126  */
127 __weak enum env_location env_get_location(enum env_operation op, int prio)
128 {
129         if (prio >= ARRAY_SIZE(env_locations))
130                 return ENVL_UNKNOWN;
131
132         gd->env_load_prio = prio;
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 (!drv->load)
191                         continue;
192
193                 if (!env_has_inited(drv->location))
194                         continue;
195
196                 printf("Loading Environment from %s... ", drv->name);
197                 /*
198                  * In error case, the error message must be printed during
199                  * drv->load() in some underlying API, and it must be exactly
200                  * one message.
201                  */
202                 ret = drv->load();
203                 if (!ret) {
204                         printf("OK\n");
205                         return 0;
206                 } else if (ret == -ENOMSG) {
207                         /* Handle "bad CRC" case */
208                         if (best_prio == -1)
209                                 best_prio = prio;
210                 } else {
211                         debug("Failed (%d)\n", ret);
212                 }
213         }
214
215         /*
216          * In case of invalid environment, we set the 'default' env location
217          * to the best choice, i.e.:
218          *   1. Environment location with bad CRC, if such location was found
219          *   2. Otherwise use the location with highest priority
220          *
221          * This way, next calls to env_save() will restore the environment
222          * at the right place.
223          */
224         if (best_prio >= 0)
225                 debug("Selecting environment with bad CRC\n");
226         else
227                 best_prio = 0;
228         env_get_location(ENVOP_LOAD, best_prio);
229
230         return -ENODEV;
231 }
232
233 int env_save(void)
234 {
235         struct env_driver *drv;
236
237         drv = env_driver_lookup(ENVOP_SAVE, gd->env_load_prio);
238         if (drv) {
239                 int ret;
240
241                 if (!drv->save)
242                         return -ENODEV;
243
244                 if (!env_has_inited(drv->location))
245                         return -ENODEV;
246
247                 printf("Saving Environment to %s... ", drv->name);
248                 ret = drv->save();
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_erase(void)
262 {
263         struct env_driver *drv;
264
265         drv = env_driver_lookup(ENVOP_ERASE, gd->env_load_prio);
266         if (drv) {
267                 int ret;
268
269                 if (!drv->erase)
270                         return -ENODEV;
271
272                 if (!env_has_inited(drv->location))
273                         return -ENODEV;
274
275                 printf("Erasing Environment on %s... ", drv->name);
276                 ret = drv->erase();
277                 if (ret)
278                         printf("Failed (%d)\n", ret);
279                 else
280                         printf("OK\n");
281
282                 if (!ret)
283                         return 0;
284         }
285
286         return -ENODEV;
287 }
288
289 int env_init(void)
290 {
291         struct env_driver *drv;
292         int ret = -ENOENT;
293         int prio;
294
295         for (prio = 0; (drv = env_driver_lookup(ENVOP_INIT, prio)); prio++) {
296                 if (!drv->init || !(ret = drv->init()))
297                         env_set_inited(drv->location);
298
299                 debug("%s: Environment %s init done (ret=%d)\n", __func__,
300                       drv->name, ret);
301         }
302
303         if (!prio)
304                 return -ENODEV;
305
306         if (ret == -ENOENT) {
307                 gd->env_addr = (ulong)&default_environment[0];
308                 gd->env_valid = ENV_VALID;
309
310                 return 0;
311         }
312
313         return ret;
314 }