dfu_mmc.c 9.9 KB

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