dfu_mmc.c 9.1 KB

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