uboot/drivers/dfu/dfu_mmc.c
<<
>>
Prefs
   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 <log.h>
  11#include <malloc.h>
  12#include <errno.h>
  13#include <div64.h>
  14#include <dfu.h>
  15#include <ext4fs.h>
  16#include <fat.h>
  17#include <mmc.h>
  18#include <part.h>
  19#include <command.h>
  20
  21static unsigned char *dfu_file_buf;
  22static u64 dfu_file_buf_len;
  23static u64 dfu_file_buf_offset;
  24
  25static int mmc_block_op(enum dfu_op op, struct dfu_entity *dfu,
  26                        u64 offset, void *buf, long *len)
  27{
  28        struct mmc *mmc;
  29        u32 blk_start, blk_count, n = 0;
  30        int ret, part_num_bkp = 0;
  31
  32        mmc = find_mmc_device(dfu->data.mmc.dev_num);
  33        if (!mmc) {
  34                pr_err("Device MMC %d - not found!", dfu->data.mmc.dev_num);
  35                return -ENODEV;
  36        }
  37
  38        /*
  39         * We must ensure that we work in lba_blk_size chunks, so ALIGN
  40         * this value.
  41         */
  42        *len = ALIGN(*len, dfu->data.mmc.lba_blk_size);
  43
  44        blk_start = dfu->data.mmc.lba_start +
  45                        (u32)lldiv(offset, dfu->data.mmc.lba_blk_size);
  46        blk_count = *len / dfu->data.mmc.lba_blk_size;
  47        if (blk_start + blk_count >
  48                        dfu->data.mmc.lba_start + dfu->data.mmc.lba_size) {
  49                puts("Request would exceed designated area!\n");
  50                return -EINVAL;
  51        }
  52
  53        if (dfu->data.mmc.hw_partition >= 0) {
  54                part_num_bkp = mmc_get_blk_desc(mmc)->hwpart;
  55                ret = blk_select_hwpart_devnum(UCLASS_MMC,
  56                                               dfu->data.mmc.dev_num,
  57                                               dfu->data.mmc.hw_partition);
  58                if (ret)
  59                        return ret;
  60        }
  61
  62        debug("%s: %s dev: %d start: %d cnt: %d buf: 0x%p\n", __func__,
  63              op == DFU_OP_READ ? "MMC READ" : "MMC WRITE",
  64              dfu->data.mmc.dev_num, blk_start, blk_count, buf);
  65        switch (op) {
  66        case DFU_OP_READ:
  67                n = blk_dread(mmc_get_blk_desc(mmc), blk_start, blk_count, buf);
  68                break;
  69        case DFU_OP_WRITE:
  70                n = blk_dwrite(mmc_get_blk_desc(mmc), blk_start, blk_count,
  71                               buf);
  72                break;
  73        default:
  74                pr_err("Operation not supported\n");
  75        }
  76
  77        if (n != blk_count) {
  78                pr_err("MMC operation failed");
  79                if (dfu->data.mmc.hw_partition >= 0)
  80                        blk_select_hwpart_devnum(UCLASS_MMC,
  81                                                 dfu->data.mmc.dev_num,
  82                                                 part_num_bkp);
  83                return -EIO;
  84        }
  85
  86        if (dfu->data.mmc.hw_partition >= 0) {
  87                ret = blk_select_hwpart_devnum(UCLASS_MMC,
  88                                               dfu->data.mmc.dev_num,
  89                                               part_num_bkp);
  90                if (ret)
  91                        return ret;
  92        }
  93
  94        return 0;
  95}
  96
  97static int mmc_file_op(enum dfu_op op, struct dfu_entity *dfu,
  98                        u64 offset, void *buf, u64 *len)
  99{
 100        char dev_part_str[8];
 101        int ret;
 102        int fstype;
 103        loff_t size = 0;
 104
 105        switch (dfu->layout) {
 106        case DFU_FS_FAT:
 107                fstype = FS_TYPE_FAT;
 108                break;
 109        case DFU_FS_EXT4:
 110                fstype = FS_TYPE_EXT;
 111                break;
 112        case DFU_SKIP:
 113                return 0;
 114        default:
 115                printf("%s: Layout (%s) not (yet) supported!\n", __func__,
 116                       dfu_get_layout(dfu->layout));
 117                return -1;
 118        }
 119
 120        snprintf(dev_part_str, sizeof(dev_part_str), "%d:%d",
 121                 dfu->data.mmc.dev, dfu->data.mmc.part);
 122
 123        ret = fs_set_blk_dev("mmc", dev_part_str, fstype);
 124        if (ret) {
 125                puts("dfu: fs_set_blk_dev error!\n");
 126                return ret;
 127        }
 128
 129        switch (op) {
 130        case DFU_OP_READ:
 131                ret = fs_read(dfu->name, (size_t)buf, offset, *len, &size);
 132                if (ret) {
 133                        puts("dfu: fs_read error!\n");
 134                        return ret;
 135                }
 136                *len = size;
 137                break;
 138        case DFU_OP_WRITE:
 139                ret = fs_write(dfu->name, (size_t)buf, offset, *len, &size);
 140                if (ret) {
 141                        puts("dfu: fs_write error!\n");
 142                        return ret;
 143                }
 144                break;
 145        case DFU_OP_SIZE:
 146                ret = fs_size(dfu->name, &size);
 147                if (ret) {
 148                        puts("dfu: fs_size error!\n");
 149                        return ret;
 150                }
 151                *len = size;
 152                break;
 153        default:
 154                return -1;
 155        }
 156
 157        return ret;
 158}
 159
 160static int mmc_file_buf_write(struct dfu_entity *dfu, u64 offset, void *buf, long *len)
 161{
 162        int ret = 0;
 163
 164        if (offset == 0) {
 165                dfu_file_buf_len = 0;
 166                dfu_file_buf_offset = 0;
 167        }
 168
 169        /* Add to the current buffer. */
 170        if (dfu_file_buf_len + *len > CONFIG_SYS_DFU_MAX_FILE_SIZE)
 171                *len = CONFIG_SYS_DFU_MAX_FILE_SIZE - dfu_file_buf_len;
 172        memcpy(dfu_file_buf + dfu_file_buf_len, buf, *len);
 173        dfu_file_buf_len += *len;
 174
 175        if (dfu_file_buf_len == CONFIG_SYS_DFU_MAX_FILE_SIZE) {
 176                ret = mmc_file_op(DFU_OP_WRITE, dfu, dfu_file_buf_offset,
 177                                  dfu_file_buf, &dfu_file_buf_len);
 178                dfu_file_buf_offset += dfu_file_buf_len;
 179                dfu_file_buf_len = 0;
 180        }
 181
 182        return ret;
 183}
 184
 185static int mmc_file_buf_write_finish(struct dfu_entity *dfu)
 186{
 187        int ret = mmc_file_op(DFU_OP_WRITE, dfu, dfu_file_buf_offset,
 188                        dfu_file_buf, &dfu_file_buf_len);
 189
 190        /* Now that we're done */
 191        dfu_file_buf_len = 0;
 192        dfu_file_buf_offset = 0;
 193
 194        return ret;
 195}
 196
 197int dfu_write_medium_mmc(struct dfu_entity *dfu,
 198                u64 offset, void *buf, long *len)
 199{
 200        int ret = -1;
 201
 202        switch (dfu->layout) {
 203        case DFU_RAW_ADDR:
 204                ret = mmc_block_op(DFU_OP_WRITE, dfu, offset, buf, len);
 205                break;
 206        case DFU_FS_FAT:
 207        case DFU_FS_EXT4:
 208                ret = mmc_file_buf_write(dfu, offset, buf, len);
 209                break;
 210        case DFU_SCRIPT:
 211                ret = run_command_list(buf, *len, 0);
 212                break;
 213        case DFU_SKIP:
 214                ret = 0;
 215                break;
 216        default:
 217                printf("%s: Layout (%s) not (yet) supported!\n", __func__,
 218                       dfu_get_layout(dfu->layout));
 219        }
 220
 221        return ret;
 222}
 223
 224int dfu_flush_medium_mmc(struct dfu_entity *dfu)
 225{
 226        int ret = 0;
 227
 228        switch (dfu->layout) {
 229        case DFU_FS_FAT:
 230        case DFU_FS_EXT4:
 231                ret = mmc_file_buf_write_finish(dfu);
 232                break;
 233        case DFU_SCRIPT:
 234                /* script may have changed the dfu_alt_info */
 235                dfu_reinit_needed = true;
 236                break;
 237        case DFU_RAW_ADDR:
 238        case DFU_SKIP:
 239                break;
 240        default:
 241                printf("%s: Layout (%s) not (yet) supported!\n", __func__,
 242                       dfu_get_layout(dfu->layout));
 243        }
 244
 245        return ret;
 246}
 247
 248int dfu_get_medium_size_mmc(struct dfu_entity *dfu, u64 *size)
 249{
 250        int ret;
 251
 252        switch (dfu->layout) {
 253        case DFU_RAW_ADDR:
 254                *size = dfu->data.mmc.lba_size * dfu->data.mmc.lba_blk_size;
 255                return 0;
 256        case DFU_FS_FAT:
 257        case DFU_FS_EXT4:
 258                ret = mmc_file_op(DFU_OP_SIZE, dfu, 0, NULL, size);
 259                if (ret < 0)
 260                        return ret;
 261                return 0;
 262        case DFU_SCRIPT:
 263        case DFU_SKIP:
 264                return 0;
 265        default:
 266                printf("%s: Layout (%s) not (yet) supported!\n", __func__,
 267                       dfu_get_layout(dfu->layout));
 268                return -1;
 269        }
 270}
 271
 272
 273static int mmc_file_buf_read(struct dfu_entity *dfu, u64 offset, void *buf,
 274                             long *len)
 275{
 276        int ret;
 277
 278        if (offset == 0 || offset >= dfu_file_buf_offset + dfu_file_buf_len ||
 279            offset + *len < dfu_file_buf_offset) {
 280                u64 file_len = CONFIG_SYS_DFU_MAX_FILE_SIZE;
 281
 282                ret = mmc_file_op(DFU_OP_READ, dfu, offset, dfu_file_buf,
 283                                  &file_len);
 284                if (ret < 0)
 285                        return ret;
 286                dfu_file_buf_len = file_len;
 287                dfu_file_buf_offset = offset;
 288        }
 289        if (offset + *len > dfu_file_buf_offset + dfu_file_buf_len)
 290                return -EINVAL;
 291
 292        /* Add to the current buffer. */
 293        memcpy(buf, dfu_file_buf + offset - dfu_file_buf_offset, *len);
 294
 295        return 0;
 296}
 297
 298int dfu_read_medium_mmc(struct dfu_entity *dfu, u64 offset, void *buf,
 299                long *len)
 300{
 301        int ret = -1;
 302
 303        switch (dfu->layout) {
 304        case DFU_RAW_ADDR:
 305                ret = mmc_block_op(DFU_OP_READ, dfu, offset, buf, len);
 306                break;
 307        case DFU_FS_FAT:
 308        case DFU_FS_EXT4:
 309                ret = mmc_file_buf_read(dfu, offset, buf, len);
 310                break;
 311        default:
 312                printf("%s: Layout (%s) not (yet) supported!\n", __func__,
 313                       dfu_get_layout(dfu->layout));
 314        }
 315
 316        return ret;
 317}
 318
 319void dfu_free_entity_mmc(struct dfu_entity *dfu)
 320{
 321        if (dfu_file_buf) {
 322                free(dfu_file_buf);
 323                dfu_file_buf = NULL;
 324        }
 325}
 326
 327/*
 328 * @param s Parameter string containing space-separated arguments:
 329 *      1st:
 330 *              raw     (raw read/write)
 331 *              fat     (files)
 332 *              ext4    (^)
 333 *              part    (partition image)
 334 *      2nd and 3rd:
 335 *              lba_start and lba_size, for raw write
 336 *              mmc_dev and mmc_part, for filesystems and part
 337 *      4th (optional):
 338 *              mmcpart <num> (access to HW eMMC partitions)
 339 */
 340int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *devstr, char **argv, int argc)
 341{
 342        const char *entity_type;
 343        ssize_t second_arg;
 344        size_t third_arg;
 345        struct mmc *mmc;
 346        char *s;
 347
 348        if (argc < 3) {
 349                pr_err("The number of parameters are not enough.\n");
 350                return -EINVAL;
 351        }
 352
 353        dfu->data.mmc.dev_num = dectoul(devstr, &s);
 354        if (*s)
 355                return -EINVAL;
 356
 357        entity_type = argv[0];
 358        /*
 359         * Base 0 means we'll accept (prefixed with 0x or 0) base 16, 8,
 360         * with default 10.
 361         */
 362        second_arg = simple_strtol(argv[1], &s, 0);
 363        if (*s)
 364                return -EINVAL;
 365        third_arg = simple_strtoul(argv[2], &s, 0);
 366        if (*s)
 367                return -EINVAL;
 368
 369        mmc = find_mmc_device(dfu->data.mmc.dev_num);
 370        if (mmc == NULL) {
 371                pr_err("Couldn't find MMC device no. %d.\n",
 372                      dfu->data.mmc.dev_num);
 373                return -ENODEV;
 374        }
 375
 376        if (mmc_init(mmc)) {
 377                pr_err("Couldn't init MMC device.\n");
 378                return -ENODEV;
 379        }
 380
 381        dfu->data.mmc.hw_partition = -EINVAL;
 382        if (!strcmp(entity_type, "raw")) {
 383                dfu->layout                     = DFU_RAW_ADDR;
 384                dfu->data.mmc.lba_start         = second_arg;
 385                dfu->data.mmc.lba_size          = third_arg;
 386                dfu->data.mmc.lba_blk_size      = mmc->read_bl_len;
 387
 388                /*
 389                 * Check for an extra entry at dfu_alt_info env variable
 390                 * specifying the mmc HW defined partition number
 391                 */
 392                if (argc > 3) {
 393                        if (argc != 5 || strcmp(argv[3], "mmcpart")) {
 394                                pr_err("DFU mmc raw accept 'mmcpart <partnum>' option.\n");
 395                                return -EINVAL;
 396                        }
 397                        dfu->data.mmc.hw_partition =
 398                                simple_strtoul(argv[4], NULL, 0);
 399                }
 400
 401        } else if (!strcmp(entity_type, "part")) {
 402                struct disk_partition partinfo;
 403                struct blk_desc *blk_dev = mmc_get_blk_desc(mmc);
 404                int mmcdev = second_arg;
 405                int mmcpart = third_arg;
 406                int offset = 0;
 407
 408                if (part_get_info(blk_dev, mmcpart, &partinfo) != 0) {
 409                        pr_err("Couldn't find part #%d on mmc device #%d\n",
 410                              mmcpart, mmcdev);
 411                        return -ENODEV;
 412                }
 413
 414                /*
 415                 * Check for an extra entry at dfu_alt_info env variable
 416                 * specifying the mmc HW defined partition number
 417                 */
 418                if (argc > 3) {
 419                        if (argc != 5 || strcmp(argv[3], "offset")) {
 420                                pr_err("DFU mmc raw accept 'mmcpart <partnum>' option.\n");
 421                                return -EINVAL;
 422                        }
 423                        dfu->data.mmc.hw_partition =
 424                                simple_strtoul(argv[4], NULL, 0);
 425                }
 426
 427                dfu->layout                     = DFU_RAW_ADDR;
 428                dfu->data.mmc.lba_start         = partinfo.start + offset;
 429                dfu->data.mmc.lba_size          = partinfo.size - offset;
 430                dfu->data.mmc.lba_blk_size      = partinfo.blksz;
 431        } else if (!strcmp(entity_type, "fat")) {
 432                dfu->layout = DFU_FS_FAT;
 433        } else if (!strcmp(entity_type, "ext4")) {
 434                dfu->layout = DFU_FS_EXT4;
 435        } else if (!strcmp(entity_type, "skip")) {
 436                dfu->layout = DFU_SKIP;
 437        } else if (!strcmp(entity_type, "script")) {
 438                dfu->layout = DFU_SCRIPT;
 439        } else {
 440                pr_err("Memory layout (%s) not supported!\n", entity_type);
 441                return -ENODEV;
 442        }
 443
 444        /* if it's NOT a raw write */
 445        if (strcmp(entity_type, "raw")) {
 446                dfu->data.mmc.dev = (second_arg != -1) ? second_arg :
 447                                                         dfu->data.mmc.dev_num;
 448                dfu->data.mmc.part = third_arg;
 449        }
 450
 451        dfu->dev_type = DFU_DEV_MMC;
 452        dfu->get_medium_size = dfu_get_medium_size_mmc;
 453        dfu->read_medium = dfu_read_medium_mmc;
 454        dfu->write_medium = dfu_write_medium_mmc;
 455        dfu->flush_medium = dfu_flush_medium_mmc;
 456        dfu->inited = 0;
 457        dfu->free_entity = dfu_free_entity_mmc;
 458
 459        /* Check if file buffer is ready */
 460        if (!dfu_file_buf) {
 461                dfu_file_buf = memalign(CONFIG_SYS_CACHELINE_SIZE,
 462                                        CONFIG_SYS_DFU_MAX_FILE_SIZE);
 463                if (!dfu_file_buf) {
 464                        pr_err("Could not memalign 0x%x bytes",
 465                              CONFIG_SYS_DFU_MAX_FILE_SIZE);
 466                        return -ENOMEM;
 467                }
 468        }
 469
 470        return 0;
 471}
 472