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