xref: /openbmc/u-boot/env/env.c (revision 10117a298554d586e7a288a9fa0b574ce35d62a0)
1  /*
2   * Copyright (C) 2017 Google, Inc
3   * Written by Simon Glass <sjg@chromium.org>
4   *
5   * SPDX-License-Identifier:     GPL-2.0+
6   */
7  
8  #include <common.h>
9  #include <environment.h>
10  
11  DECLARE_GLOBAL_DATA_PTR;
12  
13  static struct env_driver *_env_driver_lookup(enum env_location loc)
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 (loc == entry->location)
22  			return entry;
23  	}
24  
25  	/* Not found */
26  	return NULL;
27  }
28  
29  static enum env_location env_locations[] = {
30  #ifdef CONFIG_ENV_IS_IN_EEPROM
31  	ENVL_EEPROM,
32  #endif
33  #ifdef CONFIG_ENV_IS_IN_EXT4
34  	ENVL_EXT4,
35  #endif
36  #ifdef CONFIG_ENV_IS_IN_FAT
37  	ENVL_FAT,
38  #endif
39  #ifdef CONFIG_ENV_IS_IN_FLASH
40  	ENVL_FLASH,
41  #endif
42  #ifdef CONFIG_ENV_IS_IN_MMC
43  	ENVL_MMC,
44  #endif
45  #ifdef CONFIG_ENV_IS_IN_NAND
46  	ENVL_NAND,
47  #endif
48  #ifdef CONFIG_ENV_IS_IN_NVRAM
49  	ENVL_NVRAM,
50  #endif
51  #ifdef CONFIG_ENV_IS_IN_REMOTE
52  	ENVL_REMOTE,
53  #endif
54  #ifdef CONFIG_ENV_IS_IN_SPI_FLASH
55  	ENVL_SPI_FLASH,
56  #endif
57  #ifdef CONFIG_ENV_IS_IN_UBI
58  	ENVL_UBI,
59  #endif
60  #ifdef CONFIG_ENV_IS_NOWHERE
61  	ENVL_NOWHERE,
62  #endif
63  };
64  
65  static bool env_has_inited(enum env_location location)
66  {
67  	return gd->env_has_init & BIT(location);
68  }
69  
70  static void env_set_inited(enum env_location location)
71  {
72  	/*
73  	 * We're using a 32-bits bitmask stored in gd (env_has_init)
74  	 * using the above enum value as the bit index. We need to
75  	 * make sure that we're not overflowing it.
76  	 */
77  	BUILD_BUG_ON(ARRAY_SIZE(env_locations) > BITS_PER_LONG);
78  
79  	gd->env_has_init |= BIT(location);
80  }
81  
82  /**
83   * env_get_location() - Returns the best env location for a board
84   * @op: operations performed on the environment
85   * @prio: priority between the multiple environments, 0 being the
86   *        highest priority
87   *
88   * This will return the preferred environment for the given priority.
89   * This is overridable by boards if they need to.
90   *
91   * All implementations are free to use the operation, the priority and
92   * any other data relevant to their choice, but must take into account
93   * the fact that the lowest prority (0) is the most important location
94   * in the system. The following locations should be returned by order
95   * of descending priorities, from the highest to the lowest priority.
96   *
97   * Returns:
98   * an enum env_location value on success, a negative error code otherwise
99   */
100  __weak enum env_location env_get_location(enum env_operation op, int prio)
101  {
102  	switch (op) {
103  	case ENVOP_GET_CHAR:
104  	case ENVOP_INIT:
105  	case ENVOP_LOAD:
106  		if (prio >= ARRAY_SIZE(env_locations))
107  			return ENVL_UNKNOWN;
108  
109  		gd->env_load_location = env_locations[prio];
110  		return gd->env_load_location;
111  
112  	case ENVOP_SAVE:
113  		return gd->env_load_location;
114  	}
115  
116  	return ENVL_UNKNOWN;
117  }
118  
119  
120  /**
121   * env_driver_lookup() - Finds the most suited environment location
122   * @op: operations performed on the environment
123   * @prio: priority between the multiple environments, 0 being the
124   *        highest priority
125   *
126   * This will try to find the available environment with the highest
127   * priority in the system.
128   *
129   * Returns:
130   * NULL on error, a pointer to a struct env_driver otherwise
131   */
132  static struct env_driver *env_driver_lookup(enum env_operation op, int prio)
133  {
134  	enum env_location loc = env_get_location(op, prio);
135  	struct env_driver *drv;
136  
137  	if (loc == ENVL_UNKNOWN)
138  		return NULL;
139  
140  	drv = _env_driver_lookup(loc);
141  	if (!drv) {
142  		debug("%s: No environment driver for location %d\n", __func__,
143  		      loc);
144  		return NULL;
145  	}
146  
147  	return drv;
148  }
149  
150  __weak int env_get_char_spec(int index)
151  {
152  	return *(uchar *)(gd->env_addr + index);
153  }
154  
155  int env_get_char(int index)
156  {
157  	if (gd->env_valid == ENV_INVALID)
158  		return default_environment[index];
159  	else
160  		return env_get_char_spec(index);
161  }
162  
163  int env_load(void)
164  {
165  	struct env_driver *drv;
166  	int prio;
167  
168  	for (prio = 0; (drv = env_driver_lookup(ENVOP_LOAD, prio)); prio++) {
169  		int ret;
170  
171  		if (!drv->load)
172  			continue;
173  
174  		if (!env_has_inited(drv->location))
175  			continue;
176  
177  		printf("Loading Environment from %s... ", drv->name);
178  		ret = drv->load();
179  		if (ret)
180  			printf("Failed (%d)\n", ret);
181  		else
182  			printf("OK\n");
183  
184  		if (!ret)
185  			return 0;
186  	}
187  
188  	return -ENODEV;
189  }
190  
191  int env_save(void)
192  {
193  	struct env_driver *drv;
194  	int prio;
195  
196  	for (prio = 0; (drv = env_driver_lookup(ENVOP_SAVE, prio)); prio++) {
197  		int ret;
198  
199  		if (!drv->save)
200  			continue;
201  
202  		if (!env_has_inited(drv->location))
203  			continue;
204  
205  		printf("Saving Environment to %s... ", drv->name);
206  		ret = drv->save();
207  		if (ret)
208  			printf("Failed (%d)\n", ret);
209  		else
210  			printf("OK\n");
211  
212  		if (!ret)
213  			return 0;
214  	}
215  
216  	return -ENODEV;
217  }
218  
219  int env_init(void)
220  {
221  	struct env_driver *drv;
222  	int ret = -ENOENT;
223  	int prio;
224  
225  	for (prio = 0; (drv = env_driver_lookup(ENVOP_INIT, prio)); prio++) {
226  		if (!drv->init || !(ret = drv->init()))
227  			env_set_inited(drv->location);
228  
229  		debug("%s: Environment %s init done (ret=%d)\n", __func__,
230  		      drv->name, ret);
231  	}
232  
233  	if (!prio)
234  		return -ENODEV;
235  
236  	if (ret == -ENOENT) {
237  		gd->env_addr = (ulong)&default_environment[0];
238  		gd->env_valid = ENV_VALID;
239  
240  		return 0;
241  	}
242  
243  	return ret;
244  }
245