common: Drop linux/bug.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 #include <linux/bug.h>
12
13 DECLARE_GLOBAL_DATA_PTR;
14
15 #if defined(CONFIG_NEEDS_MANUAL_RELOC)
16 void env_fix_drivers(void)
17 {
18         struct env_driver *drv;
19         const int n_ents = ll_entry_count(struct env_driver, env_driver);
20         struct env_driver *entry;
21
22         drv = ll_entry_start(struct env_driver, env_driver);
23         for (entry = drv; entry != drv + n_ents; entry++) {
24                 if (entry->name)
25                         entry->name += gd->reloc_off;
26                 if (entry->load)
27                         entry->load += gd->reloc_off;
28                 if (entry->save)
29                         entry->save += gd->reloc_off;
30                 if (entry->erase)
31                         entry->erase += gd->reloc_off;
32                 if (entry->init)
33                         entry->init += gd->reloc_off;
34         }
35 }
36 #endif
37
38 static struct env_driver *_env_driver_lookup(enum env_location loc)
39 {
40         struct env_driver *drv;
41         const int n_ents = ll_entry_count(struct env_driver, env_driver);
42         struct env_driver *entry;
43
44         drv = ll_entry_start(struct env_driver, env_driver);
45         for (entry = drv; entry != drv + n_ents; entry++) {
46                 if (loc == entry->location)
47                         return entry;
48         }
49
50         /* Not found */
51         return NULL;
52 }
53
54 static enum env_location env_locations[] = {
55 #ifdef CONFIG_ENV_IS_IN_EEPROM
56         ENVL_EEPROM,
57 #endif
58 #ifdef CONFIG_ENV_IS_IN_EXT4
59         ENVL_EXT4,
60 #endif
61 #ifdef CONFIG_ENV_IS_IN_FAT
62         ENVL_FAT,
63 #endif
64 #ifdef CONFIG_ENV_IS_IN_FLASH
65         ENVL_FLASH,
66 #endif
67 #ifdef CONFIG_ENV_IS_IN_MMC
68         ENVL_MMC,
69 #endif
70 #ifdef CONFIG_ENV_IS_IN_NAND
71         ENVL_NAND,
72 #endif
73 #ifdef CONFIG_ENV_IS_IN_NVRAM
74         ENVL_NVRAM,
75 #endif
76 #ifdef CONFIG_ENV_IS_IN_REMOTE
77         ENVL_REMOTE,
78 #endif
79 #ifdef CONFIG_ENV_IS_IN_SATA
80         ENVL_ESATA,
81 #endif
82 #ifdef CONFIG_ENV_IS_IN_SPI_FLASH
83         ENVL_SPI_FLASH,
84 #endif
85 #ifdef CONFIG_ENV_IS_IN_UBI
86         ENVL_UBI,
87 #endif
88 #ifdef CONFIG_ENV_IS_NOWHERE
89         ENVL_NOWHERE,
90 #endif
91 };
92
93 static bool env_has_inited(enum env_location location)
94 {
95         return gd->env_has_init & BIT(location);
96 }
97
98 static void env_set_inited(enum env_location location)
99 {
100         /*
101          * We're using a 32-bits bitmask stored in gd (env_has_init)
102          * using the above enum value as the bit index. We need to
103          * make sure that we're not overflowing it.
104          */
105         BUILD_BUG_ON(ARRAY_SIZE(env_locations) > BITS_PER_LONG);
106
107         gd->env_has_init |= BIT(location);
108 }
109
110 /**
111  * env_get_location() - Returns the best env location for a board
112  * @op: operations performed on the environment
113  * @prio: priority between the multiple environments, 0 being the
114  *        highest priority
115  *
116  * This will return the preferred environment for the given priority.
117  * This is overridable by boards if they need to.
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 env_get_location(enum env_operation op, int prio)
129 {
130         if (prio >= ARRAY_SIZE(env_locations))
131                 return ENVL_UNKNOWN;
132
133         gd->env_load_prio = prio;
134
135         return env_locations[prio];
136 }
137
138
139 /**
140  * env_driver_lookup() - Finds the most suited environment location
141  * @op: operations performed on the environment
142  * @prio: priority between the multiple environments, 0 being the
143  *        highest priority
144  *
145  * This will try to find the available environment with the highest
146  * priority in the system.
147  *
148  * Returns:
149  * NULL on error, a pointer to a struct env_driver otherwise
150  */
151 static struct env_driver *env_driver_lookup(enum env_operation op, int prio)
152 {
153         enum env_location loc = env_get_location(op, prio);
154         struct env_driver *drv;
155
156         if (loc == ENVL_UNKNOWN)
157                 return NULL;
158
159         drv = _env_driver_lookup(loc);
160         if (!drv) {
161                 debug("%s: No environment driver for location %d\n", __func__,
162                       loc);
163                 return NULL;
164         }
165
166         return drv;
167 }
168
169 __weak int env_get_char_spec(int index)
170 {
171         return *(uchar *)(gd->env_addr + index);
172 }
173
174 int env_get_char(int index)
175 {
176         if (gd->env_valid == ENV_INVALID)
177                 return default_environment[index];
178         else
179                 return env_get_char_spec(index);
180 }
181
182 int env_load(void)
183 {
184         struct env_driver *drv;
185         int best_prio = -1;
186         int prio;
187
188         for (prio = 0; (drv = env_driver_lookup(ENVOP_LOAD, prio)); prio++) {
189                 int ret;
190
191                 if (!drv->load)
192                         continue;
193
194                 if (!env_has_inited(drv->location))
195                         continue;
196
197                 printf("Loading Environment from %s... ", drv->name);
198                 /*
199                  * In error case, the error message must be printed during
200                  * drv->load() in some underlying API, and it must be exactly
201                  * one message.
202                  */
203                 ret = drv->load();
204                 if (!ret) {
205                         printf("OK\n");
206                         return 0;
207                 } else if (ret == -ENOMSG) {
208                         /* Handle "bad CRC" case */
209                         if (best_prio == -1)
210                                 best_prio = prio;
211                 } else {
212                         debug("Failed (%d)\n", ret);
213                 }
214         }
215
216         /*
217          * In case of invalid environment, we set the 'default' env location
218          * to the best choice, i.e.:
219          *   1. Environment location with bad CRC, if such location was found
220          *   2. Otherwise use the location with highest priority
221          *
222          * This way, next calls to env_save() will restore the environment
223          * at the right place.
224          */
225         if (best_prio >= 0)
226                 debug("Selecting environment with bad CRC\n");
227         else
228                 best_prio = 0;
229         env_get_location(ENVOP_LOAD, best_prio);
230
231         return -ENODEV;
232 }
233
234 int env_save(void)
235 {
236         struct env_driver *drv;
237
238         drv = env_driver_lookup(ENVOP_SAVE, gd->env_load_prio);
239         if (drv) {
240                 int ret;
241
242                 if (!drv->save)
243                         return -ENODEV;
244
245                 if (!env_has_inited(drv->location))
246                         return -ENODEV;
247
248                 printf("Saving Environment to %s... ", drv->name);
249                 ret = drv->save();
250                 if (ret)
251                         printf("Failed (%d)\n", ret);
252                 else
253                         printf("OK\n");
254
255                 if (!ret)
256                         return 0;
257         }
258
259         return -ENODEV;
260 }
261
262 int env_erase(void)
263 {
264         struct env_driver *drv;
265
266         drv = env_driver_lookup(ENVOP_ERASE, gd->env_load_prio);
267         if (drv) {
268                 int ret;
269
270                 if (!drv->erase)
271                         return -ENODEV;
272
273                 if (!env_has_inited(drv->location))
274                         return -ENODEV;
275
276                 printf("Erasing Environment on %s... ", drv->name);
277                 ret = drv->erase();
278                 if (ret)
279                         printf("Failed (%d)\n", ret);
280                 else
281                         printf("OK\n");
282
283                 if (!ret)
284                         return 0;
285         }
286
287         return -ENODEV;
288 }
289
290 int env_init(void)
291 {
292         struct env_driver *drv;
293         int ret = -ENOENT;
294         int prio;
295
296         for (prio = 0; (drv = env_driver_lookup(ENVOP_INIT, prio)); prio++) {
297                 if (!drv->init || !(ret = drv->init()))
298                         env_set_inited(drv->location);
299
300                 debug("%s: Environment %s init done (ret=%d)\n", __func__,
301                       drv->name, ret);
302         }
303
304         if (!prio)
305                 return -ENODEV;
306
307         if (ret == -ENOENT) {
308                 gd->env_addr = (ulong)&default_environment[0];
309                 gd->env_valid = ENV_VALID;
310
311                 return 0;
312         }
313
314         return ret;
315 }