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 // 6d2458baaSPeter Ujfalusi // Copyright(c) 2022 Intel Corporation. All rights reserved. 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 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 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 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 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 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 78*e16809a7SPierre-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]; 82*e16809a7SPierre-Louis Bossart dev_dbg(sdev->dev, "get index %d token %d val %d\n", 83*e16809a7SPierre-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 112d2458baaSPeter Ujfalusi static ssize_t ipc3_fw_ext_man_size(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 */ 134d2458baaSPeter Ujfalusi return 0; 135d2458baaSPeter Ujfalusi } 136d2458baaSPeter Ujfalusi 137d2458baaSPeter Ujfalusi static size_t sof_ipc3_fw_parse_ext_man(struct snd_sof_dev *sdev) 138d2458baaSPeter Ujfalusi { 139d2458baaSPeter Ujfalusi struct snd_sof_pdata *plat_data = sdev->pdata; 140d2458baaSPeter Ujfalusi const struct firmware *fw = plat_data->fw; 141d2458baaSPeter Ujfalusi const struct sof_ext_man_elem_header *elem_hdr; 142d2458baaSPeter Ujfalusi const struct sof_ext_man_header *head; 143d2458baaSPeter Ujfalusi ssize_t ext_man_size; 144d2458baaSPeter Ujfalusi ssize_t remaining; 145d2458baaSPeter Ujfalusi uintptr_t iptr; 146d2458baaSPeter Ujfalusi int ret = 0; 147d2458baaSPeter Ujfalusi 148d2458baaSPeter Ujfalusi head = (struct sof_ext_man_header *)fw->data; 149d2458baaSPeter Ujfalusi remaining = head->full_size - head->header_size; 150d2458baaSPeter Ujfalusi ext_man_size = ipc3_fw_ext_man_size(fw); 151d2458baaSPeter Ujfalusi 152d2458baaSPeter Ujfalusi /* Assert firmware starts with extended manifest */ 153d2458baaSPeter Ujfalusi if (ext_man_size <= 0) 154d2458baaSPeter Ujfalusi return ext_man_size; 155d2458baaSPeter Ujfalusi 156d2458baaSPeter Ujfalusi /* incompatible version */ 157d2458baaSPeter Ujfalusi if (SOF_EXT_MAN_VERSION_INCOMPATIBLE(SOF_EXT_MAN_VERSION, 158d2458baaSPeter Ujfalusi head->header_version)) { 159d2458baaSPeter Ujfalusi dev_err(sdev->dev, 160d2458baaSPeter Ujfalusi "extended manifest version %#x differ from used %#x\n", 161d2458baaSPeter Ujfalusi head->header_version, SOF_EXT_MAN_VERSION); 162d2458baaSPeter Ujfalusi return -EINVAL; 163d2458baaSPeter Ujfalusi } 164d2458baaSPeter Ujfalusi 165d2458baaSPeter Ujfalusi /* get first extended manifest element header */ 166d2458baaSPeter Ujfalusi iptr = (uintptr_t)fw->data + head->header_size; 167d2458baaSPeter Ujfalusi 168d2458baaSPeter Ujfalusi while (remaining > sizeof(*elem_hdr)) { 169d2458baaSPeter Ujfalusi elem_hdr = (struct sof_ext_man_elem_header *)iptr; 170d2458baaSPeter Ujfalusi 171d2458baaSPeter Ujfalusi dev_dbg(sdev->dev, "found sof_ext_man header type %d size %#x\n", 172d2458baaSPeter Ujfalusi elem_hdr->type, elem_hdr->size); 173d2458baaSPeter Ujfalusi 174d2458baaSPeter Ujfalusi if (elem_hdr->size < sizeof(*elem_hdr) || 175d2458baaSPeter Ujfalusi elem_hdr->size > remaining) { 176d2458baaSPeter Ujfalusi dev_err(sdev->dev, 177d2458baaSPeter Ujfalusi "invalid sof_ext_man header size, type %d size %#x\n", 178d2458baaSPeter Ujfalusi elem_hdr->type, elem_hdr->size); 179d2458baaSPeter Ujfalusi return -EINVAL; 180d2458baaSPeter Ujfalusi } 181d2458baaSPeter Ujfalusi 182d2458baaSPeter Ujfalusi /* process structure data */ 183d2458baaSPeter Ujfalusi switch (elem_hdr->type) { 184d2458baaSPeter Ujfalusi case SOF_EXT_MAN_ELEM_FW_VERSION: 185d2458baaSPeter Ujfalusi ret = ipc3_fw_ext_man_get_version(sdev, elem_hdr); 186d2458baaSPeter Ujfalusi break; 187d2458baaSPeter Ujfalusi case SOF_EXT_MAN_ELEM_WINDOW: 188d2458baaSPeter Ujfalusi ret = ipc3_fw_ext_man_get_windows(sdev, elem_hdr); 189d2458baaSPeter Ujfalusi break; 190d2458baaSPeter Ujfalusi case SOF_EXT_MAN_ELEM_CC_VERSION: 191d2458baaSPeter Ujfalusi ret = ipc3_fw_ext_man_get_cc_info(sdev, elem_hdr); 192d2458baaSPeter Ujfalusi break; 193d2458baaSPeter Ujfalusi case SOF_EXT_MAN_ELEM_DBG_ABI: 194d2458baaSPeter Ujfalusi ret = ipc3_fw_ext_man_get_dbg_abi_info(sdev, elem_hdr); 195d2458baaSPeter Ujfalusi break; 196d2458baaSPeter Ujfalusi case SOF_EXT_MAN_ELEM_CONFIG_DATA: 197d2458baaSPeter Ujfalusi ret = ipc3_fw_ext_man_get_config_data(sdev, elem_hdr); 198d2458baaSPeter Ujfalusi break; 199d2458baaSPeter Ujfalusi case SOF_EXT_MAN_ELEM_PLATFORM_CONFIG_DATA: 200d2458baaSPeter Ujfalusi ret = snd_sof_dsp_parse_platform_ext_manifest(sdev, elem_hdr); 201d2458baaSPeter Ujfalusi break; 202d2458baaSPeter Ujfalusi default: 203d2458baaSPeter Ujfalusi dev_info(sdev->dev, 204d2458baaSPeter Ujfalusi "unknown sof_ext_man header type %d size %#x\n", 205d2458baaSPeter Ujfalusi elem_hdr->type, elem_hdr->size); 206d2458baaSPeter Ujfalusi break; 207d2458baaSPeter Ujfalusi } 208d2458baaSPeter Ujfalusi 209d2458baaSPeter Ujfalusi if (ret < 0) { 210d2458baaSPeter Ujfalusi dev_err(sdev->dev, 211d2458baaSPeter Ujfalusi "failed to parse sof_ext_man header type %d size %#x\n", 212d2458baaSPeter Ujfalusi elem_hdr->type, elem_hdr->size); 213d2458baaSPeter Ujfalusi return ret; 214d2458baaSPeter Ujfalusi } 215d2458baaSPeter Ujfalusi 216d2458baaSPeter Ujfalusi remaining -= elem_hdr->size; 217d2458baaSPeter Ujfalusi iptr += elem_hdr->size; 218d2458baaSPeter Ujfalusi } 219d2458baaSPeter Ujfalusi 220d2458baaSPeter Ujfalusi if (remaining) { 221d2458baaSPeter Ujfalusi dev_err(sdev->dev, "error: sof_ext_man header is inconsistent\n"); 222d2458baaSPeter Ujfalusi return -EINVAL; 223d2458baaSPeter Ujfalusi } 224d2458baaSPeter Ujfalusi 225d2458baaSPeter Ujfalusi return ext_man_size; 226d2458baaSPeter Ujfalusi } 227d2458baaSPeter Ujfalusi 228d2458baaSPeter Ujfalusi /* generic module parser for mmaped DSPs */ 229d2458baaSPeter Ujfalusi static int sof_ipc3_parse_module_memcpy(struct snd_sof_dev *sdev, 230d2458baaSPeter Ujfalusi struct snd_sof_mod_hdr *module) 231d2458baaSPeter Ujfalusi { 232d2458baaSPeter Ujfalusi struct snd_sof_blk_hdr *block; 233d2458baaSPeter Ujfalusi int count, ret; 234d2458baaSPeter Ujfalusi u32 offset; 235d2458baaSPeter Ujfalusi size_t remaining; 236d2458baaSPeter Ujfalusi 237d2458baaSPeter Ujfalusi dev_dbg(sdev->dev, "new module size %#x blocks %#x type %#x\n", 238d2458baaSPeter Ujfalusi module->size, module->num_blocks, module->type); 239d2458baaSPeter Ujfalusi 240d2458baaSPeter Ujfalusi block = (struct snd_sof_blk_hdr *)((u8 *)module + sizeof(*module)); 241d2458baaSPeter Ujfalusi 242d2458baaSPeter Ujfalusi /* module->size doesn't include header size */ 243d2458baaSPeter Ujfalusi remaining = module->size; 244d2458baaSPeter Ujfalusi for (count = 0; count < module->num_blocks; count++) { 245d2458baaSPeter Ujfalusi /* check for wrap */ 246d2458baaSPeter Ujfalusi if (remaining < sizeof(*block)) { 247d2458baaSPeter Ujfalusi dev_err(sdev->dev, "not enough data remaining\n"); 248d2458baaSPeter Ujfalusi return -EINVAL; 249d2458baaSPeter Ujfalusi } 250d2458baaSPeter Ujfalusi 251d2458baaSPeter Ujfalusi /* minus header size of block */ 252d2458baaSPeter Ujfalusi remaining -= sizeof(*block); 253d2458baaSPeter Ujfalusi 254d2458baaSPeter Ujfalusi if (block->size == 0) { 255d2458baaSPeter Ujfalusi dev_warn(sdev->dev, 256d2458baaSPeter Ujfalusi "warning: block %d size zero\n", count); 257d2458baaSPeter Ujfalusi dev_warn(sdev->dev, " type %#x offset %#x\n", 258d2458baaSPeter Ujfalusi block->type, block->offset); 259d2458baaSPeter Ujfalusi continue; 260d2458baaSPeter Ujfalusi } 261d2458baaSPeter Ujfalusi 262d2458baaSPeter Ujfalusi switch (block->type) { 263d2458baaSPeter Ujfalusi case SOF_FW_BLK_TYPE_RSRVD0: 264d2458baaSPeter Ujfalusi case SOF_FW_BLK_TYPE_ROM...SOF_FW_BLK_TYPE_RSRVD14: 265d2458baaSPeter Ujfalusi continue; /* not handled atm */ 266d2458baaSPeter Ujfalusi case SOF_FW_BLK_TYPE_IRAM: 267d2458baaSPeter Ujfalusi case SOF_FW_BLK_TYPE_DRAM: 268d2458baaSPeter Ujfalusi case SOF_FW_BLK_TYPE_SRAM: 269d2458baaSPeter Ujfalusi offset = block->offset; 270d2458baaSPeter Ujfalusi break; 271d2458baaSPeter Ujfalusi default: 272d2458baaSPeter Ujfalusi dev_err(sdev->dev, "%s: bad type %#x for block %#x\n", 273d2458baaSPeter Ujfalusi __func__, block->type, count); 274d2458baaSPeter Ujfalusi return -EINVAL; 275d2458baaSPeter Ujfalusi } 276d2458baaSPeter Ujfalusi 277d2458baaSPeter Ujfalusi dev_dbg(sdev->dev, "block %d type %#x size %#x ==> offset %#x\n", 278d2458baaSPeter Ujfalusi count, block->type, block->size, offset); 279d2458baaSPeter Ujfalusi 280d2458baaSPeter Ujfalusi /* checking block->size to avoid unaligned access */ 281d2458baaSPeter Ujfalusi if (block->size % sizeof(u32)) { 282d2458baaSPeter Ujfalusi dev_err(sdev->dev, "%s: invalid block size %#x\n", 283d2458baaSPeter Ujfalusi __func__, block->size); 284d2458baaSPeter Ujfalusi return -EINVAL; 285d2458baaSPeter Ujfalusi } 286d2458baaSPeter Ujfalusi ret = snd_sof_dsp_block_write(sdev, block->type, offset, 287d2458baaSPeter Ujfalusi block + 1, block->size); 288d2458baaSPeter Ujfalusi if (ret < 0) { 289d2458baaSPeter Ujfalusi dev_err(sdev->dev, "%s: write to block type %#x failed\n", 290d2458baaSPeter Ujfalusi __func__, block->type); 291d2458baaSPeter Ujfalusi return ret; 292d2458baaSPeter Ujfalusi } 293d2458baaSPeter Ujfalusi 294d2458baaSPeter Ujfalusi if (remaining < block->size) { 295d2458baaSPeter Ujfalusi dev_err(sdev->dev, "%s: not enough data remaining\n", __func__); 296d2458baaSPeter Ujfalusi return -EINVAL; 297d2458baaSPeter Ujfalusi } 298d2458baaSPeter Ujfalusi 299d2458baaSPeter Ujfalusi /* minus body size of block */ 300d2458baaSPeter Ujfalusi remaining -= block->size; 301d2458baaSPeter Ujfalusi /* next block */ 302d2458baaSPeter Ujfalusi block = (struct snd_sof_blk_hdr *)((u8 *)block + sizeof(*block) 303d2458baaSPeter Ujfalusi + block->size); 304d2458baaSPeter Ujfalusi } 305d2458baaSPeter Ujfalusi 306d2458baaSPeter Ujfalusi return 0; 307d2458baaSPeter Ujfalusi } 308d2458baaSPeter Ujfalusi 309d2458baaSPeter Ujfalusi static int sof_ipc3_load_fw_to_dsp(struct snd_sof_dev *sdev) 310d2458baaSPeter Ujfalusi { 311d2458baaSPeter Ujfalusi struct snd_sof_pdata *plat_data = sdev->pdata; 312d2458baaSPeter Ujfalusi const struct firmware *fw = plat_data->fw; 313d2458baaSPeter Ujfalusi struct snd_sof_fw_header *header; 314d2458baaSPeter Ujfalusi struct snd_sof_mod_hdr *module; 315d2458baaSPeter Ujfalusi int (*load_module)(struct snd_sof_dev *sof_dev, struct snd_sof_mod_hdr *hdr); 316d2458baaSPeter Ujfalusi size_t remaining; 317d2458baaSPeter Ujfalusi int ret, count; 318d2458baaSPeter Ujfalusi 319d2458baaSPeter Ujfalusi if (!plat_data->fw) 320d2458baaSPeter Ujfalusi return -EINVAL; 321d2458baaSPeter Ujfalusi 322d2458baaSPeter Ujfalusi header = (struct snd_sof_fw_header *)(fw->data + plat_data->fw_offset); 323d2458baaSPeter Ujfalusi load_module = sof_ops(sdev)->load_module; 324d2458baaSPeter Ujfalusi if (!load_module) { 325*e16809a7SPierre-Louis Bossart dev_dbg(sdev->dev, "Using generic module loading\n"); 326d2458baaSPeter Ujfalusi load_module = sof_ipc3_parse_module_memcpy; 327d2458baaSPeter Ujfalusi } else { 328*e16809a7SPierre-Louis Bossart dev_dbg(sdev->dev, "Using custom module loading\n"); 329d2458baaSPeter Ujfalusi } 330d2458baaSPeter Ujfalusi 331d2458baaSPeter Ujfalusi /* parse each module */ 332d2458baaSPeter Ujfalusi module = (struct snd_sof_mod_hdr *)(fw->data + plat_data->fw_offset + 333d2458baaSPeter Ujfalusi sizeof(*header)); 334d2458baaSPeter Ujfalusi remaining = fw->size - sizeof(*header) - plat_data->fw_offset; 335d2458baaSPeter Ujfalusi /* check for wrap */ 336d2458baaSPeter Ujfalusi if (remaining > fw->size) { 337d2458baaSPeter Ujfalusi dev_err(sdev->dev, "%s: fw size smaller than header size\n", __func__); 338d2458baaSPeter Ujfalusi return -EINVAL; 339d2458baaSPeter Ujfalusi } 340d2458baaSPeter Ujfalusi 341d2458baaSPeter Ujfalusi for (count = 0; count < header->num_modules; count++) { 342d2458baaSPeter Ujfalusi /* check for wrap */ 343d2458baaSPeter Ujfalusi if (remaining < sizeof(*module)) { 344d2458baaSPeter Ujfalusi dev_err(sdev->dev, "%s: not enough data for a module\n", 345d2458baaSPeter Ujfalusi __func__); 346d2458baaSPeter Ujfalusi return -EINVAL; 347d2458baaSPeter Ujfalusi } 348d2458baaSPeter Ujfalusi 349d2458baaSPeter Ujfalusi /* minus header size of module */ 350d2458baaSPeter Ujfalusi remaining -= sizeof(*module); 351d2458baaSPeter Ujfalusi 352d2458baaSPeter Ujfalusi /* module */ 353d2458baaSPeter Ujfalusi ret = load_module(sdev, module); 354d2458baaSPeter Ujfalusi if (ret < 0) { 355d2458baaSPeter Ujfalusi dev_err(sdev->dev, "%s: invalid module %d\n", __func__, count); 356d2458baaSPeter Ujfalusi return ret; 357d2458baaSPeter Ujfalusi } 358d2458baaSPeter Ujfalusi 359d2458baaSPeter Ujfalusi if (remaining < module->size) { 360d2458baaSPeter Ujfalusi dev_err(sdev->dev, "%s: not enough data remaining\n", __func__); 361d2458baaSPeter Ujfalusi return -EINVAL; 362d2458baaSPeter Ujfalusi } 363d2458baaSPeter Ujfalusi 364d2458baaSPeter Ujfalusi /* minus body size of module */ 365d2458baaSPeter Ujfalusi remaining -= module->size; 366d2458baaSPeter Ujfalusi module = (struct snd_sof_mod_hdr *)((u8 *)module + 367d2458baaSPeter Ujfalusi sizeof(*module) + module->size); 368d2458baaSPeter Ujfalusi } 369d2458baaSPeter Ujfalusi 370d2458baaSPeter Ujfalusi return 0; 371d2458baaSPeter Ujfalusi } 372d2458baaSPeter Ujfalusi 373d2458baaSPeter Ujfalusi static int sof_ipc3_validate_firmware(struct snd_sof_dev *sdev) 374d2458baaSPeter Ujfalusi { 375d2458baaSPeter Ujfalusi struct snd_sof_pdata *plat_data = sdev->pdata; 376d2458baaSPeter Ujfalusi const struct firmware *fw = plat_data->fw; 377d2458baaSPeter Ujfalusi struct snd_sof_fw_header *header; 378d2458baaSPeter Ujfalusi size_t fw_size = fw->size - plat_data->fw_offset; 379d2458baaSPeter Ujfalusi 380d2458baaSPeter Ujfalusi if (fw->size <= plat_data->fw_offset) { 381d2458baaSPeter Ujfalusi dev_err(sdev->dev, 382d2458baaSPeter Ujfalusi "firmware size must be greater than firmware offset\n"); 383d2458baaSPeter Ujfalusi return -EINVAL; 384d2458baaSPeter Ujfalusi } 385d2458baaSPeter Ujfalusi 386d2458baaSPeter Ujfalusi /* Read the header information from the data pointer */ 387d2458baaSPeter Ujfalusi header = (struct snd_sof_fw_header *)(fw->data + plat_data->fw_offset); 388d2458baaSPeter Ujfalusi 389d2458baaSPeter Ujfalusi /* verify FW sig */ 390d2458baaSPeter Ujfalusi if (strncmp(header->sig, SND_SOF_FW_SIG, SND_SOF_FW_SIG_SIZE) != 0) { 391d2458baaSPeter Ujfalusi dev_err(sdev->dev, "invalid firmware signature\n"); 392d2458baaSPeter Ujfalusi return -EINVAL; 393d2458baaSPeter Ujfalusi } 394d2458baaSPeter Ujfalusi 395d2458baaSPeter Ujfalusi /* check size is valid */ 396d2458baaSPeter Ujfalusi if (fw_size != header->file_size + sizeof(*header)) { 397d2458baaSPeter Ujfalusi dev_err(sdev->dev, 398d2458baaSPeter Ujfalusi "invalid filesize mismatch got 0x%zx expected 0x%zx\n", 399d2458baaSPeter Ujfalusi fw_size, header->file_size + sizeof(*header)); 400d2458baaSPeter Ujfalusi return -EINVAL; 401d2458baaSPeter Ujfalusi } 402d2458baaSPeter Ujfalusi 403d2458baaSPeter Ujfalusi dev_dbg(sdev->dev, "header size=0x%x modules=0x%x abi=0x%x size=%zu\n", 404d2458baaSPeter Ujfalusi header->file_size, header->num_modules, 405d2458baaSPeter Ujfalusi header->abi, sizeof(*header)); 406d2458baaSPeter Ujfalusi 407d2458baaSPeter Ujfalusi return 0; 408d2458baaSPeter Ujfalusi } 409d2458baaSPeter Ujfalusi 410d2458baaSPeter Ujfalusi const struct sof_ipc_fw_loader_ops ipc3_loader_ops = { 411d2458baaSPeter Ujfalusi .validate = sof_ipc3_validate_firmware, 412d2458baaSPeter Ujfalusi .parse_ext_manifest = sof_ipc3_fw_parse_ext_man, 413d2458baaSPeter Ujfalusi .load_fw_to_dsp = sof_ipc3_load_fw_to_dsp, 414d2458baaSPeter Ujfalusi }; 415