ipc3-loader.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
  2. //
  3. // This file is provided under a dual BSD/GPLv2 license. When using or
  4. // redistributing this file, you may do so under either license.
  5. //
  6. // Copyright(c) 2022 Intel Corporation
  7. #include <linux/firmware.h>
  8. #include "sof-priv.h"
  9. #include "sof-audio.h"
  10. #include "ipc3-priv.h"
  11. #include "ops.h"
  12. static int ipc3_fw_ext_man_get_version(struct snd_sof_dev *sdev,
  13. const struct sof_ext_man_elem_header *hdr)
  14. {
  15. const struct sof_ext_man_fw_version *v =
  16. container_of(hdr, struct sof_ext_man_fw_version, hdr);
  17. memcpy(&sdev->fw_ready.version, &v->version, sizeof(v->version));
  18. sdev->fw_ready.flags = v->flags;
  19. /* log ABI versions and check FW compatibility */
  20. return sof_ipc3_validate_fw_version(sdev);
  21. }
  22. static int ipc3_fw_ext_man_get_windows(struct snd_sof_dev *sdev,
  23. const struct sof_ext_man_elem_header *hdr)
  24. {
  25. const struct sof_ext_man_window *w;
  26. w = container_of(hdr, struct sof_ext_man_window, hdr);
  27. return sof_ipc3_get_ext_windows(sdev, &w->ipc_window.ext_hdr);
  28. }
  29. static int ipc3_fw_ext_man_get_cc_info(struct snd_sof_dev *sdev,
  30. const struct sof_ext_man_elem_header *hdr)
  31. {
  32. const struct sof_ext_man_cc_version *cc;
  33. cc = container_of(hdr, struct sof_ext_man_cc_version, hdr);
  34. return sof_ipc3_get_cc_info(sdev, &cc->cc_version.ext_hdr);
  35. }
  36. static int ipc3_fw_ext_man_get_dbg_abi_info(struct snd_sof_dev *sdev,
  37. const struct sof_ext_man_elem_header *hdr)
  38. {
  39. const struct ext_man_dbg_abi *dbg_abi =
  40. container_of(hdr, struct ext_man_dbg_abi, hdr);
  41. if (sdev->first_boot)
  42. dev_dbg(sdev->dev,
  43. "Firmware: DBG_ABI %d:%d:%d\n",
  44. SOF_ABI_VERSION_MAJOR(dbg_abi->dbg_abi.abi_dbg_version),
  45. SOF_ABI_VERSION_MINOR(dbg_abi->dbg_abi.abi_dbg_version),
  46. SOF_ABI_VERSION_PATCH(dbg_abi->dbg_abi.abi_dbg_version));
  47. return 0;
  48. }
  49. static int ipc3_fw_ext_man_get_config_data(struct snd_sof_dev *sdev,
  50. const struct sof_ext_man_elem_header *hdr)
  51. {
  52. const struct sof_ext_man_config_data *config =
  53. container_of(hdr, struct sof_ext_man_config_data, hdr);
  54. const struct sof_config_elem *elem;
  55. int elems_counter;
  56. int elems_size;
  57. int ret = 0;
  58. int i;
  59. /* calculate elements counter */
  60. elems_size = config->hdr.size - sizeof(struct sof_ext_man_elem_header);
  61. elems_counter = elems_size / sizeof(struct sof_config_elem);
  62. dev_dbg(sdev->dev, "manifest can hold up to %d config elements\n", elems_counter);
  63. for (i = 0; i < elems_counter; ++i) {
  64. elem = &config->elems[i];
  65. dev_dbg(sdev->dev, "get index %d token %d val %d\n",
  66. i, elem->token, elem->value);
  67. switch (elem->token) {
  68. case SOF_EXT_MAN_CONFIG_EMPTY:
  69. /* unused memory space is zero filled - mapped to EMPTY elements */
  70. break;
  71. case SOF_EXT_MAN_CONFIG_IPC_MSG_SIZE:
  72. /* TODO: use ipc msg size from config data */
  73. break;
  74. case SOF_EXT_MAN_CONFIG_MEMORY_USAGE_SCAN:
  75. if (sdev->first_boot && elem->value)
  76. ret = snd_sof_dbg_memory_info_init(sdev);
  77. break;
  78. default:
  79. dev_info(sdev->dev,
  80. "Unknown firmware configuration token %d value %d",
  81. elem->token, elem->value);
  82. break;
  83. }
  84. if (ret < 0) {
  85. dev_err(sdev->dev,
  86. "%s: processing failed for token %d value %#x, %d\n",
  87. __func__, elem->token, elem->value, ret);
  88. return ret;
  89. }
  90. }
  91. return 0;
  92. }
  93. static ssize_t ipc3_fw_ext_man_size(struct snd_sof_dev *sdev, const struct firmware *fw)
  94. {
  95. const struct sof_ext_man_header *head;
  96. head = (struct sof_ext_man_header *)fw->data;
  97. /*
  98. * assert fw size is big enough to contain extended manifest header,
  99. * it prevents from reading unallocated memory from `head` in following
  100. * step.
  101. */
  102. if (fw->size < sizeof(*head))
  103. return -EINVAL;
  104. /*
  105. * When fw points to extended manifest,
  106. * then first u32 must be equal SOF_EXT_MAN_MAGIC_NUMBER.
  107. */
  108. if (head->magic == SOF_EXT_MAN_MAGIC_NUMBER)
  109. return head->full_size;
  110. /* otherwise given fw don't have an extended manifest */
  111. dev_dbg(sdev->dev, "Unexpected extended manifest magic number: %#x\n",
  112. head->magic);
  113. return 0;
  114. }
  115. static size_t sof_ipc3_fw_parse_ext_man(struct snd_sof_dev *sdev)
  116. {
  117. const struct firmware *fw = sdev->basefw.fw;
  118. const struct sof_ext_man_elem_header *elem_hdr;
  119. const struct sof_ext_man_header *head;
  120. ssize_t ext_man_size;
  121. ssize_t remaining;
  122. uintptr_t iptr;
  123. int ret = 0;
  124. head = (struct sof_ext_man_header *)fw->data;
  125. remaining = head->full_size - head->header_size;
  126. if (remaining < 0 || remaining > sdev->basefw.fw->size)
  127. return -EINVAL;
  128. ext_man_size = ipc3_fw_ext_man_size(sdev, fw);
  129. /* Assert firmware starts with extended manifest */
  130. if (ext_man_size <= 0)
  131. return ext_man_size;
  132. /* incompatible version */
  133. if (SOF_EXT_MAN_VERSION_INCOMPATIBLE(SOF_EXT_MAN_VERSION,
  134. head->header_version)) {
  135. dev_err(sdev->dev,
  136. "extended manifest version %#x differ from used %#x\n",
  137. head->header_version, SOF_EXT_MAN_VERSION);
  138. return -EINVAL;
  139. }
  140. /* get first extended manifest element header */
  141. iptr = (uintptr_t)fw->data + head->header_size;
  142. while (remaining > sizeof(*elem_hdr)) {
  143. elem_hdr = (struct sof_ext_man_elem_header *)iptr;
  144. dev_dbg(sdev->dev, "found sof_ext_man header type %d size %#x\n",
  145. elem_hdr->type, elem_hdr->size);
  146. if (elem_hdr->size < sizeof(*elem_hdr) ||
  147. elem_hdr->size > remaining) {
  148. dev_err(sdev->dev,
  149. "invalid sof_ext_man header size, type %d size %#x\n",
  150. elem_hdr->type, elem_hdr->size);
  151. return -EINVAL;
  152. }
  153. /* process structure data */
  154. switch (elem_hdr->type) {
  155. case SOF_EXT_MAN_ELEM_FW_VERSION:
  156. ret = ipc3_fw_ext_man_get_version(sdev, elem_hdr);
  157. break;
  158. case SOF_EXT_MAN_ELEM_WINDOW:
  159. ret = ipc3_fw_ext_man_get_windows(sdev, elem_hdr);
  160. break;
  161. case SOF_EXT_MAN_ELEM_CC_VERSION:
  162. ret = ipc3_fw_ext_man_get_cc_info(sdev, elem_hdr);
  163. break;
  164. case SOF_EXT_MAN_ELEM_DBG_ABI:
  165. ret = ipc3_fw_ext_man_get_dbg_abi_info(sdev, elem_hdr);
  166. break;
  167. case SOF_EXT_MAN_ELEM_CONFIG_DATA:
  168. ret = ipc3_fw_ext_man_get_config_data(sdev, elem_hdr);
  169. break;
  170. case SOF_EXT_MAN_ELEM_PLATFORM_CONFIG_DATA:
  171. ret = snd_sof_dsp_parse_platform_ext_manifest(sdev, elem_hdr);
  172. break;
  173. default:
  174. dev_info(sdev->dev,
  175. "unknown sof_ext_man header type %d size %#x\n",
  176. elem_hdr->type, elem_hdr->size);
  177. break;
  178. }
  179. if (ret < 0) {
  180. dev_err(sdev->dev,
  181. "failed to parse sof_ext_man header type %d size %#x\n",
  182. elem_hdr->type, elem_hdr->size);
  183. return ret;
  184. }
  185. remaining -= elem_hdr->size;
  186. iptr += elem_hdr->size;
  187. }
  188. if (remaining) {
  189. dev_err(sdev->dev, "error: sof_ext_man header is inconsistent\n");
  190. return -EINVAL;
  191. }
  192. return ext_man_size;
  193. }
  194. /* generic module parser for mmaped DSPs */
  195. static int sof_ipc3_parse_module_memcpy(struct snd_sof_dev *sdev,
  196. struct snd_sof_mod_hdr *module)
  197. {
  198. struct snd_sof_blk_hdr *block;
  199. int count, ret;
  200. u32 offset;
  201. size_t remaining;
  202. dev_dbg(sdev->dev, "new module size %#x blocks %#x type %#x\n",
  203. module->size, module->num_blocks, module->type);
  204. block = (struct snd_sof_blk_hdr *)((u8 *)module + sizeof(*module));
  205. /* module->size doesn't include header size */
  206. remaining = module->size;
  207. for (count = 0; count < module->num_blocks; count++) {
  208. /* check for wrap */
  209. if (remaining < sizeof(*block)) {
  210. dev_err(sdev->dev, "not enough data remaining\n");
  211. return -EINVAL;
  212. }
  213. /* minus header size of block */
  214. remaining -= sizeof(*block);
  215. if (block->size == 0) {
  216. dev_warn(sdev->dev,
  217. "warning: block %d size zero\n", count);
  218. dev_warn(sdev->dev, " type %#x offset %#x\n",
  219. block->type, block->offset);
  220. continue;
  221. }
  222. switch (block->type) {
  223. case SOF_FW_BLK_TYPE_RSRVD0:
  224. case SOF_FW_BLK_TYPE_ROM...SOF_FW_BLK_TYPE_RSRVD14:
  225. continue; /* not handled atm */
  226. case SOF_FW_BLK_TYPE_IRAM:
  227. case SOF_FW_BLK_TYPE_DRAM:
  228. case SOF_FW_BLK_TYPE_SRAM:
  229. offset = block->offset;
  230. break;
  231. default:
  232. dev_err(sdev->dev, "%s: bad type %#x for block %#x\n",
  233. __func__, block->type, count);
  234. return -EINVAL;
  235. }
  236. dev_dbg(sdev->dev, "block %d type %#x size %#x ==> offset %#x\n",
  237. count, block->type, block->size, offset);
  238. /* checking block->size to avoid unaligned access */
  239. if (block->size % sizeof(u32)) {
  240. dev_err(sdev->dev, "%s: invalid block size %#x\n",
  241. __func__, block->size);
  242. return -EINVAL;
  243. }
  244. ret = snd_sof_dsp_block_write(sdev, block->type, offset,
  245. block + 1, block->size);
  246. if (ret < 0) {
  247. dev_err(sdev->dev, "%s: write to block type %#x failed\n",
  248. __func__, block->type);
  249. return ret;
  250. }
  251. if (remaining < block->size) {
  252. dev_err(sdev->dev, "%s: not enough data remaining\n", __func__);
  253. return -EINVAL;
  254. }
  255. /* minus body size of block */
  256. remaining -= block->size;
  257. /* next block */
  258. block = (struct snd_sof_blk_hdr *)((u8 *)block + sizeof(*block)
  259. + block->size);
  260. }
  261. return 0;
  262. }
  263. static int sof_ipc3_load_fw_to_dsp(struct snd_sof_dev *sdev)
  264. {
  265. u32 payload_offset = sdev->basefw.payload_offset;
  266. const struct firmware *fw = sdev->basefw.fw;
  267. struct snd_sof_fw_header *header;
  268. struct snd_sof_mod_hdr *module;
  269. int (*load_module)(struct snd_sof_dev *sof_dev, struct snd_sof_mod_hdr *hdr);
  270. size_t remaining;
  271. int ret, count;
  272. if (!fw)
  273. return -EINVAL;
  274. header = (struct snd_sof_fw_header *)(fw->data + payload_offset);
  275. load_module = sof_ops(sdev)->load_module;
  276. if (!load_module) {
  277. dev_dbg(sdev->dev, "Using generic module loading\n");
  278. load_module = sof_ipc3_parse_module_memcpy;
  279. } else {
  280. dev_dbg(sdev->dev, "Using custom module loading\n");
  281. }
  282. /* parse each module */
  283. module = (struct snd_sof_mod_hdr *)(fw->data + payload_offset + sizeof(*header));
  284. remaining = fw->size - sizeof(*header) - payload_offset;
  285. /* check for wrap */
  286. if (remaining > fw->size) {
  287. dev_err(sdev->dev, "%s: fw size smaller than header size\n", __func__);
  288. return -EINVAL;
  289. }
  290. for (count = 0; count < header->num_modules; count++) {
  291. /* check for wrap */
  292. if (remaining < sizeof(*module)) {
  293. dev_err(sdev->dev, "%s: not enough data for a module\n",
  294. __func__);
  295. return -EINVAL;
  296. }
  297. /* minus header size of module */
  298. remaining -= sizeof(*module);
  299. /* module */
  300. ret = load_module(sdev, module);
  301. if (ret < 0) {
  302. dev_err(sdev->dev, "%s: invalid module %d\n", __func__, count);
  303. return ret;
  304. }
  305. if (remaining < module->size) {
  306. dev_err(sdev->dev, "%s: not enough data remaining\n", __func__);
  307. return -EINVAL;
  308. }
  309. /* minus body size of module */
  310. remaining -= module->size;
  311. module = (struct snd_sof_mod_hdr *)((u8 *)module +
  312. sizeof(*module) + module->size);
  313. }
  314. return 0;
  315. }
  316. static int sof_ipc3_validate_firmware(struct snd_sof_dev *sdev)
  317. {
  318. u32 payload_offset = sdev->basefw.payload_offset;
  319. const struct firmware *fw = sdev->basefw.fw;
  320. struct snd_sof_fw_header *header;
  321. size_t fw_size = fw->size - payload_offset;
  322. if (fw->size <= payload_offset) {
  323. dev_err(sdev->dev,
  324. "firmware size must be greater than firmware offset\n");
  325. return -EINVAL;
  326. }
  327. /* Read the header information from the data pointer */
  328. header = (struct snd_sof_fw_header *)(fw->data + payload_offset);
  329. /* verify FW sig */
  330. if (strncmp(header->sig, SND_SOF_FW_SIG, SND_SOF_FW_SIG_SIZE) != 0) {
  331. dev_err(sdev->dev, "invalid firmware signature\n");
  332. return -EINVAL;
  333. }
  334. /* check size is valid */
  335. if (fw_size != header->file_size + sizeof(*header)) {
  336. dev_err(sdev->dev,
  337. "invalid filesize mismatch got 0x%zx expected 0x%zx\n",
  338. fw_size, header->file_size + sizeof(*header));
  339. return -EINVAL;
  340. }
  341. dev_dbg(sdev->dev, "header size=0x%x modules=0x%x abi=0x%x size=%zu\n",
  342. header->file_size, header->num_modules,
  343. header->abi, sizeof(*header));
  344. return 0;
  345. }
  346. const struct sof_ipc_fw_loader_ops ipc3_loader_ops = {
  347. .validate = sof_ipc3_validate_firmware,
  348. .parse_ext_manifest = sof_ipc3_fw_parse_ext_man,
  349. .load_fw_to_dsp = sof_ipc3_load_fw_to_dsp,
  350. };