• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 2008
7  * Stuart Wood, Lab X Technologies <stuart.wood@labxtechnologies.com>
8  *
9  * (C) Copyright 2004
10  * Jian Zhang, Texas Instruments, jzhang@ti.com.
11  *
12  * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
13  * Andreas Heppel <aheppel@sysgo.de>
14  */
15 
16 #include <common.h>
17 #include <command.h>
18 #include <env.h>
19 #include <env_internal.h>
20 #include <linux/stddef.h>
21 #include <malloc.h>
22 #include <memalign.h>
23 #include <nand.h>
24 #include <search.h>
25 #include <errno.h>
26 #include <u-boot/crc.h>
27 
28 #if defined(CONFIG_CMD_SAVEENV) && defined(CONFIG_CMD_NAND) && \
29 		!defined(CONFIG_SPL_BUILD)
30 #define CMD_SAVEENV
31 #elif defined(CONFIG_ENV_OFFSET_REDUND) && !defined(CONFIG_SPL_BUILD)
32 #error CONFIG_ENV_OFFSET_REDUND must have CONFIG_CMD_SAVEENV & CONFIG_CMD_NAND
33 #endif
34 
35 #ifndef CONFIG_ENV_RANGE
36 #define CONFIG_ENV_RANGE	CONFIG_ENV_SIZE
37 #endif
38 
39 #if defined(ENV_IS_EMBEDDED)
40 static env_t *env_ptr = &environment;
41 #elif defined(CONFIG_NAND_ENV_DST)
42 static env_t *env_ptr = (env_t *)CONFIG_NAND_ENV_DST;
43 #endif /* ENV_IS_EMBEDDED */
44 
45 DECLARE_GLOBAL_DATA_PTR;
46 
47 /*
48  * This is called before nand_init() so we can't read NAND to
49  * validate env data.
50  *
51  * Mark it OK for now. env_relocate() in env_common.c will call our
52  * relocate function which does the real validation.
53  *
54  * When using a NAND boot image (like sequoia_nand), the environment
55  * can be embedded or attached to the U-Boot image in NAND flash.
56  * This way the SPL loads not only the U-Boot image from NAND but
57  * also the environment.
58  */
env_nand_init(void)59 static int env_nand_init(void)
60 {
61 #if defined(ENV_IS_EMBEDDED) || defined(CONFIG_NAND_ENV_DST)
62 	int crc1_ok = 0, crc2_ok = 0;
63 	env_t *tmp_env1;
64 
65 #ifdef CONFIG_ENV_OFFSET_REDUND
66 	env_t *tmp_env2;
67 
68 	tmp_env2 = (env_t *)((ulong)env_ptr + CONFIG_ENV_SIZE);
69 	crc2_ok = crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc;
70 #endif
71 	tmp_env1 = env_ptr;
72 	crc1_ok = crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc;
73 
74 	if (!crc1_ok && !crc2_ok) {
75 		gd->env_addr	= 0;
76 		gd->env_valid	= ENV_INVALID;
77 
78 		return 0;
79 	} else if (crc1_ok && !crc2_ok) {
80 		gd->env_valid = ENV_VALID;
81 	}
82 #ifdef CONFIG_ENV_OFFSET_REDUND
83 	else if (!crc1_ok && crc2_ok) {
84 		gd->env_valid = ENV_REDUND;
85 	} else {
86 		/* both ok - check serial */
87 		if (tmp_env1->flags == 255 && tmp_env2->flags == 0)
88 			gd->env_valid = ENV_REDUND;
89 		else if (tmp_env2->flags == 255 && tmp_env1->flags == 0)
90 			gd->env_valid = ENV_VALID;
91 		else if (tmp_env1->flags > tmp_env2->flags)
92 			gd->env_valid = ENV_VALID;
93 		else if (tmp_env2->flags > tmp_env1->flags)
94 			gd->env_valid = ENV_REDUND;
95 		else /* flags are equal - almost impossible */
96 			gd->env_valid = ENV_VALID;
97 	}
98 
99 	if (gd->env_valid == ENV_REDUND)
100 		env_ptr = tmp_env2;
101 	else
102 #endif
103 	if (gd->env_valid == ENV_VALID)
104 		env_ptr = tmp_env1;
105 
106 	gd->env_addr = (ulong)env_ptr->data;
107 
108 #else /* ENV_IS_EMBEDDED || CONFIG_NAND_ENV_DST */
109 	gd->env_addr	= (ulong)&default_environment[0];
110 	gd->env_valid	= ENV_VALID;
111 #endif /* ENV_IS_EMBEDDED || CONFIG_NAND_ENV_DST */
112 
113 	return 0;
114 }
115 
116 #ifdef CMD_SAVEENV
117 /*
118  * The legacy NAND code saved the environment in the first NAND device i.e.,
119  * nand_dev_desc + 0. This is also the behaviour using the new NAND code.
120  */
writeenv(size_t offset,u_char * buf)121 static int writeenv(size_t offset, u_char *buf)
122 {
123 	size_t end = offset + CONFIG_ENV_RANGE;
124 	size_t amount_saved = 0;
125 	size_t blocksize, len;
126 	struct mtd_info *mtd;
127 	u_char *char_ptr;
128 
129 	mtd = get_nand_dev_by_index(0);
130 	if (!mtd)
131 		return 1;
132 
133 	blocksize = mtd->erasesize;
134 	len = min(blocksize, (size_t)CONFIG_ENV_SIZE);
135 
136 	while (amount_saved < CONFIG_ENV_SIZE && offset < end) {
137 		if (nand_block_isbad(mtd, offset)) {
138 			offset += blocksize;
139 		} else {
140 			char_ptr = &buf[amount_saved];
141 			if (nand_write(mtd, offset, &len, char_ptr))
142 				return 1;
143 
144 			offset += blocksize;
145 			amount_saved += len;
146 		}
147 	}
148 	if (amount_saved != CONFIG_ENV_SIZE)
149 		return 1;
150 
151 	return 0;
152 }
153 
154 struct nand_env_location {
155 	const char *name;
156 	const nand_erase_options_t erase_opts;
157 };
158 
erase_and_write_env(const struct nand_env_location * location,u_char * env_new)159 static int erase_and_write_env(const struct nand_env_location *location,
160 		u_char *env_new)
161 {
162 	struct mtd_info *mtd;
163 	int ret = 0;
164 
165 	mtd = get_nand_dev_by_index(0);
166 	if (!mtd)
167 		return 1;
168 
169 	printf("Erasing %s...\n", location->name);
170 	if (nand_erase_opts(mtd, &location->erase_opts))
171 		return 1;
172 
173 	printf("Writing to %s... ", location->name);
174 	ret = writeenv(location->erase_opts.offset, env_new);
175 	puts(ret ? "FAILED!\n" : "OK\n");
176 
177 	return ret;
178 }
179 
env_nand_save(void)180 static int env_nand_save(void)
181 {
182 	int	ret = 0;
183 	ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1);
184 	int	env_idx = 0;
185 	static const struct nand_env_location location[] = {
186 		{
187 			.name = "NAND",
188 			.erase_opts = {
189 				.length = CONFIG_ENV_RANGE,
190 				.offset = CONFIG_ENV_OFFSET,
191 			},
192 		},
193 #ifdef CONFIG_ENV_OFFSET_REDUND
194 		{
195 			.name = "redundant NAND",
196 			.erase_opts = {
197 				.length = CONFIG_ENV_RANGE,
198 				.offset = CONFIG_ENV_OFFSET_REDUND,
199 			},
200 		},
201 #endif
202 	};
203 
204 
205 	if (CONFIG_ENV_RANGE < CONFIG_ENV_SIZE)
206 		return 1;
207 
208 	ret = env_export(env_new);
209 	if (ret)
210 		return ret;
211 
212 #ifdef CONFIG_ENV_OFFSET_REDUND
213 	env_idx = (gd->env_valid == ENV_VALID);
214 #endif
215 
216 	ret = erase_and_write_env(&location[env_idx], (u_char *)env_new);
217 #ifdef CONFIG_ENV_OFFSET_REDUND
218 	if (!ret) {
219 		/* preset other copy for next write */
220 		gd->env_valid = gd->env_valid == ENV_REDUND ? ENV_VALID :
221 				ENV_REDUND;
222 		return ret;
223 	}
224 
225 	env_idx = (env_idx + 1) & 1;
226 	ret = erase_and_write_env(&location[env_idx], (u_char *)env_new);
227 	if (!ret)
228 		printf("Warning: primary env write failed,"
229 				" redundancy is lost!\n");
230 #endif
231 
232 	return ret;
233 }
234 #endif /* CMD_SAVEENV */
235 
236 #if defined(CONFIG_SPL_BUILD)
readenv(size_t offset,u_char * buf)237 static int readenv(size_t offset, u_char *buf)
238 {
239 	return nand_spl_load_image(offset, CONFIG_ENV_SIZE, buf);
240 }
241 #else
readenv(size_t offset,u_char * buf)242 static int readenv(size_t offset, u_char *buf)
243 {
244 	size_t end = offset + CONFIG_ENV_RANGE;
245 	size_t amount_loaded = 0;
246 	size_t blocksize, len;
247 	struct mtd_info *mtd;
248 	u_char *char_ptr;
249 
250 	mtd = get_nand_dev_by_index(0);
251 	if (!mtd)
252 		return 1;
253 
254 	blocksize = mtd->erasesize;
255 	len = min(blocksize, (size_t)CONFIG_ENV_SIZE);
256 
257 	while (amount_loaded < CONFIG_ENV_SIZE && offset < end) {
258 		if (nand_block_isbad(mtd, offset)) {
259 			offset += blocksize;
260 		} else {
261 			char_ptr = &buf[amount_loaded];
262 			if (nand_read_skip_bad(mtd, offset,
263 					       &len, NULL,
264 					       mtd->size, char_ptr))
265 				return 1;
266 
267 			offset += blocksize;
268 			amount_loaded += len;
269 		}
270 	}
271 
272 	if (amount_loaded != CONFIG_ENV_SIZE)
273 		return 1;
274 
275 	return 0;
276 }
277 #endif /* #if defined(CONFIG_SPL_BUILD) */
278 
279 #ifdef CONFIG_ENV_OFFSET_OOB
get_nand_env_oob(struct mtd_info * mtd,unsigned long * result)280 int get_nand_env_oob(struct mtd_info *mtd, unsigned long *result)
281 {
282 	struct mtd_oob_ops ops;
283 	uint32_t oob_buf[ENV_OFFSET_SIZE / sizeof(uint32_t)];
284 	int ret;
285 
286 	ops.datbuf	= NULL;
287 	ops.mode	= MTD_OOB_AUTO;
288 	ops.ooboffs	= 0;
289 	ops.ooblen	= ENV_OFFSET_SIZE;
290 	ops.oobbuf	= (void *)oob_buf;
291 
292 	ret = mtd->read_oob(mtd, ENV_OFFSET_SIZE, &ops);
293 	if (ret) {
294 		printf("error reading OOB block 0\n");
295 		return ret;
296 	}
297 
298 	if (oob_buf[0] == ENV_OOB_MARKER) {
299 		*result = ovoid ob_buf[1] * mtd->erasesize;
300 	} else if (oob_buf[0] == ENV_OOB_MARKER_OLD) {
301 		*result = oob_buf[1];
302 	} else {
303 		printf("No dynamic environment marker in OOB block 0\n");
304 		return -ENOENT;
305 	}
306 
307 	return 0;
308 }
309 #endif
310 
311 #ifdef CONFIG_ENV_OFFSET_REDUND
env_nand_load(void)312 static int env_nand_load(void)
313 {
314 #if defined(ENV_IS_EMBEDDED)
315 	return 0;
316 #else
317 	int read1_fail, read2_fail;
318 	env_t *tmp_env1, *tmp_env2;
319 	int ret = 0;
320 
321 	tmp_env1 = (env_t *)malloc(CONFIG_ENV_SIZE);
322 	tmp_env2 = (env_t *)malloc(CONFIG_ENV_SIZE);
323 	if (tmp_env1 == NULL || tmp_env2 == NULL) {
324 		puts("Can't allocate buffers for environment\n");
325 		env_set_default("malloc() failed", 0);
326 		ret = -EIO;
327 		goto done;
328 	}
329 
330 	read1_fail = readenv(CONFIG_ENV_OFFSET, (u_char *) tmp_env1);
331 	read2_fail = readenv(CONFIG_ENV_OFFSET_REDUND, (u_char *) tmp_env2);
332 
333 	ret = env_import_redund((char *)tmp_env1, read1_fail, (char *)tmp_env2,
334 				read2_fail);
335 
336 done:
337 	free(tmp_env1);
338 	free(tmp_env2);
339 
340 	return ret;
341 #endif /* ! ENV_IS_EMBEDDED */
342 }
343 #else /* ! CONFIG_ENV_OFFSET_REDUND */
344 /*
345  * The legacy NAND code saved the environment in the first NAND
346  * device i.e., nand_dev_desc + 0. This is also the behaviour using
347  * the new NAND code.
348  */
env_nand_load(void)349 static int env_nand_load(void)
350 {
351 #if !defined(ENV_IS_EMBEDDED)
352 	int ret;
353 	ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE);
354 
355 #if defined(CONFIG_ENV_OFFSET_OOB)
356 	struct mtd_info *mtd  = get_nand_dev_by_index(0);
357 	/*
358 	 * If unable to read environment offset from NAND OOB then fall through
359 	 * to the normal environment reading code below
360 	 */
361 	if (mtd && !get_nand_env_oob(mtd, &nand_env_oob_offset)) {
362 		printf("Found Environment offset in OOB..\n");
363 	} else {
364 		env_set_default("no env offset in OOB", 0);
365 		return;
366 	}
367 #endif
368 
369 	ret = readenv(CONFIG_ENV_OFFSET, (u_char *)buf);
370 	if (ret) {
371 		env_set_default("readenv() failed", 0);
372 		return -EIO;
373 	}
374 
375 	return env_import(buf, 1);
376 #endif /* ! ENV_IS_EMBEDDED */
377 
378 	return 0;
379 }
380 #endif /* CONFIG_ENV_OFFSET_REDUND */
381 
382 U_BOOT_ENV_LOCATION(nand) = {
383 	.location	= ENVL_NAND,
384 	ENV_NAME("NAND")
385 	.load		= env_nand_load,
386 #if defined(CMD_SAVEENV)
387 	.save		= env_save_ptr(env_nand_save),
388 #endif
389 	.init		= env_nand_init,
390 };
391