]> git.sur5r.net Git - u-boot/blob - drivers/dfu/dfu_mmc.c
dm: core: Add logging of some common errors
[u-boot] / drivers / dfu / dfu_mmc.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * dfu.c -- DFU back-end routines
4  *
5  * Copyright (C) 2012 Samsung Electronics
6  * author: Lukasz Majewski <l.majewski@samsung.com>
7  */
8
9 #include <common.h>
10 #include <malloc.h>
11 #include <errno.h>
12 #include <div64.h>
13 #include <dfu.h>
14 #include <ext4fs.h>
15 #include <fat.h>
16 #include <mmc.h>
17
18 static unsigned char *dfu_file_buf;
19 static u64 dfu_file_buf_len;
20 static long dfu_file_buf_filled;
21
22 static int mmc_block_op(enum dfu_op op, struct dfu_entity *dfu,
23                         u64 offset, void *buf, long *len)
24 {
25         struct mmc *mmc;
26         u32 blk_start, blk_count, n = 0;
27         int ret, part_num_bkp = 0;
28
29         mmc = find_mmc_device(dfu->data.mmc.dev_num);
30         if (!mmc) {
31                 pr_err("Device MMC %d - not found!", dfu->data.mmc.dev_num);
32                 return -ENODEV;
33         }
34
35         /*
36          * We must ensure that we work in lba_blk_size chunks, so ALIGN
37          * this value.
38          */
39         *len = ALIGN(*len, dfu->data.mmc.lba_blk_size);
40
41         blk_start = dfu->data.mmc.lba_start +
42                         (u32)lldiv(offset, dfu->data.mmc.lba_blk_size);
43         blk_count = *len / dfu->data.mmc.lba_blk_size;
44         if (blk_start + blk_count >
45                         dfu->data.mmc.lba_start + dfu->data.mmc.lba_size) {
46                 puts("Request would exceed designated area!\n");
47                 return -EINVAL;
48         }
49
50         if (dfu->data.mmc.hw_partition >= 0) {
51                 part_num_bkp = mmc_get_blk_desc(mmc)->hwpart;
52                 ret = blk_select_hwpart_devnum(IF_TYPE_MMC,
53                                                dfu->data.mmc.dev_num,
54                                                dfu->data.mmc.hw_partition);
55                 if (ret)
56                         return ret;
57         }
58
59         debug("%s: %s dev: %d start: %d cnt: %d buf: 0x%p\n", __func__,
60               op == DFU_OP_READ ? "MMC READ" : "MMC WRITE",
61               dfu->data.mmc.dev_num, blk_start, blk_count, buf);
62         switch (op) {
63         case DFU_OP_READ:
64                 n = blk_dread(mmc_get_blk_desc(mmc), blk_start, blk_count, buf);
65                 break;
66         case DFU_OP_WRITE:
67                 n = blk_dwrite(mmc_get_blk_desc(mmc), blk_start, blk_count,
68                                buf);
69                 break;
70         default:
71                 pr_err("Operation not supported\n");
72         }
73
74         if (n != blk_count) {
75                 pr_err("MMC operation failed");
76                 if (dfu->data.mmc.hw_partition >= 0)
77                         blk_select_hwpart_devnum(IF_TYPE_MMC,
78                                                  dfu->data.mmc.dev_num,
79                                                  part_num_bkp);
80                 return -EIO;
81         }
82
83         if (dfu->data.mmc.hw_partition >= 0) {
84                 ret = blk_select_hwpart_devnum(IF_TYPE_MMC,
85                                                dfu->data.mmc.dev_num,
86                                                part_num_bkp);
87                 if (ret)
88                         return ret;
89         }
90
91         return 0;
92 }
93
94 static int mmc_file_buffer(struct dfu_entity *dfu, void *buf, long *len)
95 {
96         if (dfu_file_buf_len + *len > CONFIG_SYS_DFU_MAX_FILE_SIZE) {
97                 dfu_file_buf_len = 0;
98                 return -EINVAL;
99         }
100
101         /* Add to the current buffer. */
102         memcpy(dfu_file_buf + dfu_file_buf_len, buf, *len);
103         dfu_file_buf_len += *len;
104
105         return 0;
106 }
107
108 static int mmc_file_op(enum dfu_op op, struct dfu_entity *dfu,
109                         void *buf, u64 *len)
110 {
111         const char *fsname, *opname;
112         char cmd_buf[DFU_CMD_BUF_SIZE];
113         char *str_env;
114         int ret;
115
116         switch (dfu->layout) {
117         case DFU_FS_FAT:
118                 fsname = "fat";
119                 break;
120         case DFU_FS_EXT4:
121                 fsname = "ext4";
122                 break;
123         default:
124                 printf("%s: Layout (%s) not (yet) supported!\n", __func__,
125                        dfu_get_layout(dfu->layout));
126                 return -1;
127         }
128
129         switch (op) {
130         case DFU_OP_READ:
131                 opname = "load";
132                 break;
133         case DFU_OP_WRITE:
134                 opname = "write";
135                 break;
136         case DFU_OP_SIZE:
137                 opname = "size";
138                 break;
139         default:
140                 return -1;
141         }
142
143         sprintf(cmd_buf, "%s%s mmc %d:%d", fsname, opname,
144                 dfu->data.mmc.dev, dfu->data.mmc.part);
145
146         if (op != DFU_OP_SIZE)
147                 sprintf(cmd_buf + strlen(cmd_buf), " %p", buf);
148
149         sprintf(cmd_buf + strlen(cmd_buf), " %s", dfu->name);
150
151         if (op == DFU_OP_WRITE)
152                 sprintf(cmd_buf + strlen(cmd_buf), " %llx", *len);
153
154         debug("%s: %s 0x%p\n", __func__, cmd_buf, cmd_buf);
155
156         ret = run_command(cmd_buf, 0);
157         if (ret) {
158                 puts("dfu: Read error!\n");
159                 return ret;
160         }
161
162         if (op != DFU_OP_WRITE) {
163                 str_env = env_get("filesize");
164                 if (str_env == NULL) {
165                         puts("dfu: Wrong file size!\n");
166                         return -1;
167                 }
168                 *len = simple_strtoul(str_env, NULL, 16);
169         }
170
171         return ret;
172 }
173
174 int dfu_write_medium_mmc(struct dfu_entity *dfu,
175                 u64 offset, void *buf, long *len)
176 {
177         int ret = -1;
178
179         switch (dfu->layout) {
180         case DFU_RAW_ADDR:
181                 ret = mmc_block_op(DFU_OP_WRITE, dfu, offset, buf, len);
182                 break;
183         case DFU_FS_FAT:
184         case DFU_FS_EXT4:
185                 ret = mmc_file_buffer(dfu, buf, len);
186                 break;
187         default:
188                 printf("%s: Layout (%s) not (yet) supported!\n", __func__,
189                        dfu_get_layout(dfu->layout));
190         }
191
192         return ret;
193 }
194
195 int dfu_flush_medium_mmc(struct dfu_entity *dfu)
196 {
197         int ret = 0;
198
199         if (dfu->layout != DFU_RAW_ADDR) {
200                 /* Do stuff here. */
201                 ret = mmc_file_op(DFU_OP_WRITE, dfu, dfu_file_buf,
202                                 &dfu_file_buf_len);
203
204                 /* Now that we're done */
205                 dfu_file_buf_len = 0;
206         }
207
208         return ret;
209 }
210
211 int dfu_get_medium_size_mmc(struct dfu_entity *dfu, u64 *size)
212 {
213         int ret;
214
215         switch (dfu->layout) {
216         case DFU_RAW_ADDR:
217                 *size = dfu->data.mmc.lba_size * dfu->data.mmc.lba_blk_size;
218                 return 0;
219         case DFU_FS_FAT:
220         case DFU_FS_EXT4:
221                 dfu_file_buf_filled = -1;
222                 ret = mmc_file_op(DFU_OP_SIZE, dfu, NULL, size);
223                 if (ret < 0)
224                         return ret;
225                 if (*size > CONFIG_SYS_DFU_MAX_FILE_SIZE)
226                         return -1;
227                 return 0;
228         default:
229                 printf("%s: Layout (%s) not (yet) supported!\n", __func__,
230                        dfu_get_layout(dfu->layout));
231                 return -1;
232         }
233 }
234
235 static int mmc_file_unbuffer(struct dfu_entity *dfu, u64 offset, void *buf,
236                              long *len)
237 {
238         int ret;
239         u64 file_len;
240
241         if (dfu_file_buf_filled == -1) {
242                 ret = mmc_file_op(DFU_OP_READ, dfu, dfu_file_buf, &file_len);
243                 if (ret < 0)
244                         return ret;
245                 dfu_file_buf_filled = file_len;
246         }
247         if (offset + *len > dfu_file_buf_filled)
248                 return -EINVAL;
249
250         /* Add to the current buffer. */
251         memcpy(buf, dfu_file_buf + offset, *len);
252
253         return 0;
254 }
255
256 int dfu_read_medium_mmc(struct dfu_entity *dfu, u64 offset, void *buf,
257                 long *len)
258 {
259         int ret = -1;
260
261         switch (dfu->layout) {
262         case DFU_RAW_ADDR:
263                 ret = mmc_block_op(DFU_OP_READ, dfu, offset, buf, len);
264                 break;
265         case DFU_FS_FAT:
266         case DFU_FS_EXT4:
267                 ret = mmc_file_unbuffer(dfu, offset, buf, len);
268                 break;
269         default:
270                 printf("%s: Layout (%s) not (yet) supported!\n", __func__,
271                        dfu_get_layout(dfu->layout));
272         }
273
274         return ret;
275 }
276
277 void dfu_free_entity_mmc(struct dfu_entity *dfu)
278 {
279         if (dfu_file_buf) {
280                 free(dfu_file_buf);
281                 dfu_file_buf = NULL;
282         }
283 }
284
285 /*
286  * @param s Parameter string containing space-separated arguments:
287  *      1st:
288  *              raw     (raw read/write)
289  *              fat     (files)
290  *              ext4    (^)
291  *              part    (partition image)
292  *      2nd and 3rd:
293  *              lba_start and lba_size, for raw write
294  *              mmc_dev and mmc_part, for filesystems and part
295  *      4th (optional):
296  *              mmcpart <num> (access to HW eMMC partitions)
297  */
298 int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *devstr, char *s)
299 {
300         const char *entity_type;
301         size_t second_arg;
302         size_t third_arg;
303
304         struct mmc *mmc;
305
306         const char *argv[3];
307         const char **parg = argv;
308
309         dfu->data.mmc.dev_num = simple_strtoul(devstr, NULL, 10);
310
311         for (; parg < argv + sizeof(argv) / sizeof(*argv); ++parg) {
312                 *parg = strsep(&s, " ");
313                 if (*parg == NULL) {
314                         pr_err("Invalid number of arguments.\n");
315                         return -ENODEV;
316                 }
317         }
318
319         entity_type = argv[0];
320         /*
321          * Base 0 means we'll accept (prefixed with 0x or 0) base 16, 8,
322          * with default 10.
323          */
324         second_arg = simple_strtoul(argv[1], NULL, 0);
325         third_arg = simple_strtoul(argv[2], NULL, 0);
326
327         mmc = find_mmc_device(dfu->data.mmc.dev_num);
328         if (mmc == NULL) {
329                 pr_err("Couldn't find MMC device no. %d.\n",
330                       dfu->data.mmc.dev_num);
331                 return -ENODEV;
332         }
333
334         if (mmc_init(mmc)) {
335                 pr_err("Couldn't init MMC device.\n");
336                 return -ENODEV;
337         }
338
339         dfu->data.mmc.hw_partition = -EINVAL;
340         if (!strcmp(entity_type, "raw")) {
341                 dfu->layout                     = DFU_RAW_ADDR;
342                 dfu->data.mmc.lba_start         = second_arg;
343                 dfu->data.mmc.lba_size          = third_arg;
344                 dfu->data.mmc.lba_blk_size      = mmc->read_bl_len;
345
346                 /*
347                  * Check for an extra entry at dfu_alt_info env variable
348                  * specifying the mmc HW defined partition number
349                  */
350                 if (s)
351                         if (!strcmp(strsep(&s, " "), "mmcpart"))
352                                 dfu->data.mmc.hw_partition =
353                                         simple_strtoul(s, NULL, 0);
354
355         } else if (!strcmp(entity_type, "part")) {
356                 disk_partition_t partinfo;
357                 struct blk_desc *blk_dev = mmc_get_blk_desc(mmc);
358                 int mmcdev = second_arg;
359                 int mmcpart = third_arg;
360
361                 if (part_get_info(blk_dev, mmcpart, &partinfo) != 0) {
362                         pr_err("Couldn't find part #%d on mmc device #%d\n",
363                               mmcpart, mmcdev);
364                         return -ENODEV;
365                 }
366
367                 dfu->layout                     = DFU_RAW_ADDR;
368                 dfu->data.mmc.lba_start         = partinfo.start;
369                 dfu->data.mmc.lba_size          = partinfo.size;
370                 dfu->data.mmc.lba_blk_size      = partinfo.blksz;
371         } else if (!strcmp(entity_type, "fat")) {
372                 dfu->layout = DFU_FS_FAT;
373         } else if (!strcmp(entity_type, "ext4")) {
374                 dfu->layout = DFU_FS_EXT4;
375         } else {
376                 pr_err("Memory layout (%s) not supported!\n", entity_type);
377                 return -ENODEV;
378         }
379
380         /* if it's NOT a raw write */
381         if (strcmp(entity_type, "raw")) {
382                 dfu->data.mmc.dev = second_arg;
383                 dfu->data.mmc.part = third_arg;
384         }
385
386         dfu->dev_type = DFU_DEV_MMC;
387         dfu->get_medium_size = dfu_get_medium_size_mmc;
388         dfu->read_medium = dfu_read_medium_mmc;
389         dfu->write_medium = dfu_write_medium_mmc;
390         dfu->flush_medium = dfu_flush_medium_mmc;
391         dfu->inited = 0;
392         dfu->free_entity = dfu_free_entity_mmc;
393
394         /* Check if file buffer is ready */
395         if (!dfu_file_buf) {
396                 dfu_file_buf = memalign(CONFIG_SYS_CACHELINE_SIZE,
397                                         CONFIG_SYS_DFU_MAX_FILE_SIZE);
398                 if (!dfu_file_buf) {
399                         pr_err("Could not memalign 0x%x bytes",
400                               CONFIG_SYS_DFU_MAX_FILE_SIZE);
401                         return -ENOMEM;
402                 }
403         }
404
405         return 0;
406 }