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