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