xref: /linux/sound/soc/sof/ipc3-loader.c (revision 293ad281)
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