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