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