env: allow to export only selected variables
[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
185 #ifdef CONFIG_ENV_OFFSET_REDUND
186 static unsigned char env_flags;
187
188 int saveenv(void)
189 {
190         env_t   env_new;
191         ssize_t len;
192         char    *res;
193         int     ret = 0;
194         nand_erase_options_t nand_erase_options;
195
196         memset(&nand_erase_options, 0, sizeof(nand_erase_options));
197         nand_erase_options.length = CONFIG_ENV_RANGE;
198
199         if (CONFIG_ENV_RANGE < CONFIG_ENV_SIZE)
200                 return 1;
201
202         res = (char *)&env_new.data;
203         len = hexport_r(&env_htab, '\0', &res, ENV_SIZE, 0, NULL);
204         if (len < 0) {
205                 error("Cannot export environment: errno = %d\n", errno);
206                 return 1;
207         }
208         env_new.crc   = crc32(0, env_new.data, ENV_SIZE);
209         env_new.flags = ++env_flags; /* increase the serial */
210
211         if(gd->env_valid == 1) {
212                 puts("Erasing redundant NAND...\n");
213                 nand_erase_options.offset = CONFIG_ENV_OFFSET_REDUND;
214                 if (nand_erase_opts(&nand_info[0], &nand_erase_options))
215                         return 1;
216
217                 puts("Writing to redundant NAND... ");
218                 ret = writeenv(CONFIG_ENV_OFFSET_REDUND,
219                         (u_char *)&env_new);
220         } else {
221                 puts("Erasing NAND...\n");
222                 nand_erase_options.offset = CONFIG_ENV_OFFSET;
223                 if (nand_erase_opts(&nand_info[0], &nand_erase_options))
224                         return 1;
225
226                 puts("Writing to NAND... ");
227                 ret = writeenv(CONFIG_ENV_OFFSET,
228                         (u_char *)&env_new);
229         }
230         if (ret) {
231                 puts("FAILED!\n");
232                 return 1;
233         }
234
235         puts("done\n");
236
237         gd->env_valid = (gd->env_valid == 2 ? 1 : 2);
238
239         return ret;
240 }
241 #else /* ! CONFIG_ENV_OFFSET_REDUND */
242 int saveenv(void)
243 {
244         int ret = 0;
245         env_t   env_new;
246         ssize_t len;
247         char    *res;
248         nand_erase_options_t nand_erase_options;
249
250         memset(&nand_erase_options, 0, sizeof(nand_erase_options));
251         nand_erase_options.length = CONFIG_ENV_RANGE;
252         nand_erase_options.offset = CONFIG_ENV_OFFSET;
253
254         if (CONFIG_ENV_RANGE < CONFIG_ENV_SIZE)
255                 return 1;
256
257         res = (char *)&env_new.data;
258         len = hexport_r(&env_htab, '\0', &res, ENV_SIZE, 0, NULL);
259         if (len < 0) {
260                 error("Cannot export environment: errno = %d\n", errno);
261                 return 1;
262         }
263         env_new.crc   = crc32(0, env_new.data, ENV_SIZE);
264
265         puts("Erasing Nand...\n");
266         if (nand_erase_opts(&nand_info[0], &nand_erase_options))
267                 return 1;
268
269         puts("Writing to Nand... ");
270         if (writeenv(CONFIG_ENV_OFFSET, (u_char *)&env_new)) {
271                 puts("FAILED!\n");
272                 return 1;
273         }
274
275         puts("done\n");
276         return ret;
277 }
278 #endif /* CONFIG_ENV_OFFSET_REDUND */
279 #endif /* CMD_SAVEENV */
280
281 int readenv(size_t offset, u_char * buf)
282 {
283         size_t end = offset + CONFIG_ENV_RANGE;
284         size_t amount_loaded = 0;
285         size_t blocksize, len;
286
287         u_char *char_ptr;
288
289         blocksize = nand_info[0].erasesize;
290         if (!blocksize)
291                 return 1;
292         len = min(blocksize, CONFIG_ENV_SIZE);
293
294         while (amount_loaded < CONFIG_ENV_SIZE && offset < end) {
295                 if (nand_block_isbad(&nand_info[0], offset)) {
296                         offset += blocksize;
297                 } else {
298                         char_ptr = &buf[amount_loaded];
299                         if (nand_read_skip_bad(&nand_info[0], offset, &len, char_ptr))
300                                 return 1;
301                         offset += blocksize;
302                         amount_loaded += len;
303                 }
304         }
305         if (amount_loaded != CONFIG_ENV_SIZE)
306                 return 1;
307
308         return 0;
309 }
310
311 #ifdef CONFIG_ENV_OFFSET_OOB
312 int get_nand_env_oob(nand_info_t *nand, unsigned long *result)
313 {
314         struct mtd_oob_ops ops;
315         uint32_t oob_buf[ENV_OFFSET_SIZE/sizeof(uint32_t)];
316         int ret;
317
318         ops.datbuf = NULL;
319         ops.mode = MTD_OOB_AUTO;
320         ops.ooboffs = 0;
321         ops.ooblen = ENV_OFFSET_SIZE;
322         ops.oobbuf = (void *) oob_buf;
323
324         ret = nand->read_oob(nand, ENV_OFFSET_SIZE, &ops);
325         if (ret) {
326                 printf("error reading OOB block 0\n");
327                 return ret;
328         }
329
330         if (oob_buf[0] == ENV_OOB_MARKER) {
331                 *result = oob_buf[1] * nand->erasesize;
332         } else if (oob_buf[0] == ENV_OOB_MARKER_OLD) {
333                 *result = oob_buf[1];
334         } else {
335                 printf("No dynamic environment marker in OOB block 0\n");
336                 return -ENOENT;
337         }
338
339         return 0;
340 }
341 #endif
342
343 #ifdef CONFIG_ENV_OFFSET_REDUND
344 void env_relocate_spec(void)
345 {
346 #if !defined(ENV_IS_EMBEDDED)
347         int crc1_ok = 0, crc2_ok = 0;
348         env_t *ep, *tmp_env1, *tmp_env2;
349
350         tmp_env1 = (env_t *)malloc(CONFIG_ENV_SIZE);
351         tmp_env2 = (env_t *)malloc(CONFIG_ENV_SIZE);
352
353         if ((tmp_env1 == NULL) || (tmp_env2 == NULL)) {
354                 puts("Can't allocate buffers for environment\n");
355                 free(tmp_env1);
356                 free(tmp_env2);
357                 set_default_env("!malloc() failed");
358                 return;
359         }
360
361         if (readenv(CONFIG_ENV_OFFSET, (u_char *) tmp_env1))
362                 puts("No Valid Environment Area found\n");
363
364         if (readenv(CONFIG_ENV_OFFSET_REDUND, (u_char *) tmp_env2))
365                 puts("No Valid Redundant Environment Area found\n");
366
367         crc1_ok = (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc);
368         crc2_ok = (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc);
369
370         if (!crc1_ok && !crc2_ok) {
371                 free(tmp_env1);
372                 free(tmp_env2);
373                 set_default_env("!bad CRC");
374                 return;
375         } else if (crc1_ok && !crc2_ok) {
376                 gd->env_valid = 1;
377         } else if (!crc1_ok && crc2_ok) {
378                 gd->env_valid = 2;
379         } else {
380                 /* both ok - check serial */
381                 if (tmp_env1->flags == 255 && tmp_env2->flags == 0)
382                         gd->env_valid = 2;
383                 else if (tmp_env2->flags == 255 && tmp_env1->flags == 0)
384                         gd->env_valid = 1;
385                 else if (tmp_env1->flags > tmp_env2->flags)
386                         gd->env_valid = 1;
387                 else if (tmp_env2->flags > tmp_env1->flags)
388                         gd->env_valid = 2;
389                 else /* flags are equal - almost impossible */
390                         gd->env_valid = 1;
391
392         }
393
394         free(env_ptr);
395
396         if (gd->env_valid == 1)
397                 ep = tmp_env1;
398         else
399                 ep = tmp_env2;
400
401         env_flags = ep->flags;
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 */