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