ipc3-loader.c 12 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. All rights reserved.
  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. struct snd_sof_pdata *plat_data = sdev->pdata;
  118. const struct firmware *fw = plat_data->fw;
  119. const struct sof_ext_man_elem_header *elem_hdr;
  120. const struct sof_ext_man_header *head;
  121. ssize_t ext_man_size;
  122. ssize_t remaining;
  123. uintptr_t iptr;
  124. int ret = 0;
  125. head = (struct sof_ext_man_header *)fw->data;
  126. remaining = head->full_size - head->header_size;
  127. ext_man_size = ipc3_fw_ext_man_size(sdev, fw);
  128. /* Assert firmware starts with extended manifest */
  129. if (ext_man_size <= 0)
  130. return ext_man_size;
  131. /* incompatible version */
  132. if (SOF_EXT_MAN_VERSION_INCOMPATIBLE(SOF_EXT_MAN_VERSION,
  133. head->header_version)) {
  134. dev_err(sdev->dev,
  135. "extended manifest version %#x differ from used %#x\n",
  136. head->header_version, SOF_EXT_MAN_VERSION);
  137. return -EINVAL;
  138. }
  139. /* get first extended manifest element header */
  140. iptr = (uintptr_t)fw->data + head->header_size;
  141. while (remaining > sizeof(*elem_hdr)) {
  142. elem_hdr = (struct sof_ext_man_elem_header *)iptr;
  143. dev_dbg(sdev->dev, "found sof_ext_man header type %d size %#x\n",
  144. elem_hdr->type, elem_hdr->size);
  145. if (elem_hdr->size < sizeof(*elem_hdr) ||
  146. elem_hdr->size > remaining) {
  147. dev_err(sdev->dev,
  148. "invalid sof_ext_man header size, type %d size %#x\n",
  149. elem_hdr->type, elem_hdr->size);
  150. return -EINVAL;
  151. }
  152. /* process structure data */
  153. switch (elem_hdr->type) {
  154. case SOF_EXT_MAN_ELEM_FW_VERSION:
  155. ret = ipc3_fw_ext_man_get_version(sdev, elem_hdr);
  156. break;
  157. case SOF_EXT_MAN_ELEM_WINDOW:
  158. ret = ipc3_fw_ext_man_get_windows(sdev, elem_hdr);
  159. break;
  160. case SOF_EXT_MAN_ELEM_CC_VERSION:
  161. ret = ipc3_fw_ext_man_get_cc_info(sdev, elem_hdr);
  162. break;
  163. case SOF_EXT_MAN_ELEM_DBG_ABI:
  164. ret = ipc3_fw_ext_man_get_dbg_abi_info(sdev, elem_hdr);
  165. break;
  166. case SOF_EXT_MAN_ELEM_CONFIG_DATA:
  167. ret = ipc3_fw_ext_man_get_config_data(sdev, elem_hdr);
  168. break;
  169. case SOF_EXT_MAN_ELEM_PLATFORM_CONFIG_DATA:
  170. ret = snd_sof_dsp_parse_platform_ext_manifest(sdev, elem_hdr);
  171. break;
  172. default:
  173. dev_info(sdev->dev,
  174. "unknown sof_ext_man header type %d size %#x\n",
  175. elem_hdr->type, elem_hdr->size);
  176. break;
  177. }
  178. if (ret < 0) {
  179. dev_err(sdev->dev,
  180. "failed to parse sof_ext_man header type %d size %#x\n",
  181. elem_hdr->type, elem_hdr->size);
  182. return ret;
  183. }
  184. remaining -= elem_hdr->size;
  185. iptr += elem_hdr->size;
  186. }
  187. if (remaining) {
  188. dev_err(sdev->dev, "error: sof_ext_man header is inconsistent\n");
  189. return -EINVAL;
  190. }
  191. return ext_man_size;
  192. }
  193. /* generic module parser for mmaped DSPs */
  194. static int sof_ipc3_parse_module_memcpy(struct snd_sof_dev *sdev,
  195. struct snd_sof_mod_hdr *module)
  196. {
  197. struct snd_sof_blk_hdr *block;
  198. int count, ret;
  199. u32 offset;
  200. size_t remaining;
  201. dev_dbg(sdev->dev, "new module size %#x blocks %#x type %#x\n",
  202. module->size, module->num_blocks, module->type);
  203. block = (struct snd_sof_blk_hdr *)((u8 *)module + sizeof(*module));
  204. /* module->size doesn't include header size */
  205. remaining = module->size;
  206. for (count = 0; count < module->num_blocks; count++) {
  207. /* check for wrap */
  208. if (remaining < sizeof(*block)) {
  209. dev_err(sdev->dev, "not enough data remaining\n");
  210. return -EINVAL;
  211. }
  212. /* minus header size of block */
  213. remaining -= sizeof(*block);
  214. if (block->size == 0) {
  215. dev_warn(sdev->dev,
  216. "warning: block %d size zero\n", count);
  217. dev_warn(sdev->dev, " type %#x offset %#x\n",
  218. block->type, block->offset);
  219. continue;
  220. }
  221. switch (block->type) {
  222. case SOF_FW_BLK_TYPE_RSRVD0:
  223. case SOF_FW_BLK_TYPE_ROM...SOF_FW_BLK_TYPE_RSRVD14:
  224. continue; /* not handled atm */
  225. case SOF_FW_BLK_TYPE_IRAM:
  226. case SOF_FW_BLK_TYPE_DRAM:
  227. case SOF_FW_BLK_TYPE_SRAM:
  228. offset = block->offset;
  229. break;
  230. default:
  231. dev_err(sdev->dev, "%s: bad type %#x for block %#x\n",
  232. __func__, block->type, count);
  233. return -EINVAL;
  234. }
  235. dev_dbg(sdev->dev, "block %d type %#x size %#x ==> offset %#x\n",
  236. count, block->type, block->size, offset);
  237. /* checking block->size to avoid unaligned access */
  238. if (block->size % sizeof(u32)) {
  239. dev_err(sdev->dev, "%s: invalid block size %#x\n",
  240. __func__, block->size);
  241. return -EINVAL;
  242. }
  243. ret = snd_sof_dsp_block_write(sdev, block->type, offset,
  244. block + 1, block->size);
  245. if (ret < 0) {
  246. dev_err(sdev->dev, "%s: write to block type %#x failed\n",
  247. __func__, block->type);
  248. return ret;
  249. }
  250. if (remaining < block->size) {
  251. dev_err(sdev->dev, "%s: not enough data remaining\n", __func__);
  252. return -EINVAL;
  253. }
  254. /* minus body size of block */
  255. remaining -= block->size;
  256. /* next block */
  257. block = (struct snd_sof_blk_hdr *)((u8 *)block + sizeof(*block)
  258. + block->size);
  259. }
  260. return 0;
  261. }
  262. static int sof_ipc3_load_fw_to_dsp(struct snd_sof_dev *sdev)
  263. {
  264. struct snd_sof_pdata *plat_data = sdev->pdata;
  265. const struct firmware *fw = plat_data->fw;
  266. struct snd_sof_fw_header *header;
  267. struct snd_sof_mod_hdr *module;
  268. int (*load_module)(struct snd_sof_dev *sof_dev, struct snd_sof_mod_hdr *hdr);
  269. size_t remaining;
  270. int ret, count;
  271. if (!plat_data->fw)
  272. return -EINVAL;
  273. header = (struct snd_sof_fw_header *)(fw->data + plat_data->fw_offset);
  274. load_module = sof_ops(sdev)->load_module;
  275. if (!load_module) {
  276. dev_dbg(sdev->dev, "Using generic module loading\n");
  277. load_module = sof_ipc3_parse_module_memcpy;
  278. } else {
  279. dev_dbg(sdev->dev, "Using custom module loading\n");
  280. }
  281. /* parse each module */
  282. module = (struct snd_sof_mod_hdr *)(fw->data + plat_data->fw_offset +
  283. sizeof(*header));
  284. remaining = fw->size - sizeof(*header) - plat_data->fw_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. struct snd_sof_pdata *plat_data = sdev->pdata;
  319. const struct firmware *fw = plat_data->fw;
  320. struct snd_sof_fw_header *header;
  321. size_t fw_size = fw->size - plat_data->fw_offset;
  322. if (fw->size <= plat_data->fw_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 + plat_data->fw_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. };