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 __aligned(CONFIG_SYS_CACHELINE_SIZE) 20 dfu_file_buf[CONFIG_SYS_DFU_MAX_FILE_SIZE]; 21 static long dfu_file_buf_len; 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), " 0x%x", (unsigned int)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 ret = mmc_file_op(DFU_OP_SIZE, dfu, NULL, &len); 235 if (ret < 0) 236 return ret; 237 return len; 238 default: 239 printf("%s: Layout (%s) not (yet) supported!\n", __func__, 240 dfu_get_layout(dfu->layout)); 241 return -1; 242 } 243 } 244 245 int dfu_read_medium_mmc(struct dfu_entity *dfu, u64 offset, void *buf, 246 long *len) 247 { 248 int ret = -1; 249 250 switch (dfu->layout) { 251 case DFU_RAW_ADDR: 252 ret = mmc_block_op(DFU_OP_READ, dfu, offset, buf, len); 253 break; 254 case DFU_FS_FAT: 255 case DFU_FS_EXT4: 256 ret = mmc_file_op(DFU_OP_READ, dfu, buf, len); 257 break; 258 default: 259 printf("%s: Layout (%s) not (yet) supported!\n", __func__, 260 dfu_get_layout(dfu->layout)); 261 } 262 263 return ret; 264 } 265 266 /* 267 * @param s Parameter string containing space-separated arguments: 268 * 1st: 269 * raw (raw read/write) 270 * fat (files) 271 * ext4 (^) 272 * part (partition image) 273 * 2nd and 3rd: 274 * lba_start and lba_size, for raw write 275 * mmc_dev and mmc_part, for filesystems and part 276 * 4th (optional): 277 * mmcpart <num> (access to HW eMMC partitions) 278 */ 279 int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *devstr, char *s) 280 { 281 const char *entity_type; 282 size_t second_arg; 283 size_t third_arg; 284 285 struct mmc *mmc; 286 287 const char *argv[3]; 288 const char **parg = argv; 289 290 dfu->data.mmc.dev_num = simple_strtoul(devstr, NULL, 10); 291 292 for (; parg < argv + sizeof(argv) / sizeof(*argv); ++parg) { 293 *parg = strsep(&s, " "); 294 if (*parg == NULL) { 295 error("Invalid number of arguments.\n"); 296 return -ENODEV; 297 } 298 } 299 300 entity_type = argv[0]; 301 /* 302 * Base 0 means we'll accept (prefixed with 0x or 0) base 16, 8, 303 * with default 10. 304 */ 305 second_arg = simple_strtoul(argv[1], NULL, 0); 306 third_arg = simple_strtoul(argv[2], NULL, 0); 307 308 mmc = find_mmc_device(dfu->data.mmc.dev_num); 309 if (mmc == NULL) { 310 error("Couldn't find MMC device no. %d.\n", 311 dfu->data.mmc.dev_num); 312 return -ENODEV; 313 } 314 315 if (mmc_init(mmc)) { 316 error("Couldn't init MMC device.\n"); 317 return -ENODEV; 318 } 319 320 dfu->data.mmc.hw_partition = -EINVAL; 321 if (!strcmp(entity_type, "raw")) { 322 dfu->layout = DFU_RAW_ADDR; 323 dfu->data.mmc.lba_start = second_arg; 324 dfu->data.mmc.lba_size = third_arg; 325 dfu->data.mmc.lba_blk_size = mmc->read_bl_len; 326 327 /* 328 * Check for an extra entry at dfu_alt_info env variable 329 * specifying the mmc HW defined partition number 330 */ 331 if (s) 332 if (!strcmp(strsep(&s, " "), "mmcpart")) 333 dfu->data.mmc.hw_partition = 334 simple_strtoul(s, NULL, 0); 335 336 } else if (!strcmp(entity_type, "part")) { 337 disk_partition_t partinfo; 338 block_dev_desc_t *blk_dev = &mmc->block_dev; 339 int mmcdev = second_arg; 340 int mmcpart = third_arg; 341 342 if (get_partition_info(blk_dev, mmcpart, &partinfo) != 0) { 343 error("Couldn't find part #%d on mmc device #%d\n", 344 mmcpart, mmcdev); 345 return -ENODEV; 346 } 347 348 dfu->layout = DFU_RAW_ADDR; 349 dfu->data.mmc.lba_start = partinfo.start; 350 dfu->data.mmc.lba_size = partinfo.size; 351 dfu->data.mmc.lba_blk_size = partinfo.blksz; 352 } else if (!strcmp(entity_type, "fat")) { 353 dfu->layout = DFU_FS_FAT; 354 } else if (!strcmp(entity_type, "ext4")) { 355 dfu->layout = DFU_FS_EXT4; 356 } else { 357 error("Memory layout (%s) not supported!\n", entity_type); 358 return -ENODEV; 359 } 360 361 /* if it's NOT a raw write */ 362 if (strcmp(entity_type, "raw")) { 363 dfu->data.mmc.dev = second_arg; 364 dfu->data.mmc.part = third_arg; 365 } 366 367 dfu->dev_type = DFU_DEV_MMC; 368 dfu->get_medium_size = dfu_get_medium_size_mmc; 369 dfu->read_medium = dfu_read_medium_mmc; 370 dfu->write_medium = dfu_write_medium_mmc; 371 dfu->flush_medium = dfu_flush_medium_mmc; 372 dfu->inited = 0; 373 374 return 0; 375 } 376