xref: /rk3399_rockchip-uboot/env/envf.c (revision 2602b1ba1429717f5d33bbe0fe45c601c9a6c923)
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 #include <part_efi.h>
13 #include <asm/unaligned.h>
14 
15 DECLARE_GLOBAL_DATA_PTR;
16 
17 /*
18  * Example: ./tools/mkenvimage -s 0x8000 -p 0x0 -o env.img env.txt
19  */
20 #define ENVF_MSG(fmt, args...)	printf("ENVF: "fmt, ##args)
21 #define ENVF_DBG(fmt, args...)	debug("ENVF: "fmt, ##args)
22 
23 #define EMSG_ARGS		"error: please use \"sys_bootargs\" but not \"bootargs\""
24 #define BLK_CNT(desc, sz)	((sz) / (desc)->blksz)
25 #define ENVF_MAX		64
26 
27 static ulong env_size, env_offset, env_offset_redund;
28 
29 #if CONFIG_IS_ENABLED(ENV_PARTITION)
30 static const char *part_type[] = { "mtdparts", "blkdevparts", };
31 #endif
32 
33 /*
34  * In case of env and env-backup partitions are too large that exceeds the limit
35  * of CONFIG_SPL_SYS_MALLOC_F_LEN. we prefer to use a static address as an env
36  * buffer. The tail of bss section is good choice from experience.
37  */
38 #ifdef CONFIG_SPL_BUILD
39 static void *spl_env =
40 	(void *)CONFIG_SPL_BSS_START_ADDR + CONFIG_SPL_BSS_MAX_SIZE;
41 #else
42 static u32 envf_num;
43 static const char *envf_list[ENVF_MAX];
44 #endif
45 
46 #ifdef CONFIG_DM_MMC
47 static int pmbr_part_valid(struct partition *part)
48 {
49 	if (part->sys_ind == EFI_PMBR_OSTYPE_EFI_GPT &&
50 		get_unaligned_le32(&part->start_sect) == 1UL) {
51 		return 1;
52 	}
53 
54 	return 0;
55 }
56 
57 static int is_pmbr_valid(legacy_mbr * mbr)
58 {
59 	int i = 0;
60 
61 	if (!mbr || le16_to_cpu(mbr->signature) != MSDOS_MBR_SIGNATURE)
62 		return 0;
63 
64 	for (i = 0; i < 4; i++) {
65 		if (pmbr_part_valid(&mbr->partition_record[i])) {
66 			return 1;
67 		}
68 	}
69 	return 0;
70 }
71 
72 static int can_find_pmbr(struct blk_desc *dev_desc)
73 {
74 	ALLOC_CACHE_ALIGN_BUFFER_PAD(legacy_mbr, legacymbr, 1, dev_desc->blksz);
75 
76 	/* Read legacy MBR from block 0 and validate it */
77 	if ((blk_dread(dev_desc, 0, 1, (ulong *)legacymbr) != 1)
78 		|| (is_pmbr_valid(legacymbr) != 1)) {
79 		return -1;
80 	}
81 
82 	return 0;
83 }
84 #endif
85 
86 static void envf_init_location(struct blk_desc *desc)
87 {
88 	/* eMMC (default) */
89 	env_size = CONFIG_ENV_SIZE;
90 	env_offset = CONFIG_ENV_OFFSET;
91 	env_offset_redund = CONFIG_ENV_OFFSET_REDUND;
92 
93 #if defined(CONFIG_MTD_SPI_NAND) || defined(CONFIG_CMD_NAND)
94 	/* nand or spi-nand */
95 	if (desc->if_type == IF_TYPE_MTD &&
96 	    (desc->devnum == BLK_MTD_SPI_NAND || desc->devnum == BLK_MTD_NAND)) {
97 		env_size = CONFIG_ENV_NAND_SIZE;
98 		env_offset = CONFIG_ENV_NAND_OFFSET;
99 		env_offset_redund = CONFIG_ENV_NAND_OFFSET_REDUND;
100 	}
101 #endif
102 #if defined(CONFIG_SPI_FLASH)
103 	/* spi-nor */
104 	if (desc->if_type == IF_TYPE_MTD && desc->devnum == BLK_MTD_SPI_NOR) {
105 		env_size = CONFIG_ENV_NOR_SIZE;
106 		env_offset = CONFIG_ENV_NOR_OFFSET;
107 		env_offset_redund = CONFIG_ENV_NOR_OFFSET_REDUND;
108 	}
109 #endif
110 	if (env_offset == env_offset_redund)
111 		env_offset_redund = 0;
112 }
113 
114 static int env_read(struct blk_desc *desc, u32 offset, u32 size, env_t **envp)
115 {
116 	lbaint_t data_size;
117 	lbaint_t blk_off;
118 	lbaint_t blk_cnt;
119 	env_t *env;
120 	int ret;
121 
122 #ifdef CONFIG_SPL_BUILD
123 	env = spl_env;
124 #else
125 	env = malloc(size);
126 	if (!env)
127 		return -ENOMEM;
128 #endif
129 	data_size = size - ENV_HEADER_SIZE;
130 	blk_off = BLK_CNT(desc, offset);
131 	blk_cnt = BLK_CNT(desc, size);
132 
133 	if (blk_dread(desc, blk_off, blk_cnt, (void *)env) != blk_cnt) {
134 		ret = -EIO;
135 		goto fail;
136 	}
137 
138 	if (crc32(0, env->data, data_size) != env->crc) {
139 		ENVF_MSG("!bad CRC @ 0x%x\n", offset);
140 		ret = -EINVAL;
141 		goto fail;
142 	}
143 
144 	*envp = env;
145 
146 	return 0;
147 fail:
148 #ifndef CONFIG_SPL_BUILD
149 	free(env);
150 #endif
151 	return ret;
152 }
153 
154 static __maybe_unused env_t *envf_read(struct blk_desc *desc)
155 {
156 	env_t *env = NULL;
157 	int ret;
158 
159 	if (!desc)
160 		return NULL;
161 
162 	envf_init_location(desc);
163 
164 #ifdef CONFIG_DM_MMC
165 	/* SD upgrade card: LBA0 is MBR, and LBA1 is env.img with 16KB size */
166 	if (desc->if_type == IF_TYPE_MMC && desc->devnum == 1 &&
167 	    !env_offset && can_find_pmbr(desc)) {
168 		env_offset = 512;
169 		env_size = SZ_16K;
170 	}
171 #endif
172 	ret = env_read(desc, env_offset, env_size, &env);
173 	if (ret < 0 && env_offset_redund)
174 		ret = env_read(desc, env_offset_redund, env_size, &env);
175 
176 	return env;
177 }
178 
179 #if CONFIG_IS_ENABLED(ENV_PARTITION)
180 static const char *env_get_string(env_t *env, u32 size, const char *str)
181 {
182 	const char *dp;
183 	u32 env_size;
184 
185 	dp = (const char *)env->data;
186 	env_size = size - ENV_HEADER_SIZE;
187 	do {
188 		/* skip leading white space */
189 		while (*dp == ' ' || *dp == '\t')
190 			++dp;
191 
192 		debug("ENTRY: %s\n", dp);
193 		if (strstr(dp, str)) {
194 			debug("FIND: %s\n", dp);
195 			return dp;
196 		}
197 
198 		/* point to next ENTRY */
199 		dp += strlen(dp) + 1;
200 	} while (((ulong)dp < (ulong)env->data + env_size) && *dp);
201 
202 	debug("NOT-FIND: %s\n", str);
203 
204 	return NULL;
205 }
206 
207 char *envf_get_part_table(struct blk_desc *desc)
208 {
209 	const char *list = NULL;
210 	env_t *env;
211 
212 	if (!desc)
213 		goto out;
214 
215 	env = envf_read(desc);
216 	if (!env)
217 		goto out;
218 
219 	ENVF_MSG("Primary 0x%08lx - 0x%08lx\n", env_offset, env_offset + env_size);
220 	if (env_offset_redund)
221 		ENVF_MSG("Backup  0x%08lx - 0x%08lx\n",
222 			 env_offset_redund, env_offset_redund + env_size);
223 
224 	list = env_get_string(env, env_size, part_type[0]);
225 	if (!list)
226 		list = env_get_string(env, env_size, part_type[1]);
227 	if (!list)
228 		ENVF_MSG("Unavailable env part table\n");
229 	else
230 		ENVF_MSG("OK\n");
231 out:
232 	return (char *)list;
233 }
234 #endif
235 
236 #ifndef CONFIG_SPL_BUILD
237 static int envf_init_vars(void)
238 {
239 	char *tok, *p;
240 
241 	tok = strdup(CONFIG_ENVF_LIST);
242 	if (!tok)
243 		return 0;
244 
245 	envf_num = 0;
246 	p = strtok(tok, " ");
247 	while (p && envf_num < ENVF_MAX) {
248 		if (!strcmp(p, "bootargs")) {
249 			printf("%s\n", EMSG_ARGS);
250 			run_command("download", 0);
251 #ifdef CONFIG_FIT_SIGNATURE
252 		} else if (!strcmp(p, "sys_bootargs")) {
253 			/* Do nothing, ignore 'sys_bootargs' from env.img */
254 #endif
255 		} else {
256 			envf_list[envf_num++] = p;
257 		}
258 
259 		p = strtok(NULL, " ");
260 	}
261 
262 	return envf_num;
263 }
264 
265 #ifdef CONFIG_ENV_PARTITION
266 static int envf_add_partition_bootargs(void)
267 {
268 	char *part_list;
269 	char *bootargs;
270 	int i;
271 
272 	for (i = 0; i < ARRAY_SIZE(part_type); i++) {
273 		part_list = env_get(part_type[i]);
274 		if (part_list)
275 			break;
276 	}
277 	if (!part_list)
278 		return -EINVAL;
279 
280 	bootargs = calloc(1, strlen(part_list) + strlen(part_type[i]) + 2);
281 	if (!bootargs)
282 		return -ENOMEM;
283 
284 	strcat(bootargs, part_type[i]);
285 	strcat(bootargs, "=");
286 	strcat(bootargs, part_list);
287 	env_update("bootargs", bootargs);
288 	free(bootargs);
289 
290 	return 0;
291 }
292 #endif
293 
294 static int envf_load(void)
295 {
296 	struct blk_desc *desc;
297 	env_t *env;
298 
299 	desc = rockchip_get_bootdev();
300 	if (!desc) {
301 		printf("dev desc null!\n");
302 		return 0;
303 	}
304 
305 	env = envf_read(desc);
306 	if (!env)
307 		return -EINVAL;
308 
309 	if (envf_init_vars() > 0) {
310 		if (!himport_r(&env_htab, (char *)env->data, env_size, '\0',
311 			H_NOCLEAR, 0, envf_num, (char * const *)envf_list)) {
312 			ENVF_MSG("envf himport error: %d\n", errno);
313 			return -EINTR;
314 		}
315 	}
316 
317 #ifdef CONFIG_ENV_PARTITION
318 	envf_add_partition_bootargs();
319 #endif
320 
321 	return 0;
322 }
323 
324 static int envf_save(void)
325 {
326 	ALLOC_CACHE_ALIGN_BUFFER(env_t, env, 1);
327 	struct blk_desc *desc;
328 	ssize_t	len;
329 	u32 blk_cnt;
330 	char *res;
331 	int ret = 0;
332 
333 	desc = rockchip_get_bootdev();
334 	if (!desc) {
335 		printf("dev desc null!\n");
336 		return -EINVAL;
337 	}
338 
339 	res = (char *)env->data;
340 	len = hexport_r(&env_htab, '\0', H_MATCH_KEY | H_MATCH_IDENT,
341 			&res, env_size - ENV_HEADER_SIZE,
342 			envf_num, (char * const *)envf_list);
343 	if (len < 0) {
344 		ENVF_MSG("hexpor error: %d\n", errno);
345 		return -EINVAL;
346 	}
347 
348 	env->crc = crc32(0, env->data, env_size - ENV_HEADER_SIZE);
349 	blk_cnt = BLK_CNT(desc, env_size);
350 	if (blk_dwrite(desc, BLK_CNT(desc, env_offset),
351 		       blk_cnt, (char *)env) != blk_cnt) {
352 		ret = -EIO;
353 		ENVF_MSG("io error\n");
354 	}
355 
356 	if (env_offset_redund) {
357 		if (blk_dwrite(desc, BLK_CNT(desc, env_offset_redund),
358 			       blk_cnt, (char *)env) != blk_cnt)
359 			ENVF_MSG("redundant: io error\n");
360 		else
361 			ret = 0;
362 	}
363 
364 	return ret;
365 }
366 
367 static int envf_nowhere_init(void)
368 {
369 	gd->env_addr	= (ulong)&default_environment[0];
370 	gd->env_valid	= ENV_INVALID;
371 
372 	return 0;
373 }
374 
375 U_BOOT_ENV_LOCATION(nowhere) = {
376 	.location	= ENVL_NOWHERE,
377 	.init		= envf_nowhere_init,
378 	.load		= envf_load,
379 	.save		= env_save_ptr(envf_save),
380 	ENV_NAME("envf")
381 };
382 #endif
383 
384