dfu_mmc.c 9.1 KB

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