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