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