1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2008-2011 Freescale Semiconductor, Inc.
4  */
5 
6 /* #define DEBUG */
7 
8 #include <asm/global_data.h>
9 
10 #include <command.h>
11 #include <env.h>
12 #include <env_internal.h>
13 #include <fdtdec.h>
14 #include <linux/stddef.h>
15 #include <malloc.h>
16 #include <memalign.h>
17 #include <mmc.h>
18 #include <part.h>
19 #include <search.h>
20 #include <errno.h>
21 #include <dm/ofnode.h>
22 
23 #define ENV_MMC_INVALID_OFFSET ((s64)-1)
24 
25 #if defined(CONFIG_ENV_MMC_USE_DT)
26 /* ENV offset is invalid when not defined in Device Tree */
27 #define ENV_MMC_OFFSET		ENV_MMC_INVALID_OFFSET
28 #define ENV_MMC_OFFSET_REDUND	ENV_MMC_INVALID_OFFSET
29 
30 #else
31 /* Default ENV offset when not defined in Device Tree */
32 #if !defined(CONFIG_ENV_OFFSET_RELATIVE_END)
33 #define ENV_MMC_OFFSET		CONFIG_ENV_OFFSET
34 #else
35 #define ENV_MMC_OFFSET		(-(CONFIG_ENV_OFFSET))
36 #endif
37 
38 #if defined(CONFIG_ENV_OFFSET_REDUND)
39 #if !defined(CONFIG_ENV_OFFSET_REDUND_RELATIVE_END)
40 #define ENV_MMC_OFFSET_REDUND	CONFIG_ENV_OFFSET_REDUND
41 #else
42 #define ENV_MMC_OFFSET_REDUND	(-(CONFIG_ENV_OFFSET_REDUND))
43 #endif
44 #else
45 #define ENV_MMC_OFFSET_REDUND	ENV_MMC_INVALID_OFFSET
46 #endif
47 #endif
48 
49 DECLARE_GLOBAL_DATA_PTR;
50 
51 #if CONFIG_IS_ENABLED(OF_CONTROL)
52 
mmc_env_partition_by_name(struct blk_desc * desc,const char * str,struct disk_partition * info)53 static int mmc_env_partition_by_name(struct blk_desc *desc, const char *str,
54 				     struct disk_partition *info)
55 {
56 	int i, ret;
57 
58 	for (i = 1;; i++) {
59 		ret = part_get_info(desc, i, info);
60 		if (ret < 0)
61 			return ret;
62 
63 		if (!strncmp((const char *)info->name, str, sizeof(info->name)))
64 			return 0;
65 	}
66 }
67 
68 /*
69  * Look for one or two partitions with the U-Boot environment GUID.
70  *
71  * If *copy is 0, return the first such partition.
72  *
73  * If *copy is 1 on entry and two partitions are found, return the
74  * second partition and set *copy = 0.
75  *
76  * If *copy is 1 on entry and only one partition is found, return that
77  * partition, leaving *copy unmodified.
78  */
mmc_env_partition_by_guid(struct blk_desc * desc,struct disk_partition * info,int * copy)79 static int mmc_env_partition_by_guid(struct blk_desc *desc, struct disk_partition *info,
80 				     int *copy)
81 {
82 	const efi_guid_t env_guid = PARTITION_U_BOOT_ENVIRONMENT;
83 	efi_guid_t type_guid;
84 	int i, ret, found = 0;
85 	struct disk_partition dp;
86 
87 	for (i = 1;; i++) {
88 		ret = part_get_info(desc, i, &dp);
89 		if (ret < 0)
90 			break;
91 
92 		uuid_str_to_bin(disk_partition_type_guid(&dp), type_guid.b, UUID_STR_FORMAT_GUID);
93 		if (!memcmp(&env_guid, &type_guid, sizeof(efi_guid_t))) {
94 			memcpy(info, &dp, sizeof(dp));
95 			/* If *copy is 0, we are only looking for the first partition. */
96 			if (*copy == 0)
97 				return 0;
98 			/* This was the second such partition. */
99 			if (found) {
100 				*copy = 0;
101 				return 0;
102 			}
103 			found = 1;
104 		}
105 	}
106 
107 	/* The loop ended after finding at most one matching partition. */
108 	if (found)
109 		ret = 0;
110 	return ret;
111 }
112 
113 
mmc_offset_try_partition(const char * str,int copy,s64 * val)114 static inline int mmc_offset_try_partition(const char *str, int copy, s64 *val)
115 {
116 	struct disk_partition info;
117 	struct blk_desc *desc;
118 	lbaint_t len;
119 	int ret;
120 	char dev_str[4];
121 
122 	snprintf(dev_str, sizeof(dev_str), "%d", mmc_get_env_dev());
123 	ret = blk_get_device_by_str("mmc", dev_str, &desc);
124 	if (ret < 0)
125 		return (ret);
126 
127 	if (str) {
128 		ret = mmc_env_partition_by_name(desc, str, &info);
129 	} else if (IS_ENABLED(CONFIG_PARTITION_TYPE_GUID) && !str) {
130 		ret = mmc_env_partition_by_guid(desc, &info, &copy);
131 	}
132 	if (ret < 0)
133 		return ret;
134 
135 	/* round up to info.blksz */
136 	len = DIV_ROUND_UP(CONFIG_ENV_SIZE, info.blksz);
137 
138 	if ((1 + copy) * len > info.size) {
139 		printf("Partition '%s' [0x"LBAF"; 0x"LBAF"] too small for %senvironment, required size 0x"LBAF" blocks\n",
140 		       (const char*)info.name, info.start, info.size,
141 		       copy ? "two copies of " : "", (1 + copy)*len);
142 		return -ENOSPC;
143 	}
144 
145 	/* use the top of the partion for the environment */
146 	*val = (info.start + info.size - (1 + copy) * len) * info.blksz;
147 
148 	return 0;
149 }
150 
mmc_offset(struct mmc * mmc,int copy)151 static inline s64 mmc_offset(struct mmc *mmc, int copy)
152 {
153 	const struct {
154 		const char *offset_redund;
155 		const char *partition;
156 		const char *offset;
157 	} dt_prop = {
158 		.offset_redund = "u-boot,mmc-env-offset-redundant",
159 		.partition = "u-boot,mmc-env-partition",
160 		.offset = "u-boot,mmc-env-offset",
161 	};
162 	s64 val = 0, defvalue;
163 	const char *propname;
164 	const char *str;
165 	int hwpart = 0;
166 	int err;
167 
168 #if defined(CONFIG_ENV_MMC_EMMC_HW_PARTITION)
169 	hwpart = mmc_get_env_part(mmc);
170 #endif
171 
172 #if defined(CONFIG_ENV_MMC_SW_PARTITION)
173 	str = CONFIG_ENV_MMC_SW_PARTITION;
174 #else
175 	/* look for the partition in mmc CONFIG_ENV_MMC_DEVICE_INDEX */
176 	str = ofnode_conf_read_str(dt_prop.partition);
177 #endif
178 
179 	if (str) {
180 		/* try to place the environment at end of the partition */
181 		err = mmc_offset_try_partition(str, copy, &val);
182 		if (!err)
183 			return val;
184 		debug("env partition '%s' not found (%d)", str, err);
185 	}
186 
187 	/* try the GPT partition with "U-Boot ENV" TYPE GUID */
188 	if (IS_ENABLED(CONFIG_PARTITION_TYPE_GUID) && hwpart == 0) {
189 		err = mmc_offset_try_partition(NULL, copy, &val);
190 		if (!err)
191 			return val;
192 	}
193 
194 	defvalue = ENV_MMC_OFFSET;
195 	propname = dt_prop.offset;
196 
197 	if (IS_ENABLED(CONFIG_ENV_REDUNDANT) && copy) {
198 		defvalue = ENV_MMC_OFFSET_REDUND;
199 		propname = dt_prop.offset_redund;
200 	}
201 
202 	return ofnode_conf_read_int(propname, defvalue);
203 }
204 #else
mmc_offset(struct mmc * mmc,int copy)205 static inline s64 mmc_offset(struct mmc *mmc, int copy)
206 {
207 	s64 offset = ENV_MMC_OFFSET;
208 
209 	if (IS_ENABLED(CONFIG_ENV_REDUNDANT) && copy)
210 		offset = ENV_MMC_OFFSET_REDUND;
211 
212 	return offset;
213 }
214 #endif
215 
mmc_env_is_redundant_in_both_boot_hwparts(struct mmc * mmc)216 static bool mmc_env_is_redundant_in_both_boot_hwparts(struct mmc *mmc)
217 {
218 	/*
219 	 * In case the environment is redundant, stored in eMMC hardware boot
220 	 * partition and the environment and redundant environment offsets are
221 	 * identical, store the environment and redundant environment in both
222 	 * eMMC boot partitions, one copy in each.
223 	 */
224 	if (!IS_ENABLED(CONFIG_ENV_REDUNDANT))
225 		return false;
226 
227 	if (CONFIG_ENV_MMC_EMMC_HW_PARTITION != 1)
228 		return false;
229 
230 	return mmc_offset(mmc, 0) == mmc_offset(mmc, 1);
231 }
232 
mmc_get_env_addr(struct mmc * mmc,int copy,u32 * env_addr)233 __weak int mmc_get_env_addr(struct mmc *mmc, int copy, u32 *env_addr)
234 {
235 	s64 offset = mmc_offset(mmc, copy);
236 
237 	if (offset == ENV_MMC_INVALID_OFFSET) {
238 		printf("Invalid ENV offset in MMC, copy=%d\n", copy);
239 		return -ENOENT;
240 	}
241 
242 	if (offset < 0)
243 		offset += mmc->capacity;
244 
245 	*env_addr = offset;
246 
247 	return 0;
248 }
249 
250 #ifdef CONFIG_ENV_MMC_EMMC_HW_PARTITION
mmc_get_env_part(struct mmc * mmc)251 __weak uint mmc_get_env_part(struct mmc *mmc)
252 {
253 	return CONFIG_ENV_MMC_EMMC_HW_PARTITION;
254 }
255 
256 static unsigned char env_mmc_orig_hwpart;
257 
mmc_set_env_part(struct mmc * mmc,uint part)258 static int mmc_set_env_part(struct mmc *mmc, uint part)
259 {
260 	int dev = mmc_get_env_dev();
261 	int ret = 0;
262 
263 	ret = blk_select_hwpart_devnum(UCLASS_MMC, dev, part);
264 	if (ret)
265 		puts("MMC partition switch failed\n");
266 
267 	return ret;
268 }
269 
mmc_set_env_part_init(struct mmc * mmc)270 static bool mmc_set_env_part_init(struct mmc *mmc)
271 {
272 	env_mmc_orig_hwpart = mmc_get_blk_desc(mmc)->hwpart;
273 	if (mmc_set_env_part(mmc, mmc_get_env_part(mmc)))
274 		return false;
275 
276 	return true;
277 }
278 
mmc_set_env_part_restore(struct mmc * mmc)279 static int mmc_set_env_part_restore(struct mmc *mmc)
280 {
281 	return mmc_set_env_part(mmc, env_mmc_orig_hwpart);
282 }
283 #else
mmc_set_env_part(struct mmc * mmc,uint part)284 static inline int mmc_set_env_part(struct mmc *mmc, uint part) {return 0; };
mmc_set_env_part_init(struct mmc * mmc)285 static bool mmc_set_env_part_init(struct mmc *mmc) {return true; }
mmc_set_env_part_restore(struct mmc * mmc)286 static inline int mmc_set_env_part_restore(struct mmc *mmc) {return 0; };
287 #endif
288 
init_mmc_for_env(struct mmc * mmc)289 static const char *init_mmc_for_env(struct mmc *mmc)
290 {
291 	if (!mmc)
292 		return "No MMC card found";
293 
294 #if CONFIG_IS_ENABLED(BLK)
295 	struct udevice *dev;
296 
297 	if (blk_get_from_parent(mmc->dev, &dev))
298 		return "No block device";
299 #else
300 	if (mmc_init(mmc))
301 		return "MMC init failed";
302 #endif
303 	if (!mmc_set_env_part_init(mmc))
304 		return "MMC partition switch failed";
305 
306 	return NULL;
307 }
308 
fini_mmc_for_env(struct mmc * mmc)309 static void fini_mmc_for_env(struct mmc *mmc)
310 {
311 	mmc_set_env_part_restore(mmc);
312 }
313 
314 #if defined(CONFIG_CMD_SAVEENV) && !defined(CONFIG_XPL_BUILD)
write_env(struct mmc * mmc,unsigned long size,unsigned long offset,const void * buffer)315 static inline int write_env(struct mmc *mmc, unsigned long size,
316 			    unsigned long offset, const void *buffer)
317 {
318 	uint blk_start, blk_cnt, n;
319 	struct blk_desc *desc = mmc_get_blk_desc(mmc);
320 
321 	blk_start	= ALIGN(offset, mmc->write_bl_len) / mmc->write_bl_len;
322 	blk_cnt		= ALIGN(size, mmc->write_bl_len) / mmc->write_bl_len;
323 
324 	n = blk_dwrite(desc, blk_start, blk_cnt, (u_char *)buffer);
325 
326 	return (n == blk_cnt) ? 0 : -1;
327 }
328 
env_mmc_save(void)329 static int env_mmc_save(void)
330 {
331 	ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1);
332 	int dev = mmc_get_env_dev();
333 	struct mmc *mmc = find_mmc_device(dev);
334 	u32	offset;
335 	int	ret, copy = 0;
336 	const char *errmsg;
337 
338 	errmsg = init_mmc_for_env(mmc);
339 	if (errmsg) {
340 		printf("%s\n", errmsg);
341 		return 1;
342 	}
343 
344 	ret = env_export(env_new);
345 	if (ret)
346 		goto fini;
347 
348 	if (IS_ENABLED(CONFIG_ENV_REDUNDANT)) {
349 		if (gd->env_valid == ENV_VALID)
350 			copy = 1;
351 
352 		if (mmc_env_is_redundant_in_both_boot_hwparts(mmc)) {
353 			ret = mmc_set_env_part(mmc, copy + 1);
354 			if (ret)
355 				goto fini;
356 		}
357 	}
358 
359 	if (mmc_get_env_addr(mmc, copy, &offset)) {
360 		ret = 1;
361 		goto fini;
362 	}
363 
364 	printf("Writing to %sMMC(%d)... ", copy ? "redundant " : "", dev);
365 	if (write_env(mmc, CONFIG_ENV_SIZE, offset, (u_char *)env_new)) {
366 		puts("failed\n");
367 		ret = 1;
368 		goto fini;
369 	}
370 
371 	ret = 0;
372 
373 	if (IS_ENABLED(CONFIG_ENV_REDUNDANT))
374 		gd->env_valid = gd->env_valid == ENV_REDUND ? ENV_VALID : ENV_REDUND;
375 
376 fini:
377 	fini_mmc_for_env(mmc);
378 
379 	return ret;
380 }
381 
erase_env(struct mmc * mmc,unsigned long size,unsigned long offset)382 static inline int erase_env(struct mmc *mmc, unsigned long size,
383 			    unsigned long offset)
384 {
385 	uint blk_start, blk_cnt, n;
386 	struct blk_desc *desc = mmc_get_blk_desc(mmc);
387 	u32 erase_size;
388 
389 	erase_size = mmc->erase_grp_size * desc->blksz;
390 	blk_start = ALIGN_DOWN(offset, erase_size) / desc->blksz;
391 	blk_cnt = ALIGN(size, erase_size) / desc->blksz;
392 
393 	n = blk_derase(desc, blk_start, blk_cnt);
394 	printf("%d blocks erased at 0x%x: %s\n", n, blk_start,
395 	       (n == blk_cnt) ? "OK" : "ERROR");
396 
397 	return (n == blk_cnt) ? 0 : 1;
398 }
399 
env_mmc_erase(void)400 static int env_mmc_erase(void)
401 {
402 	int dev = mmc_get_env_dev();
403 	struct mmc *mmc = find_mmc_device(dev);
404 	int	ret, copy = 0;
405 	u32	offset;
406 	const char *errmsg;
407 
408 	errmsg = init_mmc_for_env(mmc);
409 	if (errmsg) {
410 		printf("%s\n", errmsg);
411 		return 1;
412 	}
413 
414 	if (mmc_get_env_addr(mmc, copy, &offset)) {
415 		ret = CMD_RET_FAILURE;
416 		goto fini;
417 	}
418 
419 	printf("\n");
420 	ret = erase_env(mmc, CONFIG_ENV_SIZE, offset);
421 
422 	if (IS_ENABLED(CONFIG_ENV_REDUNDANT)) {
423 		copy = 1;
424 
425 		if (mmc_env_is_redundant_in_both_boot_hwparts(mmc)) {
426 			ret = mmc_set_env_part(mmc, copy + 1);
427 			if (ret)
428 				goto fini;
429 		}
430 
431 		if (mmc_get_env_addr(mmc, copy, &offset)) {
432 			ret = CMD_RET_FAILURE;
433 			goto fini;
434 		}
435 
436 		ret |= erase_env(mmc, CONFIG_ENV_SIZE, offset);
437 	}
438 
439 fini:
440 	fini_mmc_for_env(mmc);
441 	return ret;
442 }
443 #endif /* CONFIG_CMD_SAVEENV && !CONFIG_XPL_BUILD */
444 
read_env(struct mmc * mmc,unsigned long size,unsigned long offset,const void * buffer)445 static inline int read_env(struct mmc *mmc, unsigned long size,
446 			   unsigned long offset, const void *buffer)
447 {
448 	uint blk_start, blk_cnt, n;
449 	struct blk_desc *desc = mmc_get_blk_desc(mmc);
450 
451 	blk_start	= ALIGN(offset, mmc->read_bl_len) / mmc->read_bl_len;
452 	blk_cnt		= ALIGN(size, mmc->read_bl_len) / mmc->read_bl_len;
453 
454 	n = blk_dread(desc, blk_start, blk_cnt, (uchar *)buffer);
455 
456 	return (n == blk_cnt) ? 0 : -1;
457 }
458 
env_mmc_load_redundant(void)459 static int env_mmc_load_redundant(void)
460 {
461 	struct mmc *mmc;
462 	u32 offset1, offset2;
463 	int read1_fail = 0, read2_fail = 0;
464 	int ret;
465 	int dev = mmc_get_env_dev();
466 	const char *errmsg = NULL;
467 
468 	ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env1, 1);
469 	ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env2, 1);
470 
471 	mmc_initialize(NULL);
472 
473 	mmc = find_mmc_device(dev);
474 
475 	errmsg = init_mmc_for_env(mmc);
476 	if (errmsg) {
477 		ret = -EIO;
478 		goto err;
479 	}
480 
481 	if (mmc_get_env_addr(mmc, 0, &offset1) ||
482 	    mmc_get_env_addr(mmc, 1, &offset2)) {
483 		ret = -EIO;
484 		goto fini;
485 	}
486 
487 	if (mmc_env_is_redundant_in_both_boot_hwparts(mmc)) {
488 		ret = mmc_set_env_part(mmc, 1);
489 		if (ret)
490 			goto fini;
491 	}
492 
493 	read1_fail = read_env(mmc, CONFIG_ENV_SIZE, offset1, tmp_env1);
494 
495 	if (mmc_env_is_redundant_in_both_boot_hwparts(mmc)) {
496 		ret = mmc_set_env_part(mmc, 2);
497 		if (ret)
498 			goto fini;
499 	}
500 
501 	read2_fail = read_env(mmc, CONFIG_ENV_SIZE, offset2, tmp_env2);
502 
503 	ret = env_import_redund((char *)tmp_env1, read1_fail, (char *)tmp_env2,
504 				read2_fail, H_EXTERNAL);
505 	printf("Reading from %sMMC(%d)... ", gd->env_valid == ENV_REDUND ? "redundant " : "", dev);
506 
507 fini:
508 	fini_mmc_for_env(mmc);
509 err:
510 	if (ret)
511 		env_set_default(errmsg, 0);
512 
513 	return ret;
514 }
515 
env_mmc_load_singular(void)516 static int env_mmc_load_singular(void)
517 {
518 	ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE);
519 	struct mmc *mmc;
520 	u32 offset;
521 	int ret;
522 	int dev = mmc_get_env_dev();
523 	const char *errmsg;
524 	env_t *ep = NULL;
525 
526 	mmc = find_mmc_device(dev);
527 
528 	errmsg = init_mmc_for_env(mmc);
529 	if (errmsg) {
530 		ret = -EIO;
531 		goto err;
532 	}
533 
534 	if (mmc_get_env_addr(mmc, 0, &offset)) {
535 		ret = -EIO;
536 		goto fini;
537 	}
538 
539 	if (read_env(mmc, CONFIG_ENV_SIZE, offset, buf)) {
540 		errmsg = "!read failed";
541 		ret = -EIO;
542 		goto fini;
543 	}
544 
545 	printf("Reading from MMC(%d)... ", dev);
546 
547 	ret = env_import(buf, 1, H_EXTERNAL);
548 	if (!ret) {
549 		ep = (env_t *)buf;
550 		gd->env_addr = (ulong)&ep->data;
551 	}
552 
553 fini:
554 	fini_mmc_for_env(mmc);
555 err:
556 	if (ret)
557 		env_set_default(errmsg, 0);
558 
559 	return ret;
560 }
561 
env_mmc_load(void)562 static int env_mmc_load(void)
563 {
564 	if (IS_ENABLED(ENV_IS_EMBEDDED))
565 		return 0;
566 	else if (IS_ENABLED(CONFIG_ENV_REDUNDANT))
567 		return env_mmc_load_redundant();
568 	else
569 		return env_mmc_load_singular();
570 }
571 
572 U_BOOT_ENV_LOCATION(mmc) = {
573 	.location	= ENVL_MMC,
574 	ENV_NAME("MMC")
575 	.load		= env_mmc_load,
576 #if defined(CONFIG_CMD_SAVEENV) && !defined(CONFIG_XPL_BUILD)
577 	.save		= env_save_ptr(env_mmc_save),
578 	.erase		= ENV_ERASE_PTR(env_mmc_erase)
579 #endif
580 };
581