virtio: Add some debugging
[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(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 err;
112
113         /* look for the partition in mmc CONFIG_SYS_MMC_ENV_DEV */
114         str = ofnode_conf_read_str(dt_prop.partition);
115         if (str) {
116                 /* try to place the environment at end of the partition */
117                 err = mmc_offset_try_partition(str, copy, &val);
118                 if (!err)
119                         return val;
120                 debug("env partition '%s' not found (%d)", str, err);
121         }
122
123         /* try the GPT partition with "U-Boot ENV" TYPE GUID */
124         if (IS_ENABLED(CONFIG_PARTITION_TYPE_GUID)) {
125                 err = mmc_offset_try_partition(NULL, copy, &val);
126                 if (!err)
127                         return val;
128         }
129
130         defvalue = ENV_MMC_OFFSET;
131         propname = dt_prop.offset;
132
133         if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT) && copy) {
134                 defvalue = ENV_MMC_OFFSET_REDUND;
135                 propname = dt_prop.offset_redund;
136         }
137
138         return ofnode_conf_read_int(propname, defvalue);
139 }
140 #else
141 static inline s64 mmc_offset(int copy)
142 {
143         s64 offset = ENV_MMC_OFFSET;
144
145         if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT) && copy)
146                 offset = ENV_MMC_OFFSET_REDUND;
147
148         return offset;
149 }
150 #endif
151
152 __weak int mmc_get_env_addr(struct mmc *mmc, int copy, u32 *env_addr)
153 {
154         s64 offset = mmc_offset(copy);
155
156         if (offset == ENV_MMC_INVALID_OFFSET) {
157                 printf("Invalid ENV offset in MMC, copy=%d\n", copy);
158                 return -ENOENT;
159         }
160
161         if (offset < 0)
162                 offset += mmc->capacity;
163
164         *env_addr = offset;
165
166         return 0;
167 }
168
169 #ifdef CONFIG_SYS_MMC_ENV_PART
170 __weak uint mmc_get_env_part(struct mmc *mmc)
171 {
172         return CONFIG_SYS_MMC_ENV_PART;
173 }
174
175 static unsigned char env_mmc_orig_hwpart;
176
177 static int mmc_set_env_part(struct mmc *mmc, uint part)
178 {
179         int dev = mmc_get_env_dev();
180         int ret = 0;
181
182         ret = blk_select_hwpart_devnum(UCLASS_MMC, dev, part);
183         if (ret)
184                 puts("MMC partition switch failed\n");
185
186         return ret;
187 }
188
189 static bool mmc_set_env_part_init(struct mmc *mmc)
190 {
191         env_mmc_orig_hwpart = mmc_get_blk_desc(mmc)->hwpart;
192         if (mmc_set_env_part(mmc, mmc_get_env_part(mmc)))
193                 return false;
194
195         return true;
196 }
197
198 static int mmc_set_env_part_restore(struct mmc *mmc)
199 {
200         return mmc_set_env_part(mmc, env_mmc_orig_hwpart);
201 }
202 #else
203 static inline int mmc_set_env_part(struct mmc *mmc, uint part) {return 0; };
204 static bool mmc_set_env_part_init(struct mmc *mmc) {return true; }
205 static inline int mmc_set_env_part_restore(struct mmc *mmc) {return 0; };
206 #endif
207
208 static const char *init_mmc_for_env(struct mmc *mmc)
209 {
210         if (!mmc)
211                 return "No MMC card found";
212
213 #if CONFIG_IS_ENABLED(BLK)
214         struct udevice *dev;
215
216         if (blk_get_from_parent(mmc->dev, &dev))
217                 return "No block device";
218 #else
219         if (mmc_init(mmc))
220                 return "MMC init failed";
221 #endif
222         if (!mmc_set_env_part_init(mmc))
223                 return "MMC partition switch failed";
224
225         return NULL;
226 }
227
228 static void fini_mmc_for_env(struct mmc *mmc)
229 {
230         mmc_set_env_part_restore(mmc);
231 }
232
233 #if defined(CONFIG_CMD_SAVEENV) && !defined(CONFIG_SPL_BUILD)
234 static inline int write_env(struct mmc *mmc, unsigned long size,
235                             unsigned long offset, const void *buffer)
236 {
237         uint blk_start, blk_cnt, n;
238         struct blk_desc *desc = mmc_get_blk_desc(mmc);
239
240         blk_start       = ALIGN(offset, mmc->write_bl_len) / mmc->write_bl_len;
241         blk_cnt         = ALIGN(size, mmc->write_bl_len) / mmc->write_bl_len;
242
243         n = blk_dwrite(desc, blk_start, blk_cnt, (u_char *)buffer);
244
245         return (n == blk_cnt) ? 0 : -1;
246 }
247
248 static int env_mmc_save(void)
249 {
250         ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1);
251         int dev = mmc_get_env_dev();
252         struct mmc *mmc = find_mmc_device(dev);
253         u32     offset;
254         int     ret, copy = 0;
255         const char *errmsg;
256
257         errmsg = init_mmc_for_env(mmc);
258         if (errmsg) {
259                 printf("%s\n", errmsg);
260                 return 1;
261         }
262
263         ret = env_export(env_new);
264         if (ret)
265                 goto fini;
266
267         if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT)) {
268                 if (gd->env_valid == ENV_VALID)
269                         copy = 1;
270
271                 if (IS_ENABLED(ENV_MMC_HWPART_REDUND)) {
272                         ret = mmc_set_env_part(mmc, copy + 1);
273                         if (ret)
274                                 goto fini;
275                 }
276
277                 if (mmc_get_env_addr(mmc, copy, &offset)) {
278                         ret = 1;
279                         goto fini;
280                 }
281         }
282
283         printf("Writing to %sMMC(%d)... ", copy ? "redundant " : "", dev);
284         if (write_env(mmc, CONFIG_ENV_SIZE, offset, (u_char *)env_new)) {
285                 puts("failed\n");
286                 ret = 1;
287                 goto fini;
288         }
289
290         ret = 0;
291
292         if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT))
293                 gd->env_valid = gd->env_valid == ENV_REDUND ? ENV_VALID : ENV_REDUND;
294
295 fini:
296         fini_mmc_for_env(mmc);
297
298         return ret;
299 }
300
301 static inline int erase_env(struct mmc *mmc, unsigned long size,
302                             unsigned long offset)
303 {
304         uint blk_start, blk_cnt, n;
305         struct blk_desc *desc = mmc_get_blk_desc(mmc);
306         u32 erase_size;
307
308         erase_size = mmc->erase_grp_size * desc->blksz;
309         blk_start = ALIGN_DOWN(offset, erase_size) / desc->blksz;
310         blk_cnt = ALIGN(size, erase_size) / desc->blksz;
311
312         n = blk_derase(desc, blk_start, blk_cnt);
313         printf("%d blocks erased at 0x%x: %s\n", n, blk_start,
314                (n == blk_cnt) ? "OK" : "ERROR");
315
316         return (n == blk_cnt) ? 0 : 1;
317 }
318
319 static int env_mmc_erase(void)
320 {
321         int dev = mmc_get_env_dev();
322         struct mmc *mmc = find_mmc_device(dev);
323         int     ret, copy = 0;
324         u32     offset;
325         const char *errmsg;
326
327         errmsg = init_mmc_for_env(mmc);
328         if (errmsg) {
329                 printf("%s\n", errmsg);
330                 return 1;
331         }
332
333         if (mmc_get_env_addr(mmc, copy, &offset)) {
334                 ret = CMD_RET_FAILURE;
335                 goto fini;
336         }
337
338         printf("\n");
339         ret = erase_env(mmc, CONFIG_ENV_SIZE, offset);
340
341         if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT)) {
342                 copy = 1;
343
344                 if (IS_ENABLED(ENV_MMC_HWPART_REDUND)) {
345                         ret = mmc_set_env_part(mmc, copy + 1);
346                         if (ret)
347                                 goto fini;
348                 }
349
350                 if (mmc_get_env_addr(mmc, copy, &offset)) {
351                         ret = CMD_RET_FAILURE;
352                         goto fini;
353                 }
354
355                 ret |= erase_env(mmc, CONFIG_ENV_SIZE, offset);
356         }
357
358 fini:
359         fini_mmc_for_env(mmc);
360         return ret;
361 }
362 #endif /* CONFIG_CMD_SAVEENV && !CONFIG_SPL_BUILD */
363
364 static inline int read_env(struct mmc *mmc, unsigned long size,
365                            unsigned long offset, const void *buffer)
366 {
367         uint blk_start, blk_cnt, n;
368         struct blk_desc *desc = mmc_get_blk_desc(mmc);
369
370         blk_start       = ALIGN(offset, mmc->read_bl_len) / mmc->read_bl_len;
371         blk_cnt         = ALIGN(size, mmc->read_bl_len) / mmc->read_bl_len;
372
373         n = blk_dread(desc, blk_start, blk_cnt, (uchar *)buffer);
374
375         return (n == blk_cnt) ? 0 : -1;
376 }
377
378 #if defined(ENV_IS_EMBEDDED)
379 static int env_mmc_load(void)
380 {
381         return 0;
382 }
383 #elif defined(CONFIG_SYS_REDUNDAND_ENVIRONMENT)
384 static int env_mmc_load(void)
385 {
386         struct mmc *mmc;
387         u32 offset1, offset2;
388         int read1_fail = 0, read2_fail = 0;
389         int ret;
390         int dev = mmc_get_env_dev();
391         const char *errmsg = NULL;
392
393         ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env1, 1);
394         ALLOC_CACHE_ALIGN_BUFFER(env_t, tmp_env2, 1);
395
396         mmc_initialize(NULL);
397
398         mmc = find_mmc_device(dev);
399
400         errmsg = init_mmc_for_env(mmc);
401         if (errmsg) {
402                 ret = -EIO;
403                 goto err;
404         }
405
406         if (mmc_get_env_addr(mmc, 0, &offset1) ||
407             mmc_get_env_addr(mmc, 1, &offset2)) {
408                 ret = -EIO;
409                 goto fini;
410         }
411
412         if (IS_ENABLED(ENV_MMC_HWPART_REDUND)) {
413                 ret = mmc_set_env_part(mmc, 1);
414                 if (ret)
415                         goto fini;
416         }
417
418         read1_fail = read_env(mmc, CONFIG_ENV_SIZE, offset1, tmp_env1);
419
420         if (IS_ENABLED(ENV_MMC_HWPART_REDUND)) {
421                 ret = mmc_set_env_part(mmc, 2);
422                 if (ret)
423                         goto fini;
424         }
425
426         read2_fail = read_env(mmc, CONFIG_ENV_SIZE, offset2, tmp_env2);
427
428         ret = env_import_redund((char *)tmp_env1, read1_fail, (char *)tmp_env2,
429                                 read2_fail, H_EXTERNAL);
430
431 fini:
432         fini_mmc_for_env(mmc);
433 err:
434         if (ret)
435                 env_set_default(errmsg, 0);
436
437         return ret;
438 }
439 #else /* ! CONFIG_SYS_REDUNDAND_ENVIRONMENT */
440 static int env_mmc_load(void)
441 {
442         ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE);
443         struct mmc *mmc;
444         u32 offset;
445         int ret;
446         int dev = mmc_get_env_dev();
447         const char *errmsg;
448         env_t *ep = NULL;
449
450         mmc = find_mmc_device(dev);
451
452         errmsg = init_mmc_for_env(mmc);
453         if (errmsg) {
454                 ret = -EIO;
455                 goto err;
456         }
457
458         if (mmc_get_env_addr(mmc, 0, &offset)) {
459                 ret = -EIO;
460                 goto fini;
461         }
462
463         if (read_env(mmc, CONFIG_ENV_SIZE, offset, buf)) {
464                 errmsg = "!read failed";
465                 ret = -EIO;
466                 goto fini;
467         }
468
469         ret = env_import(buf, 1, H_EXTERNAL);
470         if (!ret) {
471                 ep = (env_t *)buf;
472                 gd->env_addr = (ulong)&ep->data;
473         }
474
475 fini:
476         fini_mmc_for_env(mmc);
477 err:
478         if (ret)
479                 env_set_default(errmsg, 0);
480
481         return ret;
482 }
483 #endif /* CONFIG_SYS_REDUNDAND_ENVIRONMENT */
484
485 U_BOOT_ENV_LOCATION(mmc) = {
486         .location       = ENVL_MMC,
487         ENV_NAME("MMC")
488         .load           = env_mmc_load,
489 #ifndef CONFIG_SPL_BUILD
490         .save           = env_save_ptr(env_mmc_save),
491         .erase          = ENV_ERASE_PTR(env_mmc_erase)
492 #endif
493 };