hashtable: drop all non-reentrant versions
[platform/kernel/u-boot.git] / common / env_nand.c
1 /*
2  * (C) Copyright 2000-2010
3  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
4  *
5  * (C) Copyright 2008
6  * Stuart Wood, Lab X Technologies <stuart.wood@labxtechnologies.com>
7  *
8  * (C) Copyright 2004
9  * Jian Zhang, Texas Instruments, jzhang@ti.com.
10  *
11  * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
12  * Andreas Heppel <aheppel@sysgo.de>
13  *
14  * See file CREDITS for list of people who contributed to this
15  * project.
16  *
17  * This program is free software; you can redistribute it and/or
18  * modify it under the terms of the GNU General Public License as
19  * published by the Free Software Foundation; either version 2 of
20  * the License, or (at your option) any later version.
21  *
22  * This program is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25  * GNU General Public License for more details.
26  *
27  * You should have received a copy of the GNU General Public License
28  * along with this program; if not, write to the Free Software
29  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
30  * MA 02111-1307 USA
31  */
32
33 #define DEBUG
34
35 #include <common.h>
36 #include <command.h>
37 #include <environment.h>
38 #include <linux/stddef.h>
39 #include <malloc.h>
40 #include <nand.h>
41 #include <search.h>
42 #include <errno.h>
43
44 #if defined(CONFIG_CMD_SAVEENV) && defined(CONFIG_CMD_NAND)
45 #define CMD_SAVEENV
46 #elif defined(CONFIG_ENV_OFFSET_REDUND)
47 #error Cannot use CONFIG_ENV_OFFSET_REDUND without CONFIG_CMD_SAVEENV & CONFIG_CMD_NAND
48 #endif
49
50 #if defined(CONFIG_ENV_SIZE_REDUND) && (CONFIG_ENV_SIZE_REDUND != CONFIG_ENV_SIZE)
51 #error CONFIG_ENV_SIZE_REDUND should be the same as CONFIG_ENV_SIZE
52 #endif
53
54 #ifndef CONFIG_ENV_RANGE
55 #define CONFIG_ENV_RANGE        CONFIG_ENV_SIZE
56 #endif
57
58 /* references to names in env_common.c */
59 extern uchar default_environment[];
60
61 char *env_name_spec = "NAND";
62
63
64 #if defined(ENV_IS_EMBEDDED)
65 extern uchar environment[];
66 env_t *env_ptr = (env_t *)(&environment[0]);
67 #elif defined(CONFIG_NAND_ENV_DST)
68 env_t *env_ptr = (env_t *)CONFIG_NAND_ENV_DST;
69 #else /* ! ENV_IS_EMBEDDED */
70 env_t *env_ptr = 0;
71 #endif /* ENV_IS_EMBEDDED */
72
73 DECLARE_GLOBAL_DATA_PTR;
74
75 uchar env_get_char_spec (int index)
76 {
77         return ( *((uchar *)(gd->env_addr + index)) );
78 }
79
80 /*
81  * This is called before nand_init() so we can't read NAND to
82  * validate env data.
83  *
84  * Mark it OK for now. env_relocate() in env_common.c will call our
85  * relocate function which does the real validation.
86  *
87  * When using a NAND boot image (like sequoia_nand), the environment
88  * can be embedded or attached to the U-Boot image in NAND flash.
89  * This way the SPL loads not only the U-Boot image from NAND but
90  * also the environment.
91  */
92 int env_init(void)
93 {
94 #if defined(ENV_IS_EMBEDDED) || defined(CONFIG_NAND_ENV_DST)
95         int crc1_ok = 0, crc2_ok = 0;
96         env_t *tmp_env1;
97
98 #ifdef CONFIG_ENV_OFFSET_REDUND
99         env_t *tmp_env2;
100
101         tmp_env2 = (env_t *)((ulong)env_ptr + CONFIG_ENV_SIZE);
102         crc2_ok = (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc);
103 #endif
104
105         tmp_env1 = env_ptr;
106
107         crc1_ok = (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc);
108
109         if (!crc1_ok && !crc2_ok) {
110                 gd->env_addr  = 0;
111                 gd->env_valid = 0;
112
113                 return 0;
114         } else if (crc1_ok && !crc2_ok) {
115                 gd->env_valid = 1;
116         }
117 #ifdef CONFIG_ENV_OFFSET_REDUND
118         else if (!crc1_ok && crc2_ok) {
119                 gd->env_valid = 2;
120         } else {
121                 /* both ok - check serial */
122                 if(tmp_env1->flags == 255 && tmp_env2->flags == 0)
123                         gd->env_valid = 2;
124                 else if(tmp_env2->flags == 255 && tmp_env1->flags == 0)
125                         gd->env_valid = 1;
126                 else if(tmp_env1->flags > tmp_env2->flags)
127                         gd->env_valid = 1;
128                 else if(tmp_env2->flags > tmp_env1->flags)
129                         gd->env_valid = 2;
130                 else /* flags are equal - almost impossible */
131                         gd->env_valid = 1;
132         }
133
134         if (gd->env_valid == 2)
135                 env_ptr = tmp_env2;
136         else
137 #endif
138         if (gd->env_valid == 1)
139                 env_ptr = tmp_env1;
140
141         gd->env_addr = (ulong)env_ptr->data;
142
143 #else /* ENV_IS_EMBEDDED || CONFIG_NAND_ENV_DST */
144         gd->env_addr  = (ulong)&default_environment[0];
145         gd->env_valid = 1;
146 #endif /* ENV_IS_EMBEDDED || CONFIG_NAND_ENV_DST */
147
148         return (0);
149 }
150
151 #ifdef CMD_SAVEENV
152 /*
153  * The legacy NAND code saved the environment in the first NAND device i.e.,
154  * nand_dev_desc + 0. This is also the behaviour using the new NAND code.
155  */
156 int writeenv(size_t offset, u_char *buf)
157 {
158         size_t end = offset + CONFIG_ENV_RANGE;
159         size_t amount_saved = 0;
160         size_t blocksize, len;
161
162         u_char *char_ptr;
163
164         blocksize = nand_info[0].erasesize;
165         len = min(blocksize, CONFIG_ENV_SIZE);
166
167         while (amount_saved < CONFIG_ENV_SIZE && offset < end) {
168                 if (nand_block_isbad(&nand_info[0], offset)) {
169                         offset += blocksize;
170                 } else {
171                         char_ptr = &buf[amount_saved];
172                         if (nand_write(&nand_info[0], offset, &len,
173                                         char_ptr))
174                                 return 1;
175                         offset += blocksize;
176                         amount_saved += len;
177                 }
178         }
179         if (amount_saved != CONFIG_ENV_SIZE)
180                 return 1;
181
182         return 0;
183 }
184 #ifdef CONFIG_ENV_OFFSET_REDUND
185 int saveenv(void)
186 {
187         env_t   env_new;
188         ssize_t len;
189         char    *res;
190         int     ret = 0;
191         nand_erase_options_t nand_erase_options;
192
193         nand_erase_options.length = CONFIG_ENV_RANGE;
194         nand_erase_options.quiet = 0;
195         nand_erase_options.jffs2 = 0;
196         nand_erase_options.scrub = 0;
197
198         if (CONFIG_ENV_RANGE < CONFIG_ENV_SIZE)
199                 return 1;
200
201         res = (char *)&env_new.data;
202         len = hexport_r(&env_htab, '\0', &res, ENV_SIZE);
203         if (len < 0) {
204                 error("Cannot export environment: errno = %d\n", errno);
205                 return 1;
206         }
207         env_new.crc   = crc32(0, env_new.data, ENV_SIZE);
208         env_new.flags = ACTIVE_FLAG;
209
210         if(gd->env_valid == 1) {
211                 puts("Erasing redundant NAND...\n");
212                 nand_erase_options.offset = CONFIG_ENV_OFFSET_REDUND;
213                 if (nand_erase_opts(&nand_info[0], &nand_erase_options))
214                         return 1;
215
216                 puts("Writing to redundant NAND... ");
217                 ret = writeenv(CONFIG_ENV_OFFSET_REDUND,
218                         (u_char *)&env_new);
219         } else {
220                 puts("Erasing NAND...\n");
221                 nand_erase_options.offset = CONFIG_ENV_OFFSET;
222                 if (nand_erase_opts(&nand_info[0], &nand_erase_options))
223                         return 1;
224
225                 puts("Writing to NAND... ");
226                 ret = writeenv(CONFIG_ENV_OFFSET,
227                         (u_char *)&env_new);
228         }
229         if (ret) {
230                 puts("FAILED!\n");
231                 return 1;
232         }
233
234         puts("done\n");
235
236         gd->env_valid = (gd->env_valid == 2 ? 1 : 2);
237
238         return ret;
239 }
240 #else /* ! CONFIG_ENV_OFFSET_REDUND */
241 int saveenv(void)
242 {
243         int ret = 0;
244         env_t   env_new;
245         ssize_t len;
246         char    *res;
247         nand_erase_options_t nand_erase_options;
248
249         nand_erase_options.length = CONFIG_ENV_RANGE;
250         nand_erase_options.quiet = 0;
251         nand_erase_options.jffs2 = 0;
252         nand_erase_options.scrub = 0;
253         nand_erase_options.offset = CONFIG_ENV_OFFSET;
254
255         if (CONFIG_ENV_RANGE < CONFIG_ENV_SIZE)
256                 return 1;
257
258         res = (char *)&env_new.data;
259         len = hexport_r(&env_htab, '\0', &res, ENV_SIZE);
260         if (len < 0) {
261                 error("Cannot export environment: errno = %d\n", errno);
262                 return 1;
263         }
264         env_new.crc   = crc32(0, env_new.data, ENV_SIZE);
265
266         puts("Erasing Nand...\n");
267         if (nand_erase_opts(&nand_info[0], &nand_erase_options))
268                 return 1;
269
270         puts("Writing to Nand... ");
271         if (writeenv(CONFIG_ENV_OFFSET, (u_char *)&env_new)) {
272                 puts("FAILED!\n");
273                 return 1;
274         }
275
276         puts("done\n");
277         return ret;
278 }
279 #endif /* CONFIG_ENV_OFFSET_REDUND */
280 #endif /* CMD_SAVEENV */
281
282 int readenv(size_t offset, u_char * buf)
283 {
284         size_t end = offset + CONFIG_ENV_RANGE;
285         size_t amount_loaded = 0;
286         size_t blocksize, len;
287
288         u_char *char_ptr;
289
290         blocksize = nand_info[0].erasesize;
291         if (!blocksize)
292                 return 1;
293         len = min(blocksize, CONFIG_ENV_SIZE);
294
295         while (amount_loaded < CONFIG_ENV_SIZE && offset < end) {
296                 if (nand_block_isbad(&nand_info[0], offset)) {
297                         offset += blocksize;
298                 } else {
299                         char_ptr = &buf[amount_loaded];
300                         if (nand_read_skip_bad(&nand_info[0], offset, &len, char_ptr))
301                                 return 1;
302                         offset += blocksize;
303                         amount_loaded += len;
304                 }
305         }
306         if (amount_loaded != CONFIG_ENV_SIZE)
307                 return 1;
308
309         return 0;
310 }
311
312 #ifdef CONFIG_ENV_OFFSET_OOB
313 int get_nand_env_oob(nand_info_t *nand, unsigned long *result)
314 {
315         struct mtd_oob_ops ops;
316         uint32_t oob_buf[ENV_OFFSET_SIZE/sizeof(uint32_t)];
317         int ret;
318
319         ops.datbuf = NULL;
320         ops.mode = MTD_OOB_AUTO;
321         ops.ooboffs = 0;
322         ops.ooblen = ENV_OFFSET_SIZE;
323         ops.oobbuf = (void *) oob_buf;
324
325         ret = nand->read_oob(nand, ENV_OFFSET_SIZE, &ops);
326         if (ret) {
327                 printf("error reading OOB block 0\n");
328                 return ret;
329         }
330
331         if (oob_buf[0] == ENV_OOB_MARKER) {
332                 *result = oob_buf[1] * nand->erasesize;
333         } else if (oob_buf[0] == ENV_OOB_MARKER_OLD) {
334                 *result = oob_buf[1];
335         } else {
336                 printf("No dynamic environment marker in OOB block 0\n");
337                 return -ENOENT;
338         }
339
340         return 0;
341 }
342 #endif
343
344 #ifdef CONFIG_ENV_OFFSET_REDUND
345 void env_relocate_spec(void)
346 {
347 #if !defined(ENV_IS_EMBEDDED)
348         int crc1_ok = 0, crc2_ok = 0;
349         env_t *ep, *tmp_env1, *tmp_env2;
350
351         tmp_env1 = (env_t *)malloc(CONFIG_ENV_SIZE);
352         tmp_env2 = (env_t *)malloc(CONFIG_ENV_SIZE);
353
354         if ((tmp_env1 == NULL) || (tmp_env2 == NULL)) {
355                 puts("Can't allocate buffers for environment\n");
356                 free(tmp_env1);
357                 free(tmp_env2);
358                 set_default_env("!malloc() failed");
359                 return;
360         }
361
362         if (readenv(CONFIG_ENV_OFFSET, (u_char *) tmp_env1))
363                 puts("No Valid Environment Area found\n");
364
365         if (readenv(CONFIG_ENV_OFFSET_REDUND, (u_char *) tmp_env2))
366                 puts("No Valid Redundant Environment Area found\n");
367
368         crc1_ok = (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc);
369         crc2_ok = (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc);
370
371         if (!crc1_ok && !crc2_ok) {
372                 free(tmp_env1);
373                 free(tmp_env2);
374                 set_default_env("!bad CRC");
375                 return;
376         } else if (crc1_ok && !crc2_ok) {
377                 gd->env_valid = 1;
378         } else if (!crc1_ok && crc2_ok) {
379                 gd->env_valid = 2;
380         } else {
381                 /* both ok - check serial */
382                 if (tmp_env1->flags == 255 && tmp_env2->flags == 0)
383                         gd->env_valid = 2;
384                 else if (tmp_env2->flags == 255 && tmp_env1->flags == 0)
385                         gd->env_valid = 1;
386                 else if (tmp_env1->flags > tmp_env2->flags)
387                         gd->env_valid = 1;
388                 else if (tmp_env2->flags > tmp_env1->flags)
389                         gd->env_valid = 2;
390                 else /* flags are equal - almost impossible */
391                         gd->env_valid = 1;
392
393         }
394
395         free(env_ptr);
396
397         if (gd->env_valid == 1)
398                 ep = tmp_env1;
399         else
400                 ep = tmp_env2;
401
402         env_import((char *)ep, 0);
403
404         free(tmp_env1);
405         free(tmp_env2);
406
407 #endif /* ! ENV_IS_EMBEDDED */
408 }
409 #else /* ! CONFIG_ENV_OFFSET_REDUND */
410 /*
411  * The legacy NAND code saved the environment in the first NAND
412  * device i.e., nand_dev_desc + 0. This is also the behaviour using
413  * the new NAND code.
414  */
415 void env_relocate_spec (void)
416 {
417 #if !defined(ENV_IS_EMBEDDED)
418         int ret;
419         char buf[CONFIG_ENV_SIZE];
420
421 #if defined(CONFIG_ENV_OFFSET_OOB)
422         ret = get_nand_env_oob(&nand_info[0], &nand_env_oob_offset);
423         /*
424          * If unable to read environment offset from NAND OOB then fall through
425          * to the normal environment reading code below
426          */
427         if (!ret) {
428                 printf("Found Environment offset in OOB..\n");
429         } else {
430                 set_default_env("!no env offset in OOB");
431                 return;
432         }
433 #endif
434
435         ret = readenv(CONFIG_ENV_OFFSET, (u_char *)buf);
436         if (ret) {
437                 set_default_env("!readenv() failed");
438                 return;
439         }
440
441         env_import(buf, 1);
442 #endif /* ! ENV_IS_EMBEDDED */
443 }
444 #endif /* CONFIG_ENV_OFFSET_REDUND */