xref: /rk3399_rockchip-uboot/env/envf.c (revision 2433663c664fe89961507844c5545fc1fcd9307a)
1 /*
2  * Copyright (c) 2022 Rockchip Electronics Co., Ltd
3  *
4  * SPDX-License-Identifier:     GPL-2.0+
5  */
6 
7 #include <common.h>
8 #include <boot_rkimg.h>
9 #include <environment.h>
10 #include <memalign.h>
11 #include <part.h>
12 
13 DECLARE_GLOBAL_DATA_PTR;
14 
15 #define ENVF_MSG(fmt, args...)	printf("ENVF: "fmt, ##args)
16 #define BLK_CNT(desc, sz)	((sz) / (desc)->blksz)
17 #define ENVPARAM_SIZE		SZ_4K
18 
19 static ulong env_offset, env_offset_redund;
20 static ulong env_size;
21 static u32 envparam_addr_common[]  = { 0, SZ_4K };
22 static u32 envparam_addr_spinand[] = { 0, SZ_256K };
23 
24 #ifdef CONFIG_SPL_BUILD
25 #ifdef CONFIG_SPL_ENV_PARTITION
26 /*
27  * In case of env and env-backup partitions are too large that exceeds the limit
28  * of CONFIG_SPL_SYS_MALLOC_F_LEN. we prefer to use a static address as an env
29  * buffer. The tail of bss section is good choice from experience.
30  */
31 static void *env_buffer =
32 	(void *)CONFIG_SPL_BSS_START_ADDR + CONFIG_SPL_BSS_MAX_SIZE;
33 
34 static const char *get_strval(env_t *env, u32 size, const char *str)
35 {
36 	const char *dp;
37 	u32 env_size;
38 
39 	dp = (const char *)env->data;
40 	env_size = size - ENV_HEADER_SIZE;
41 	do {
42 		/* skip leading white space */
43 		while (*dp == ' ' || *dp == '\t')
44 			++dp;
45 
46 		debug("ENTRY: %s\n", dp);
47 		if (strstr(dp, str)) {
48 			debug("FIND: %s\n", dp);
49 			return dp;
50 		}
51 
52 		/* point to next ENTRY */
53 		dp += strlen(dp) + 1;
54 	} while (((ulong)dp < (ulong)env->data + env_size) && *dp);
55 
56 	debug("NOT-FIND: %s\n", str);
57 
58 	return NULL;
59 }
60 
61 static ulong get_strval_ulong(env_t *env, u32 size,
62 			      const char *str, ulong default_val)
63 {
64 	const char *p;
65 
66 	p = get_strval(env, size, str);
67 	if (!p)
68 		return default_val;
69 	p = strstr(p, "=");
70 	if (!p)
71 		return default_val;
72 
73 	++p; /* skip '=' */
74 
75 	return simple_strtoul(p, NULL, 16);
76 }
77 
78 static int env_do_load(struct blk_desc *desc, u32 offset, u32 size,
79 		       void *buf, void **envp)
80 {
81 	lbaint_t env_size;
82 	lbaint_t env_off;
83 	env_t *env = buf;
84 	u32 blk_cnt;
85 
86 	env_size = size - ENV_HEADER_SIZE;
87 	env_off = BLK_CNT(desc, offset);
88 	blk_cnt = BLK_CNT(desc, size);
89 
90 	if (blk_dread(desc, env_off, blk_cnt, (void *)env) != blk_cnt) {
91 		ENVF_MSG("io error @ 0x%x\n", offset);
92 		return -EIO;
93 	}
94 
95 	if (crc32(0, env->data, env_size) != env->crc) {
96 		ENVF_MSG("!bad CRC @ 0x%x\n", offset);
97 		return -EINVAL;
98 	}
99 
100 	if (envp)
101 		*envp = env;
102 
103 	return 0;
104 }
105 
106 int envf_load(struct blk_desc *desc)
107 {
108 	void *env, *envp0, *envp1;
109 	const char *part_list;
110 	int read0_fail, read1_fail;
111 	int envp_blks;
112 	int ret = -ENOENT;
113 	u32 *addr;
114 
115 	if (!desc)
116 		return -ENODEV;
117 
118 	/* Import envparam */
119 	if (desc->if_type == IF_TYPE_MTD && desc->devnum == BLK_MTD_SPI_NAND)
120 		addr = envparam_addr_spinand;
121 	else
122 		addr = envparam_addr_common;
123 
124 	envp_blks = BLK_CNT(desc, ENVPARAM_SIZE);
125 	read0_fail = env_do_load(desc, addr[0], ENVPARAM_SIZE,
126 				 env_buffer, &envp0);
127 	read1_fail = env_do_load(desc, addr[1], ENVPARAM_SIZE,
128 				 env_buffer + ENVPARAM_SIZE, &envp1);
129 	if (read0_fail && read1_fail) {
130 		ENVF_MSG("No valid envparam!\n");
131 	} else if (!read0_fail && read1_fail) {
132 		env = envp0;
133 		ret = blk_dwrite(desc, BLK_CNT(desc, addr[1]),
134 				 BLK_CNT(desc, ENVPARAM_SIZE), envp0);
135 		ENVF_MSG("Repair backup envparam %s\n",
136 			 ret == envp_blks ? "ok" : "fail");
137 	} else if (read0_fail && !read1_fail) {
138 		env = envp1;
139 		ret = blk_dwrite(desc, BLK_CNT(desc, addr[0]),
140 				 BLK_CNT(desc, ENVPARAM_SIZE), envp1);
141 		ENVF_MSG("Repair primary envparam %s\n",
142 			 ret == envp_blks ?  "ok" : "fail");
143 	} else {
144 		env = envp0; /* both ok, use primary envparam */
145 	}
146 
147 	/* Import env data */
148 	env_size = get_strval_ulong(env, ENVPARAM_SIZE, "ENV_SIZE", 0);
149 	env_offset = get_strval_ulong(env, ENVPARAM_SIZE, "ENV_OFFSET", 0);
150 	env_offset_redund = get_strval_ulong(env, ENVPARAM_SIZE, "ENV_OFFSET_REDUND", 0);
151 	if (!env_offset || !env_size) {
152 		ENVF_MSG("Using default\n");
153 		env_offset = CONFIG_ENV_OFFSET;
154 		env_size = CONFIG_ENV_SIZE;
155 		env_offset_redund = CONFIG_ENV_OFFSET_REDUND;
156 	}
157 	if (env_offset == env_offset_redund)
158 		env_offset_redund = 0;
159 
160 	ENVF_MSG("Primary 0x%08lx - 0x%08lx\n", env_offset, env_offset + env_size);
161 	if (env_offset_redund)
162 		ENVF_MSG("Backup  0x%08lx - 0x%08lx\n",
163 			 env_offset_redund, env_offset_redund + env_size);
164 
165 	ret = env_do_load(desc, env_offset, env_size, env_buffer, &env);
166 	if (ret < 0 && env_offset_redund)
167 		ret = env_do_load(desc, env_offset_redund, env_size,
168 				  env_buffer + env_size, &env);
169 	if (ret < 0) {
170 		ENVF_MSG("No valid env data, ret=%d\n", ret);
171 		return ret;
172 	}
173 
174 	part_list = get_strval(env, env_size, "mtdparts");
175 	if (!part_list)
176 		part_list = get_strval(env, env_size, "blkdevparts");
177 	if (!part_list) {
178 		ENVF_MSG("No valid env part list\n");
179 		return ret;
180 	}
181 
182 	ENVF_MSG("OK\n");
183 	env_part_list_init(part_list);
184 	/*
185 	 * Call blk_dread() to read env content:
186 	 *    => mmc_init() => part_init() with CONFIG_ENV_PARTITION_LIST => mmc init ok
187 	 *    => read env content! including partition tables(blkdevparts/mtdparts).
188 	 *
189 	 * So we must reinit env partition after we setup the partition
190 	 * tables(blkdevparts/mtdparts) from env.
191 	 */
192 	part_init(desc);
193 
194 	return 0;
195 }
196 #endif
197 
198 #else
199 /*
200  * [Example]
201  *  generate envparam:
202  *	./tools/mkenvimage -s 0x1000 -p 0x0 -o envparam.bin envparam.txt
203  *  generate data:
204  *	./tools/mkenvimage -s 0x8000 -p 0x0 -o envf.bin envf.txt
205  */
206 #define ENVF_MAX		64
207 #define ENVF_EMSG		"error: please use \"sys_bootargs\" but not " \
208 				"\"bootargs\" in CONFIG_ENVF_LIST and envf.bin"
209 static const char *envf_list[ENVF_MAX];
210 static u32 envf_num;
211 
212 static int envf_extract_list(void)
213 {
214 	char *tok, *p;
215 	u32 i = 0;
216 
217 	tok = strdup(CONFIG_ENVF_LIST);
218 	if (!tok)
219 		return -ENOMEM;
220 
221 	p = strtok(tok, " ");
222 	while (p && i < ENVF_MAX) {
223 		if (!strcmp(p, "bootargs")) {
224 			printf("%s\n", ENVF_EMSG);
225 			run_command("download", 0);
226 		}
227 		envf_list[i++] = p;
228 		p = strtok(NULL, " ");
229 	}
230 
231 	envf_num = i;
232 
233 	return 0;
234 }
235 
236 static int env_do_load(struct blk_desc *desc, u32 offset, u32 size,
237 		       const char *list[], int num, void **envp)
238 {
239 	lbaint_t env_size;
240 	lbaint_t env_off;
241 	u32 blk_cnt;
242 	int ret = 0;
243 	env_t *env;
244 
245 	env_size = size - ENV_HEADER_SIZE;
246 	env_off = BLK_CNT(desc, offset);
247 	blk_cnt = BLK_CNT(desc, size);
248 
249 	env = memalign(ARCH_DMA_MINALIGN, size);
250 	if (!env)
251 		return -ENOMEM;
252 
253 	if (blk_dread(desc, env_off, blk_cnt, (void *)env) != blk_cnt) {
254 		ret = -EIO;
255 		goto out;
256 	}
257 
258 	if (crc32(0, env->data, env_size) != env->crc) {
259 		ENVF_MSG("!bad CRC @ 0x%x\n", offset);
260 		ret = -EINVAL;
261 		goto out;
262 	}
263 
264 	if (!himport_r(&env_htab, (char *)env->data, env_size, '\0',
265 		       H_NOCLEAR, 0, num, (char * const *)list)) {
266 		ENVF_MSG("himport error: %d\n", errno);
267 		ret = -EINTR;
268 	}
269 
270 	if (envp)
271 		*envp = env;
272 out:
273 	return ret;
274 }
275 
276 static int envf_load(void)
277 {
278 	const char *envparam[] = {
279 		"ENV_OFFSET", "ENV_SIZE", "ENV_OFFSET_REDUND",
280 	};
281 	struct blk_desc *desc;
282 	int read0_fail, read1_fail;
283 	int envp_blks;
284 	void *envp0 = NULL;
285 	void *envp1 = NULL;
286 	int ret = -ENOENT;
287 	u32 *addr;
288 
289 	desc = rockchip_get_bootdev();
290 	if (!desc) {
291 		printf("dev desc null!\n");
292 		return 0;
293 	}
294 
295 	/* Import envparam */
296 	if (desc->if_type == IF_TYPE_MTD && desc->devnum == BLK_MTD_SPI_NAND)
297 		addr = envparam_addr_spinand;
298 	else
299 		addr = envparam_addr_common;
300 
301 	envp_blks = BLK_CNT(desc, ENVPARAM_SIZE);
302 	read0_fail = env_do_load(desc, addr[0], ENVPARAM_SIZE,
303 				 envparam, ARRAY_SIZE(envparam), &envp0);
304 	read1_fail = env_do_load(desc, addr[1], ENVPARAM_SIZE,
305 				 envparam, ARRAY_SIZE(envparam), &envp1);
306 	if (read0_fail && read1_fail) {
307 		ENVF_MSG("No valid envparam!\n");
308 	} else if (!read0_fail && read1_fail) {
309 		ret = blk_dwrite(desc, BLK_CNT(desc, addr[1]),
310 				 BLK_CNT(desc, ENVPARAM_SIZE), envp0);
311 		ENVF_MSG("Repair backup envparam %s\n",
312 			 ret == envp_blks ? "ok" : "fail");
313 	} else if (read0_fail && !read1_fail) {
314 		ret = blk_dwrite(desc, BLK_CNT(desc, addr[0]),
315 				 BLK_CNT(desc, ENVPARAM_SIZE), envp1);
316 		ENVF_MSG("Repair primary envparam %s\n",
317 			 ret == envp_blks ?  "ok" : "fail");
318 	}
319 
320 	if (envp0)
321 		free(envp0);
322 	if (envp1)
323 		free(envp1);
324 
325 	/* Import env data */
326 	env_size = env_get_ulong("ENV_SIZE", 16, 0);
327 	env_offset = env_get_ulong("ENV_OFFSET", 16, 0);
328 	env_offset_redund = env_get_ulong("ENV_OFFSET_REDUND", 16, 0);
329 	if (!env_offset || !env_size) {
330 		ENVF_MSG("Using default\n");
331 		env_offset = CONFIG_ENV_OFFSET;
332 		env_size = CONFIG_ENV_SIZE;
333 		env_offset_redund = CONFIG_ENV_OFFSET_REDUND;
334 	}
335 	if (env_offset == env_offset_redund)
336 		env_offset_redund = 0;
337 
338 	ENVF_MSG("Primary 0x%08lx - 0x%08lx\n", env_offset, env_offset + env_size);
339 	if (env_offset_redund)
340 		ENVF_MSG("Backup  0x%08lx - 0x%08lx\n",
341 			 env_offset_redund, env_offset_redund + env_size);
342 
343 	envf_extract_list();
344 	ret = env_do_load(desc, env_offset, env_size, envf_list, envf_num, NULL);
345 	if (ret < 0 && env_offset_redund) {
346 		ret = env_do_load(desc, env_offset_redund,
347 				  env_size, envf_list, envf_num, NULL);
348 	}
349 	if (ret < 0) {
350 		ENVF_MSG("No valid env data, ret=%d\n", ret);
351 		return ret;
352 	}
353 
354 	ENVF_MSG("OK\n");
355 	printf("  - %s\n", CONFIG_ENVF_LIST);
356 #ifdef CONFIG_ENV_PARTITION
357 	/*
358 	 * Call blk_dread() to read envf content:
359 	 *    => mmc_init() => part_init() with CONFIG_ENV_PARTITION_LIST => mmc init ok
360 	 *    => read envf content! including partition tables(blkdevparts/mtdparts).
361 	 *
362 	 * So we must reinit env partition after we setup the partition
363 	 * tables(blkdevparts/mtdparts) from envf.
364 	 */
365 	part_init(desc);
366 #endif
367 	return 0;
368 }
369 
370 static int envf_save(void)
371 {
372 	ALLOC_CACHE_ALIGN_BUFFER(env_t, env, 1);
373 	struct blk_desc *desc;
374 	u32 blk_cnt;
375 	ssize_t	len;
376 	char *res;
377 	int ret = 0;
378 
379 	desc = rockchip_get_bootdev();
380 	if (!desc) {
381 		printf("dev desc null!\n");
382 		return -EINVAL;
383 	}
384 
385 	res = (char *)env->data;
386 	len = hexport_r(&env_htab, '\0', H_MATCH_KEY | H_MATCH_IDENT,
387 			&res, env_size - ENV_HEADER_SIZE,
388 			envf_num, (char * const *)envf_list);
389 	if (len < 0) {
390 		ENVF_MSG("hexpor error: %d\n", errno);
391 		return -EINVAL;
392 	}
393 
394 	env->crc = crc32(0, env->data, env_size - ENV_HEADER_SIZE);
395 	blk_cnt = BLK_CNT(desc, env_size);
396 	if (blk_dwrite(desc, BLK_CNT(desc, env_offset),
397 		       blk_cnt, (char *)env) != blk_cnt) {
398 		ret = -EIO;
399 		ENVF_MSG("io error\n");
400 	}
401 
402 	if (env_offset_redund) {
403 		if (blk_dwrite(desc, BLK_CNT(desc, env_offset_redund),
404 			       blk_cnt, (char *)env) != blk_cnt)
405 			ENVF_MSG("redundant: io error\n");
406 		else
407 			ret = 0;
408 	}
409 
410 	return ret;
411 }
412 
413 static int envf_nowhere_init(void)
414 {
415 	gd->env_addr	= (ulong)&default_environment[0];
416 	gd->env_valid	= ENV_INVALID;
417 
418 	return 0;
419 }
420 
421 U_BOOT_ENV_LOCATION(nowhere) = {
422 	.location	= ENVL_NOWHERE,
423 	.init		= envf_nowhere_init,
424 	.load		= envf_load,
425 	.save		= env_save_ptr(envf_save),
426 	ENV_NAME("envf")
427 };
428 #endif
429