wcd-dsp-utils.c 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
  4. * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
  5. */
  6. #include <linux/device.h>
  7. #include <linux/err.h>
  8. #include <linux/firmware.h>
  9. #include <linux/elf.h>
  10. #include <linux/slab.h>
  11. #include <linux/list.h>
  12. #include "wcd-dsp-utils.h"
  13. static bool wdsp_is_valid_elf_hdr(const struct elf32_hdr *ehdr,
  14. size_t fw_size)
  15. {
  16. if (fw_size < sizeof(*ehdr)) {
  17. pr_err_ratelimited("%s: Firmware too small\n", __func__);
  18. goto elf_check_fail;
  19. }
  20. if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) {
  21. pr_err_ratelimited("%s: Not an ELF file\n", __func__);
  22. goto elf_check_fail;
  23. }
  24. if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) {
  25. pr_err_ratelimited("%s: Not an executable image\n", __func__);
  26. goto elf_check_fail;
  27. }
  28. if (ehdr->e_phnum == 0) {
  29. pr_err_ratelimited("%s: no segments to load\n", __func__);
  30. goto elf_check_fail;
  31. }
  32. if (sizeof(struct elf32_phdr) * ehdr->e_phnum +
  33. sizeof(struct elf32_hdr) > fw_size) {
  34. pr_err_ratelimited("%s: Too small MDT file\n", __func__);
  35. goto elf_check_fail;
  36. }
  37. return true;
  38. elf_check_fail:
  39. return false;
  40. }
  41. static int wdsp_add_segment_to_list(struct device *dev,
  42. const char *img_fname,
  43. const struct elf32_phdr *phdr,
  44. int phdr_idx,
  45. struct list_head *seg_list)
  46. {
  47. struct wdsp_img_segment *seg;
  48. int ret = 0;
  49. /* Do not load segments with zero size */
  50. if (phdr->p_filesz == 0 || phdr->p_memsz == 0)
  51. goto done;
  52. seg = kzalloc(sizeof(*seg), GFP_KERNEL);
  53. if (!seg) {
  54. ret = -ENOMEM;
  55. goto done;
  56. }
  57. snprintf(seg->split_fname, sizeof(seg->split_fname),
  58. "%s.b%02d", img_fname, phdr_idx);
  59. ret = request_firmware(&seg->split_fw, seg->split_fname, dev);
  60. if (ret < 0) {
  61. dev_err_ratelimited(dev, "%s: firmware %s not found\n",
  62. __func__, seg->split_fname);
  63. goto bad_seg;
  64. }
  65. if (phdr->p_filesz != seg->split_fw->size) {
  66. dev_err_ratelimited(dev,
  67. "%s: %s size mismatch, phdr_size: 0x%x fw_size: 0x%zx",
  68. __func__, seg->split_fname, phdr->p_filesz,
  69. seg->split_fw->size);
  70. ret = -EINVAL;
  71. goto bad_elf;
  72. }
  73. seg->load_addr = phdr->p_paddr;
  74. seg->size = phdr->p_filesz;
  75. seg->data = (u8 *) seg->split_fw->data;
  76. list_add_tail(&seg->list, seg_list);
  77. done:
  78. return ret;
  79. bad_elf:
  80. release_firmware(seg->split_fw);
  81. bad_seg:
  82. kfree(seg);
  83. return ret;
  84. }
  85. /*
  86. * wdsp_flush_segment_list: Flush the list of segments
  87. * @seg_list: List of segments to be flushed
  88. * This API will traverse through the list of segments provided in
  89. * seg_list, release the firmware for each segment and delete the
  90. * segment from the list.
  91. */
  92. void wdsp_flush_segment_list(struct list_head *seg_list)
  93. {
  94. struct wdsp_img_segment *seg, *next;
  95. list_for_each_entry_safe(seg, next, seg_list, list) {
  96. release_firmware(seg->split_fw);
  97. list_del(&seg->list);
  98. kfree(seg);
  99. }
  100. }
  101. EXPORT_SYMBOL(wdsp_flush_segment_list);
  102. /*
  103. * wdsp_get_segment_list: Get the list of requested segments
  104. * @dev: struct device pointer of caller
  105. * @img_fname: Image name for the mdt and split firmware files
  106. * @segment_type: Requested segment type, should be either
  107. * WDSP_ELF_FLAG_RE or WDSP_ELF_FLAG_WRITE
  108. * @seg_list: An initialized head for list of segmented to be returned
  109. * @entry_point: Pointer to return the entry point of the image
  110. * This API will parse the mdt file for img_fname and create
  111. * an struct wdsp_img_segment for each segment that matches segment_type
  112. * and add this structure to list pointed by seg_list
  113. */
  114. int wdsp_get_segment_list(struct device *dev,
  115. const char *img_fname,
  116. unsigned int segment_type,
  117. struct list_head *seg_list,
  118. u32 *entry_point)
  119. {
  120. const struct firmware *fw;
  121. const struct elf32_hdr *ehdr;
  122. const struct elf32_phdr *phdr;
  123. const u8 *elf_ptr;
  124. char mdt_name[WDSP_IMG_NAME_LEN_MAX];
  125. int ret, phdr_idx;
  126. bool segment_match;
  127. if (!dev) {
  128. ret = -EINVAL;
  129. pr_err_ratelimited("%s: Invalid device handle\n", __func__);
  130. goto done;
  131. }
  132. if (!img_fname || !seg_list || !entry_point) {
  133. ret = -EINVAL;
  134. dev_err_ratelimited(dev, "%s: Invalid input params\n",
  135. __func__);
  136. goto done;
  137. }
  138. if (segment_type != WDSP_ELF_FLAG_RE &&
  139. segment_type != WDSP_ELF_FLAG_WRITE) {
  140. dev_err_ratelimited(dev, "%s: Invalid request for segment_type %d\n",
  141. __func__, segment_type);
  142. ret = -EINVAL;
  143. goto done;
  144. }
  145. snprintf(mdt_name, sizeof(mdt_name), "%s.mdt", img_fname);
  146. ret = request_firmware(&fw, mdt_name, dev);
  147. if (ret < 0) {
  148. dev_err_ratelimited(dev, "%s: firmware %s not found\n",
  149. __func__, mdt_name);
  150. goto done;
  151. }
  152. ehdr = (struct elf32_hdr *) fw->data;
  153. *entry_point = ehdr->e_entry;
  154. if (!wdsp_is_valid_elf_hdr(ehdr, fw->size)) {
  155. dev_err_ratelimited(dev, "%s: fw mdt %s is invalid\n",
  156. __func__, mdt_name);
  157. ret = -EINVAL;
  158. goto bad_elf;
  159. }
  160. elf_ptr = fw->data + sizeof(*ehdr);
  161. for (phdr_idx = 0; phdr_idx < ehdr->e_phnum; phdr_idx++) {
  162. phdr = (struct elf32_phdr *) elf_ptr;
  163. segment_match = false;
  164. switch (segment_type) {
  165. case WDSP_ELF_FLAG_RE:
  166. /*
  167. * Flag can be READ or EXECUTE or both but
  168. * WRITE flag should not be set.
  169. */
  170. if ((phdr->p_flags & segment_type) &&
  171. !(phdr->p_flags & WDSP_ELF_FLAG_WRITE))
  172. segment_match = true;
  173. break;
  174. case WDSP_ELF_FLAG_WRITE:
  175. /*
  176. * If WRITE flag is set, other flags do not
  177. * matter.
  178. */
  179. if (phdr->p_flags & segment_type)
  180. segment_match = true;
  181. break;
  182. }
  183. if (segment_match) {
  184. ret = wdsp_add_segment_to_list(dev, img_fname, phdr,
  185. phdr_idx, seg_list);
  186. if (ret < 0) {
  187. wdsp_flush_segment_list(seg_list);
  188. goto bad_elf;
  189. }
  190. }
  191. elf_ptr = elf_ptr + sizeof(*phdr);
  192. }
  193. bad_elf:
  194. release_firmware(fw);
  195. done:
  196. return ret;
  197. }
  198. EXPORT_SYMBOL(wdsp_get_segment_list);