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