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, ©);
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