mtdsplit_fit.c 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. /*
  2. * Copyright (c) 2015 The Linux Foundation
  3. * Copyright (C) 2014 Gabor Juhos <[email protected]>
  4. *
  5. * Permission to use, copy, modify, and/or distribute this software for any
  6. * purpose with or without fee is hereby granted, provided that the above
  7. * copyright notice and this permission notice appear in all copies.
  8. *
  9. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  10. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  11. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  12. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  13. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  14. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  15. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  16. */
  17. #include <linux/module.h>
  18. #include <linux/mtd/mtd.h>
  19. #include <linux/mtd/partitions.h>
  20. #include <linux/types.h>
  21. #include <linux/byteorder/generic.h>
  22. #include <linux/slab.h>
  23. #include <linux/libfdt.h>
  24. #include <linux/of_fdt.h>
  25. #include "mtdsplit.h"
  26. // string macros from git://git.denx.de/u-boot.git/include/image.h
  27. #define FIT_IMAGES_PATH "/images"
  28. #define FIT_DATA_PROP "data"
  29. #define FIT_DATA_POSITION_PROP "data-position"
  30. #define FIT_DATA_OFFSET_PROP "data-offset"
  31. #define FIT_DATA_SIZE_PROP "data-size"
  32. // functions from git://git.denx.de/u-boot.git/common/image-fit.c
  33. /**
  34. * fit_image_get_data - get data property and its size for a given component image node
  35. * @fit: pointer to the FIT format image header
  36. * @noffset: component image node offset
  37. * @data: double pointer to void, will hold data property's data address
  38. * @size: pointer to size_t, will hold data property's data size
  39. *
  40. * fit_image_get_data() finds data property in a given component image node.
  41. * If the property is found its data start address and size are returned to
  42. * the caller.
  43. *
  44. * returns:
  45. * 0, on success
  46. * -1, on failure
  47. */
  48. static int fit_image_get_data(const void *fit, int noffset,
  49. const void **data, size_t *size)
  50. {
  51. int len;
  52. *data = fdt_getprop(fit, noffset, FIT_DATA_PROP, &len);
  53. if (*data == NULL) {
  54. *size = 0;
  55. return -1;
  56. }
  57. *size = len;
  58. return 0;
  59. }
  60. /**
  61. * Get 'data-offset' property from a given image node.
  62. *
  63. * @fit: pointer to the FIT image header
  64. * @noffset: component image node offset
  65. * @data_offset: holds the data-offset property
  66. *
  67. * returns:
  68. * 0, on success
  69. * -ENOENT if the property could not be found
  70. */
  71. static int fit_image_get_data_offset(const void *fit, int noffset, int *data_offset)
  72. {
  73. const fdt32_t *val;
  74. val = fdt_getprop(fit, noffset, FIT_DATA_OFFSET_PROP, NULL);
  75. if (!val)
  76. return -ENOENT;
  77. *data_offset = fdt32_to_cpu(*val);
  78. return 0;
  79. }
  80. /**
  81. * Get 'data-position' property from a given image node.
  82. *
  83. * @fit: pointer to the FIT image header
  84. * @noffset: component image node offset
  85. * @data_position: holds the data-position property
  86. *
  87. * returns:
  88. * 0, on success
  89. * -ENOENT if the property could not be found
  90. */
  91. static int fit_image_get_data_position(const void *fit, int noffset,
  92. int *data_position)
  93. {
  94. const fdt32_t *val;
  95. val = fdt_getprop(fit, noffset, FIT_DATA_POSITION_PROP, NULL);
  96. if (!val)
  97. return -ENOENT;
  98. *data_position = fdt32_to_cpu(*val);
  99. return 0;
  100. }
  101. /**
  102. * Get 'data-size' property from a given image node.
  103. *
  104. * @fit: pointer to the FIT image header
  105. * @noffset: component image node offset
  106. * @data_size: holds the data-size property
  107. *
  108. * returns:
  109. * 0, on success
  110. * -ENOENT if the property could not be found
  111. */
  112. static int fit_image_get_data_size(const void *fit, int noffset, int *data_size)
  113. {
  114. const fdt32_t *val;
  115. val = fdt_getprop(fit, noffset, FIT_DATA_SIZE_PROP, NULL);
  116. if (!val)
  117. return -ENOENT;
  118. *data_size = fdt32_to_cpu(*val);
  119. return 0;
  120. }
  121. /**
  122. * fit_image_get_data_and_size - get data and its size including
  123. * both embedded and external data
  124. * @fit: pointer to the FIT format image header
  125. * @noffset: component image node offset
  126. * @data: double pointer to void, will hold data property's data address
  127. * @size: pointer to size_t, will hold data property's data size
  128. *
  129. * fit_image_get_data_and_size() finds data and its size including
  130. * both embedded and external data. If the property is found
  131. * its data start address and size are returned to the caller.
  132. *
  133. * returns:
  134. * 0, on success
  135. * otherwise, on failure
  136. */
  137. static int fit_image_get_data_and_size(const void *fit, int noffset,
  138. const void **data, size_t *size)
  139. {
  140. bool external_data = false;
  141. int offset;
  142. int len;
  143. int ret;
  144. if (!fit_image_get_data_position(fit, noffset, &offset)) {
  145. external_data = true;
  146. } else if (!fit_image_get_data_offset(fit, noffset, &offset)) {
  147. external_data = true;
  148. /*
  149. * For FIT with external data, figure out where
  150. * the external images start. This is the base
  151. * for the data-offset properties in each image.
  152. */
  153. offset += ((fdt_totalsize(fit) + 3) & ~3);
  154. }
  155. if (external_data) {
  156. ret = fit_image_get_data_size(fit, noffset, &len);
  157. if (!ret) {
  158. *data = fit + offset;
  159. *size = len;
  160. }
  161. } else {
  162. ret = fit_image_get_data(fit, noffset, data, size);
  163. }
  164. return ret;
  165. }
  166. static int
  167. mtdsplit_fit_parse(struct mtd_info *mtd,
  168. const struct mtd_partition **pparts,
  169. struct mtd_part_parser_data *data)
  170. {
  171. struct device_node *np = mtd_get_of_node(mtd);
  172. const char *cmdline_match = NULL;
  173. struct fdt_header hdr;
  174. size_t hdr_len, retlen;
  175. size_t offset;
  176. u32 offset_start = 0;
  177. size_t fit_offset, fit_size;
  178. size_t rootfs_offset, rootfs_size;
  179. size_t data_size, img_total, max_size = 0;
  180. struct mtd_partition *parts;
  181. int ret, ndepth, noffset, images_noffset;
  182. const void *img_data;
  183. void *fit;
  184. of_property_read_string(np, "openwrt,cmdline-match", &cmdline_match);
  185. if (cmdline_match && !strstr(saved_command_line, cmdline_match))
  186. return -ENODEV;
  187. of_property_read_u32(np, "openwrt,fit-offset", &offset_start);
  188. hdr_len = sizeof(struct fdt_header);
  189. /* Parse the MTD device & search for the FIT image location */
  190. for(offset = 0; offset + hdr_len <= mtd->size; offset += mtd->erasesize) {
  191. ret = mtd_read(mtd, offset + offset_start, hdr_len, &retlen, (void*) &hdr);
  192. if (ret) {
  193. pr_err("read error in \"%s\" at offset 0x%llx\n",
  194. mtd->name, (unsigned long long) offset);
  195. return ret;
  196. }
  197. if (retlen != hdr_len) {
  198. pr_err("short read in \"%s\"\n", mtd->name);
  199. return -EIO;
  200. }
  201. /* Check the magic - see if this is a FIT image */
  202. if (be32_to_cpu(hdr.magic) != OF_DT_HEADER) {
  203. pr_debug("no valid FIT image found in \"%s\" at offset %llx\n",
  204. mtd->name, (unsigned long long) offset);
  205. continue;
  206. }
  207. /* We found a FIT image. Let's keep going */
  208. break;
  209. }
  210. fit_offset = offset;
  211. fit_size = be32_to_cpu(hdr.totalsize);
  212. if (fit_size == 0) {
  213. pr_err("FIT image in \"%s\" at offset %llx has null size\n",
  214. mtd->name, (unsigned long long) fit_offset);
  215. return -ENODEV;
  216. }
  217. /*
  218. * Classic uImage.FIT has all data embedded into the FDT
  219. * data structure. Hence the total size of the image equals
  220. * the total size of the FDT structure.
  221. * Modern uImage.FIT may have only references to data in FDT,
  222. * hence we need to parse FDT structure to find the end of the
  223. * last external data refernced.
  224. */
  225. if (fit_size > 0x1000) {
  226. enum mtdsplit_part_type type;
  227. /* Search for the rootfs partition after the FIT image */
  228. ret = mtd_find_rootfs_from(mtd, fit_offset + fit_size + offset_start, mtd->size,
  229. &rootfs_offset, &type);
  230. if (ret) {
  231. pr_info("no rootfs found after FIT image in \"%s\"\n",
  232. mtd->name);
  233. return ret;
  234. }
  235. rootfs_size = mtd->size - rootfs_offset;
  236. parts = kzalloc(2 * sizeof(*parts), GFP_KERNEL);
  237. if (!parts)
  238. return -ENOMEM;
  239. parts[0].name = KERNEL_PART_NAME;
  240. parts[0].offset = fit_offset;
  241. parts[0].size = mtd_roundup_to_eb(fit_size + offset_start, mtd);
  242. if (type == MTDSPLIT_PART_TYPE_UBI)
  243. parts[1].name = UBI_PART_NAME;
  244. else
  245. parts[1].name = ROOTFS_PART_NAME;
  246. parts[1].offset = rootfs_offset;
  247. parts[1].size = rootfs_size;
  248. *pparts = parts;
  249. return 2;
  250. } else {
  251. /* Search for rootfs_data after FIT external data */
  252. fit = kzalloc(fit_size, GFP_KERNEL);
  253. ret = mtd_read(mtd, offset, fit_size + offset_start, &retlen, fit);
  254. if (ret) {
  255. pr_err("read error in \"%s\" at offset 0x%llx\n",
  256. mtd->name, (unsigned long long) offset);
  257. return ret;
  258. }
  259. images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH);
  260. if (images_noffset < 0) {
  261. pr_err("Can't find images parent node '%s' (%s)\n",
  262. FIT_IMAGES_PATH, fdt_strerror(images_noffset));
  263. return -ENODEV;
  264. }
  265. for (ndepth = 0,
  266. noffset = fdt_next_node(fit, images_noffset, &ndepth);
  267. (noffset >= 0) && (ndepth > 0);
  268. noffset = fdt_next_node(fit, noffset, &ndepth)) {
  269. if (ndepth == 1) {
  270. ret = fit_image_get_data_and_size(fit, noffset, &img_data, &data_size);
  271. if (ret)
  272. return 0;
  273. img_total = data_size + (img_data - fit);
  274. max_size = (max_size > img_total) ? max_size : img_total;
  275. }
  276. }
  277. parts = kzalloc(sizeof(*parts), GFP_KERNEL);
  278. if (!parts)
  279. return -ENOMEM;
  280. parts[0].name = ROOTFS_SPLIT_NAME;
  281. parts[0].offset = fit_offset + mtd_roundup_to_eb(max_size, mtd);
  282. parts[0].size = mtd->size - parts[0].offset;
  283. *pparts = parts;
  284. kfree(fit);
  285. return 1;
  286. }
  287. }
  288. static const struct of_device_id mtdsplit_fit_of_match_table[] = {
  289. { .compatible = "denx,fit" },
  290. {},
  291. };
  292. static struct mtd_part_parser uimage_parser = {
  293. .owner = THIS_MODULE,
  294. .name = "fit-fw",
  295. .of_match_table = mtdsplit_fit_of_match_table,
  296. .parse_fn = mtdsplit_fit_parse,
  297. .type = MTD_PARSER_TYPE_FIRMWARE,
  298. };
  299. /**************************************************
  300. * Init
  301. **************************************************/
  302. static int __init mtdsplit_fit_init(void)
  303. {
  304. register_mtd_parser(&uimage_parser);
  305. return 0;
  306. }
  307. module_init(mtdsplit_fit_init);