raw_mtd.c 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * Copyright (c) 2023, Linaro Limited
  4. */
  5. #define LOG_CATEGORY UCLASS_FWU_MDATA
  6. #include <fwu.h>
  7. #include <fwu_mdata.h>
  8. #include <memalign.h>
  9. #include <linux/errno.h>
  10. #include <linux/types.h>
  11. /* Internal helper structure to move data around */
  12. struct fwu_mdata_mtd_priv {
  13. struct mtd_info *mtd;
  14. char pri_label[50];
  15. char sec_label[50];
  16. u32 pri_offset;
  17. u32 sec_offset;
  18. };
  19. enum fwu_mtd_op {
  20. FWU_MTD_READ,
  21. FWU_MTD_WRITE,
  22. };
  23. extern struct fwu_mtd_image_info fwu_mtd_images[];
  24. static bool mtd_is_aligned_with_block_size(struct mtd_info *mtd, u64 size)
  25. {
  26. return !do_div(size, mtd->erasesize);
  27. }
  28. static int mtd_io_data(struct mtd_info *mtd, u32 offs, u32 size, void *data,
  29. enum fwu_mtd_op op)
  30. {
  31. struct mtd_oob_ops io_op = {};
  32. u64 lock_len;
  33. size_t len;
  34. void *buf;
  35. int ret;
  36. if (!mtd_is_aligned_with_block_size(mtd, offs)) {
  37. log_err("Offset unaligned with a block (0x%x)\n", mtd->erasesize);
  38. return -EINVAL;
  39. }
  40. /* This will expand erase size to align with the block size */
  41. lock_len = round_up(size, mtd->erasesize);
  42. ret = mtd_unlock(mtd, offs, lock_len);
  43. if (ret && ret != -EOPNOTSUPP)
  44. return ret;
  45. if (op == FWU_MTD_WRITE) {
  46. struct erase_info erase_op = {};
  47. erase_op.mtd = mtd;
  48. erase_op.addr = offs;
  49. erase_op.len = lock_len;
  50. erase_op.scrub = 0;
  51. ret = mtd_erase(mtd, &erase_op);
  52. if (ret)
  53. goto lock;
  54. }
  55. /* Also, expand the write size to align with the write size */
  56. len = round_up(size, mtd->writesize);
  57. buf = memalign(ARCH_DMA_MINALIGN, len);
  58. if (!buf) {
  59. ret = -ENOMEM;
  60. goto lock;
  61. }
  62. memset(buf, 0xff, len);
  63. io_op.mode = MTD_OPS_AUTO_OOB;
  64. io_op.len = len;
  65. io_op.datbuf = buf;
  66. if (op == FWU_MTD_WRITE) {
  67. memcpy(buf, data, size);
  68. ret = mtd_write_oob(mtd, offs, &io_op);
  69. } else {
  70. ret = mtd_read_oob(mtd, offs, &io_op);
  71. if (!ret)
  72. memcpy(data, buf, size);
  73. }
  74. free(buf);
  75. lock:
  76. mtd_lock(mtd, offs, lock_len);
  77. return ret;
  78. }
  79. static int fwu_mtd_read_mdata(struct udevice *dev, struct fwu_mdata *mdata, bool primary)
  80. {
  81. struct fwu_mdata_mtd_priv *mtd_priv = dev_get_priv(dev);
  82. struct mtd_info *mtd = mtd_priv->mtd;
  83. u32 offs = primary ? mtd_priv->pri_offset : mtd_priv->sec_offset;
  84. return mtd_io_data(mtd, offs, sizeof(struct fwu_mdata), mdata, FWU_MTD_READ);
  85. }
  86. static int fwu_mtd_write_mdata(struct udevice *dev, struct fwu_mdata *mdata, bool primary)
  87. {
  88. struct fwu_mdata_mtd_priv *mtd_priv = dev_get_priv(dev);
  89. struct mtd_info *mtd = mtd_priv->mtd;
  90. u32 offs = primary ? mtd_priv->pri_offset : mtd_priv->sec_offset;
  91. return mtd_io_data(mtd, offs, sizeof(struct fwu_mdata), mdata, FWU_MTD_WRITE);
  92. }
  93. static int flash_partition_offset(struct udevice *dev, const char *part_name, fdt_addr_t *offset)
  94. {
  95. ofnode node, parts_node;
  96. fdt_addr_t size = 0;
  97. parts_node = ofnode_by_compatible(dev_ofnode(dev), "fixed-partitions");
  98. node = ofnode_by_prop_value(parts_node, "label", part_name, strlen(part_name) + 1);
  99. if (!ofnode_valid(node)) {
  100. log_err("Warning: Failed to find partition by label <%s>\n", part_name);
  101. return -ENOENT;
  102. }
  103. *offset = ofnode_get_addr_size_index_notrans(node, 0, &size);
  104. return (int)size;
  105. }
  106. static int fwu_mdata_mtd_of_to_plat(struct udevice *dev)
  107. {
  108. struct fwu_mdata_mtd_priv *mtd_priv = dev_get_priv(dev);
  109. const fdt32_t *phandle_p = NULL;
  110. struct udevice *mtd_dev;
  111. struct mtd_info *mtd;
  112. const char *label;
  113. fdt_addr_t offset;
  114. int ret, size;
  115. u32 phandle;
  116. ofnode bank;
  117. int off_img;
  118. /* Find the FWU mdata storage device */
  119. phandle_p = ofnode_get_property(dev_ofnode(dev),
  120. "fwu-mdata-store", &size);
  121. if (!phandle_p) {
  122. log_err("FWU meta data store not defined in device-tree\n");
  123. return -ENOENT;
  124. }
  125. phandle = fdt32_to_cpu(*phandle_p);
  126. ret = device_get_global_by_ofnode(ofnode_get_by_phandle(phandle),
  127. &mtd_dev);
  128. if (ret) {
  129. log_err("FWU: failed to get mtd device\n");
  130. return ret;
  131. }
  132. mtd_probe_devices();
  133. mtd_for_each_device(mtd) {
  134. if (mtd->dev == mtd_dev) {
  135. mtd_priv->mtd = mtd;
  136. log_debug("Found the FWU mdata mtd device %s\n", mtd->name);
  137. break;
  138. }
  139. }
  140. if (!mtd_priv->mtd) {
  141. log_err("Failed to find mtd device by fwu-mdata-store\n");
  142. return -ENODEV;
  143. }
  144. /* Get the offset of primary and secondary mdata */
  145. ret = ofnode_read_string_index(dev_ofnode(dev), "mdata-parts", 0, &label);
  146. if (ret)
  147. return ret;
  148. strncpy(mtd_priv->pri_label, label, 50);
  149. ret = flash_partition_offset(mtd_dev, mtd_priv->pri_label, &offset);
  150. if (ret <= 0)
  151. return ret;
  152. mtd_priv->pri_offset = offset;
  153. ret = ofnode_read_string_index(dev_ofnode(dev), "mdata-parts", 1, &label);
  154. if (ret)
  155. return ret;
  156. strncpy(mtd_priv->sec_label, label, 50);
  157. ret = flash_partition_offset(mtd_dev, mtd_priv->sec_label, &offset);
  158. if (ret <= 0)
  159. return ret;
  160. mtd_priv->sec_offset = offset;
  161. off_img = 0;
  162. ofnode_for_each_subnode(bank, dev_ofnode(dev)) {
  163. int bank_num, bank_offset, bank_size;
  164. const char *bank_name;
  165. ofnode image;
  166. ofnode_read_u32(bank, "id", &bank_num);
  167. bank_name = ofnode_read_string(bank, "label");
  168. bank_size = flash_partition_offset(mtd_dev, bank_name, &offset);
  169. if (bank_size <= 0)
  170. return bank_size;
  171. bank_offset = offset;
  172. log_debug("Bank%d: %s [0x%x - 0x%x]\n",
  173. bank_num, bank_name, bank_offset, bank_offset + bank_size);
  174. ofnode_for_each_subnode(image, bank) {
  175. int image_num, image_offset, image_size;
  176. const char *uuid;
  177. if (off_img == CONFIG_FWU_NUM_BANKS *
  178. CONFIG_FWU_NUM_IMAGES_PER_BANK) {
  179. log_err("DT provides more images than configured!\n");
  180. break;
  181. }
  182. uuid = ofnode_read_string(image, "uuid");
  183. ofnode_read_u32(image, "id", &image_num);
  184. ofnode_read_u32(image, "offset", &image_offset);
  185. ofnode_read_u32(image, "size", &image_size);
  186. fwu_mtd_images[off_img].start = bank_offset + image_offset;
  187. fwu_mtd_images[off_img].size = image_size;
  188. fwu_mtd_images[off_img].bank_num = bank_num;
  189. fwu_mtd_images[off_img].image_num = image_num;
  190. strcpy(fwu_mtd_images[off_img].uuidbuf, uuid);
  191. log_debug("\tImage%d: %s @0x%x\n\n",
  192. image_num, uuid, bank_offset + image_offset);
  193. off_img++;
  194. }
  195. }
  196. return 0;
  197. }
  198. static int fwu_mdata_mtd_probe(struct udevice *dev)
  199. {
  200. /* Ensure the metadata can be read. */
  201. return fwu_get_mdata(NULL);
  202. }
  203. static struct fwu_mdata_ops fwu_mtd_ops = {
  204. .read_mdata = fwu_mtd_read_mdata,
  205. .write_mdata = fwu_mtd_write_mdata,
  206. };
  207. static const struct udevice_id fwu_mdata_ids[] = {
  208. { .compatible = "u-boot,fwu-mdata-mtd" },
  209. { }
  210. };
  211. U_BOOT_DRIVER(fwu_mdata_mtd) = {
  212. .name = "fwu-mdata-mtd",
  213. .id = UCLASS_FWU_MDATA,
  214. .of_match = fwu_mdata_ids,
  215. .ops = &fwu_mtd_ops,
  216. .probe = fwu_mdata_mtd_probe,
  217. .of_to_plat = fwu_mdata_mtd_of_to_plat,
  218. .priv_auto = sizeof(struct fwu_mdata_mtd_priv),
  219. };