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