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