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