dfu_mmc.c 9.1 KB

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