xref: /openbmc/u-boot/env/flash.c (revision 203e94f6c9ca03e260175ce240f5856507395585)
1  /*
2   * (C) Copyright 2000-2010
3   * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
4   *
5   * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
6   * Andreas Heppel <aheppel@sysgo.de>
7  
8   * SPDX-License-Identifier:	GPL-2.0+
9   */
10  
11  /* #define DEBUG */
12  
13  #include <common.h>
14  #include <command.h>
15  #include <environment.h>
16  #include <linux/stddef.h>
17  #include <malloc.h>
18  #include <search.h>
19  #include <errno.h>
20  
21  DECLARE_GLOBAL_DATA_PTR;
22  
23  #if defined(CONFIG_CMD_SAVEENV) && defined(CONFIG_CMD_FLASH)
24  #define CMD_SAVEENV
25  #elif defined(CONFIG_ENV_ADDR_REDUND)
26  #error CONFIG_ENV_ADDR_REDUND must have CONFIG_CMD_SAVEENV & CONFIG_CMD_FLASH
27  #endif
28  
29  #if defined(CONFIG_ENV_SIZE_REDUND) &&	\
30  	(CONFIG_ENV_SIZE_REDUND < CONFIG_ENV_SIZE)
31  #error CONFIG_ENV_SIZE_REDUND should not be less then CONFIG_ENV_SIZE
32  #endif
33  
34  char *env_name_spec = "Flash";
35  
36  #ifdef ENV_IS_EMBEDDED
37  env_t *env_ptr = &environment;
38  
39  static env_t *flash_addr = (env_t *)CONFIG_ENV_ADDR;
40  
41  #else /* ! ENV_IS_EMBEDDED */
42  
43  env_t *env_ptr = (env_t *)CONFIG_ENV_ADDR;
44  static env_t *flash_addr = (env_t *)CONFIG_ENV_ADDR;
45  #endif /* ENV_IS_EMBEDDED */
46  
47  #if defined(CMD_SAVEENV) || defined(CONFIG_ENV_ADDR_REDUND)
48  /* CONFIG_ENV_ADDR is supposed to be on sector boundary */
49  static ulong end_addr = CONFIG_ENV_ADDR + CONFIG_ENV_SECT_SIZE - 1;
50  #endif
51  
52  #ifdef CONFIG_ENV_ADDR_REDUND
53  static env_t *flash_addr_new = (env_t *)CONFIG_ENV_ADDR_REDUND;
54  
55  /* CONFIG_ENV_ADDR_REDUND is supposed to be on sector boundary */
56  static ulong end_addr_new = CONFIG_ENV_ADDR_REDUND + CONFIG_ENV_SECT_SIZE - 1;
57  #endif /* CONFIG_ENV_ADDR_REDUND */
58  
59  
60  #ifdef CONFIG_ENV_ADDR_REDUND
61  int env_init(void)
62  {
63  	int crc1_ok = 0, crc2_ok = 0;
64  
65  	uchar flag1 = flash_addr->flags;
66  	uchar flag2 = flash_addr_new->flags;
67  
68  	ulong addr_default = (ulong)&default_environment[0];
69  	ulong addr1 = (ulong)&(flash_addr->data);
70  	ulong addr2 = (ulong)&(flash_addr_new->data);
71  
72  	crc1_ok = crc32(0, flash_addr->data, ENV_SIZE) == flash_addr->crc;
73  	crc2_ok =
74  		crc32(0, flash_addr_new->data, ENV_SIZE) == flash_addr_new->crc;
75  
76  	if (crc1_ok && !crc2_ok) {
77  		gd->env_addr	= addr1;
78  		gd->env_valid	= ENV_VALID;
79  	} else if (!crc1_ok && crc2_ok) {
80  		gd->env_addr	= addr2;
81  		gd->env_valid	= ENV_VALID;
82  	} else if (!crc1_ok && !crc2_ok) {
83  		gd->env_addr	= addr_default;
84  		gd->env_valid	= 0;
85  	} else if (flag1 == ACTIVE_FLAG && flag2 == OBSOLETE_FLAG) {
86  		gd->env_addr	= addr1;
87  		gd->env_valid	= ENV_VALID;
88  	} else if (flag1 == OBSOLETE_FLAG && flag2 == ACTIVE_FLAG) {
89  		gd->env_addr	= addr2;
90  		gd->env_valid	= ENV_VALID;
91  	} else if (flag1 == flag2) {
92  		gd->env_addr	= addr1;
93  		gd->env_valid	= ENV_REDUND;
94  	} else if (flag1 == 0xFF) {
95  		gd->env_addr	= addr1;
96  		gd->env_valid	= ENV_REDUND;
97  	} else if (flag2 == 0xFF) {
98  		gd->env_addr	= addr2;
99  		gd->env_valid	= ENV_REDUND;
100  	}
101  
102  	return 0;
103  }
104  
105  #ifdef CMD_SAVEENV
106  int saveenv(void)
107  {
108  	env_t	env_new;
109  	char	*saved_data = NULL;
110  	char	flag = OBSOLETE_FLAG, new_flag = ACTIVE_FLAG;
111  	int	rc = 1;
112  #if CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE
113  	ulong	up_data = 0;
114  #endif
115  
116  	debug("Protect off %08lX ... %08lX\n", (ulong)flash_addr, end_addr);
117  
118  	if (flash_sect_protect(0, (ulong)flash_addr, end_addr))
119  		goto done;
120  
121  	debug("Protect off %08lX ... %08lX\n",
122  		(ulong)flash_addr_new, end_addr_new);
123  
124  	if (flash_sect_protect(0, (ulong)flash_addr_new, end_addr_new))
125  		goto done;
126  
127  	rc = env_export(&env_new);
128  	if (rc)
129  		return rc;
130  	env_new.flags	= new_flag;
131  
132  #if CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE
133  	up_data = end_addr_new + 1 - ((long)flash_addr_new + CONFIG_ENV_SIZE);
134  	debug("Data to save 0x%lX\n", up_data);
135  	if (up_data) {
136  		saved_data = malloc(up_data);
137  		if (saved_data == NULL) {
138  			printf("Unable to save the rest of sector (%ld)\n",
139  				up_data);
140  			goto done;
141  		}
142  		memcpy(saved_data,
143  			(void *)((long)flash_addr_new + CONFIG_ENV_SIZE),
144  			up_data);
145  		debug("Data (start 0x%lX, len 0x%lX) saved at 0x%p\n",
146  			(long)flash_addr_new + CONFIG_ENV_SIZE,
147  			up_data, saved_data);
148  	}
149  #endif
150  	puts("Erasing Flash...");
151  	debug(" %08lX ... %08lX ...", (ulong)flash_addr_new, end_addr_new);
152  
153  	if (flash_sect_erase((ulong)flash_addr_new, end_addr_new))
154  		goto done;
155  
156  	puts("Writing to Flash... ");
157  	debug(" %08lX ... %08lX ...",
158  		(ulong)&(flash_addr_new->data),
159  		sizeof(env_ptr->data) + (ulong)&(flash_addr_new->data));
160  	rc = flash_write((char *)&env_new, (ulong)flash_addr_new,
161  			 sizeof(env_new));
162  	if (rc)
163  		goto perror;
164  
165  	rc = flash_write(&flag, (ulong)&(flash_addr->flags),
166  			 sizeof(flash_addr->flags));
167  	if (rc)
168  		goto perror;
169  
170  #if CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE
171  	if (up_data) { /* restore the rest of sector */
172  		debug("Restoring the rest of data to 0x%lX len 0x%lX\n",
173  			(long)flash_addr_new + CONFIG_ENV_SIZE, up_data);
174  		if (flash_write(saved_data,
175  				(long)flash_addr_new + CONFIG_ENV_SIZE,
176  				up_data))
177  			goto perror;
178  	}
179  #endif
180  	puts("done\n");
181  
182  	{
183  		env_t *etmp = flash_addr;
184  		ulong ltmp = end_addr;
185  
186  		flash_addr = flash_addr_new;
187  		flash_addr_new = etmp;
188  
189  		end_addr = end_addr_new;
190  		end_addr_new = ltmp;
191  	}
192  
193  	rc = 0;
194  	goto done;
195  perror:
196  	flash_perror(rc);
197  done:
198  	if (saved_data)
199  		free(saved_data);
200  	/* try to re-protect */
201  	flash_sect_protect(1, (ulong)flash_addr, end_addr);
202  	flash_sect_protect(1, (ulong)flash_addr_new, end_addr_new);
203  
204  	return rc;
205  }
206  #endif /* CMD_SAVEENV */
207  
208  #else /* ! CONFIG_ENV_ADDR_REDUND */
209  
210  int env_init(void)
211  {
212  	if (crc32(0, env_ptr->data, ENV_SIZE) == env_ptr->crc) {
213  		gd->env_addr	= (ulong)&(env_ptr->data);
214  		gd->env_valid	= ENV_VALID;
215  		return 0;
216  	}
217  
218  	gd->env_addr	= (ulong)&default_environment[0];
219  	gd->env_valid	= 0;
220  	return 0;
221  }
222  
223  #ifdef CMD_SAVEENV
224  int saveenv(void)
225  {
226  	env_t	env_new;
227  	int	rc = 1;
228  	char	*saved_data = NULL;
229  #if CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE
230  	ulong	up_data = 0;
231  
232  	up_data = end_addr + 1 - ((long)flash_addr + CONFIG_ENV_SIZE);
233  	debug("Data to save 0x%lx\n", up_data);
234  	if (up_data) {
235  		saved_data = malloc(up_data);
236  		if (saved_data == NULL) {
237  			printf("Unable to save the rest of sector (%ld)\n",
238  				up_data);
239  			goto done;
240  		}
241  		memcpy(saved_data,
242  			(void *)((long)flash_addr + CONFIG_ENV_SIZE), up_data);
243  		debug("Data (start 0x%lx, len 0x%lx) saved at 0x%lx\n",
244  			(ulong)flash_addr + CONFIG_ENV_SIZE,
245  			up_data,
246  			(ulong)saved_data);
247  	}
248  #endif	/* CONFIG_ENV_SECT_SIZE */
249  
250  	debug("Protect off %08lX ... %08lX\n", (ulong)flash_addr, end_addr);
251  
252  	if (flash_sect_protect(0, (long)flash_addr, end_addr))
253  		goto done;
254  
255  	rc = env_export(&env_new);
256  	if (rc)
257  		goto done;
258  
259  	puts("Erasing Flash...");
260  	if (flash_sect_erase((long)flash_addr, end_addr))
261  		goto done;
262  
263  	puts("Writing to Flash... ");
264  	rc = flash_write((char *)&env_new, (long)flash_addr, CONFIG_ENV_SIZE);
265  	if (rc != 0)
266  		goto perror;
267  
268  #if CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE
269  	if (up_data) {	/* restore the rest of sector */
270  		debug("Restoring the rest of data to 0x%lx len 0x%lx\n",
271  			(ulong)flash_addr + CONFIG_ENV_SIZE, up_data);
272  		if (flash_write(saved_data,
273  				(long)flash_addr + CONFIG_ENV_SIZE,
274  				up_data))
275  			goto perror;
276  	}
277  #endif
278  	puts("done\n");
279  	rc = 0;
280  	goto done;
281  perror:
282  	flash_perror(rc);
283  done:
284  	if (saved_data)
285  		free(saved_data);
286  	/* try to re-protect */
287  	flash_sect_protect(1, (long)flash_addr, end_addr);
288  	return rc;
289  }
290  #endif /* CMD_SAVEENV */
291  
292  #endif /* CONFIG_ENV_ADDR_REDUND */
293  
294  void env_relocate_spec(void)
295  {
296  #ifdef CONFIG_ENV_ADDR_REDUND
297  	if (gd->env_addr != (ulong)&(flash_addr->data)) {
298  		env_t *etmp = flash_addr;
299  		ulong ltmp = end_addr;
300  
301  		flash_addr = flash_addr_new;
302  		flash_addr_new = etmp;
303  
304  		end_addr = end_addr_new;
305  		end_addr_new = ltmp;
306  	}
307  
308  	if (flash_addr_new->flags != OBSOLETE_FLAG &&
309  	    crc32(0, flash_addr_new->data, ENV_SIZE) == flash_addr_new->crc) {
310  		char flag = OBSOLETE_FLAG;
311  
312  		gd->env_valid = ENV_REDUND;
313  		flash_sect_protect(0, (ulong)flash_addr_new, end_addr_new);
314  		flash_write(&flag,
315  			    (ulong)&(flash_addr_new->flags),
316  			    sizeof(flash_addr_new->flags));
317  		flash_sect_protect(1, (ulong)flash_addr_new, end_addr_new);
318  	}
319  
320  	if (flash_addr->flags != ACTIVE_FLAG &&
321  	    (flash_addr->flags & ACTIVE_FLAG) == ACTIVE_FLAG) {
322  		char flag = ACTIVE_FLAG;
323  
324  		gd->env_valid = ENV_REDUND;
325  		flash_sect_protect(0, (ulong)flash_addr, end_addr);
326  		flash_write(&flag,
327  			    (ulong)&(flash_addr->flags),
328  			    sizeof(flash_addr->flags));
329  		flash_sect_protect(1, (ulong)flash_addr, end_addr);
330  	}
331  
332  	if (gd->env_valid == ENV_REDUND)
333  		puts("*** Warning - some problems detected "
334  		     "reading environment; recovered successfully\n\n");
335  #endif /* CONFIG_ENV_ADDR_REDUND */
336  
337  	env_import((char *)flash_addr, 1);
338  }
339