env: correct overflow check of env_has_init size
[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         gd->env_load_prio = prio;
135
136         return env_locations[prio];
137 }
138
139
140 /**
141  * env_driver_lookup() - Finds the most suited environment location
142  * @op: operations performed on the environment
143  * @prio: priority between the multiple environments, 0 being the
144  *        highest priority
145  *
146  * This will try to find the available environment with the highest
147  * priority in the system.
148  *
149  * Returns:
150  * NULL on error, a pointer to a struct env_driver otherwise
151  */
152 static struct env_driver *env_driver_lookup(enum env_operation op, int prio)
153 {
154         enum env_location loc = env_get_location(op, prio);
155         struct env_driver *drv;
156
157         if (loc == ENVL_UNKNOWN)
158                 return NULL;
159
160         drv = _env_driver_lookup(loc);
161         if (!drv) {
162                 debug("%s: No environment driver for location %d\n", __func__,
163                       loc);
164                 return NULL;
165         }
166
167         return drv;
168 }
169
170 __weak int env_get_char_spec(int index)
171 {
172         return *(uchar *)(gd->env_addr + index);
173 }
174
175 int env_get_char(int index)
176 {
177         if (gd->env_valid == ENV_INVALID)
178                 return default_environment[index];
179         else
180                 return env_get_char_spec(index);
181 }
182
183 int env_load(void)
184 {
185         struct env_driver *drv;
186         int best_prio = -1;
187         int prio;
188
189         for (prio = 0; (drv = env_driver_lookup(ENVOP_LOAD, prio)); prio++) {
190                 int ret;
191
192                 if (!drv->load)
193                         continue;
194
195                 if (!env_has_inited(drv->location))
196                         continue;
197
198                 printf("Loading Environment from %s... ", drv->name);
199                 /*
200                  * In error case, the error message must be printed during
201                  * drv->load() in some underlying API, and it must be exactly
202                  * one message.
203                  */
204                 ret = drv->load();
205                 if (!ret) {
206                         printf("OK\n");
207                         return 0;
208                 } else if (ret == -ENOMSG) {
209                         /* Handle "bad CRC" case */
210                         if (best_prio == -1)
211                                 best_prio = prio;
212                 } else {
213                         debug("Failed (%d)\n", ret);
214                 }
215         }
216
217         /*
218          * In case of invalid environment, we set the 'default' env location
219          * to the best choice, i.e.:
220          *   1. Environment location with bad CRC, if such location was found
221          *   2. Otherwise use the location with highest priority
222          *
223          * This way, next calls to env_save() will restore the environment
224          * at the right place.
225          */
226         if (best_prio >= 0)
227                 debug("Selecting environment with bad CRC\n");
228         else
229                 best_prio = 0;
230         env_get_location(ENVOP_LOAD, best_prio);
231
232         return -ENODEV;
233 }
234
235 int env_save(void)
236 {
237         struct env_driver *drv;
238
239         drv = env_driver_lookup(ENVOP_SAVE, gd->env_load_prio);
240         if (drv) {
241                 int ret;
242
243                 if (!drv->save)
244                         return -ENODEV;
245
246                 if (!env_has_inited(drv->location))
247                         return -ENODEV;
248
249                 printf("Saving Environment to %s... ", drv->name);
250                 ret = drv->save();
251                 if (ret)
252                         printf("Failed (%d)\n", ret);
253                 else
254                         printf("OK\n");
255
256                 if (!ret)
257                         return 0;
258         }
259
260         return -ENODEV;
261 }
262
263 int env_erase(void)
264 {
265         struct env_driver *drv;
266
267         drv = env_driver_lookup(ENVOP_ERASE, gd->env_load_prio);
268         if (drv) {
269                 int ret;
270
271                 if (!drv->erase)
272                         return -ENODEV;
273
274                 if (!env_has_inited(drv->location))
275                         return -ENODEV;
276
277                 printf("Erasing Environment on %s... ", drv->name);
278                 ret = drv->erase();
279                 if (ret)
280                         printf("Failed (%d)\n", ret);
281                 else
282                         printf("OK\n");
283
284                 if (!ret)
285                         return 0;
286         }
287
288         return -ENODEV;
289 }
290
291 int env_init(void)
292 {
293         struct env_driver *drv;
294         int ret = -ENOENT;
295         int prio;
296
297         for (prio = 0; (drv = env_driver_lookup(ENVOP_INIT, prio)); prio++) {
298                 if (!drv->init || !(ret = drv->init()))
299                         env_set_inited(drv->location);
300
301                 debug("%s: Environment %s init done (ret=%d)\n", __func__,
302                       drv->name, ret);
303         }
304
305         if (!prio)
306                 return -ENODEV;
307
308         if (ret == -ENOENT) {
309                 gd->env_addr = (ulong)&default_environment[0];
310                 gd->env_valid = ENV_VALID;
311
312                 return 0;
313         }
314
315         return ret;
316 }