Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
0002 //
0003 // This file is provided under a dual BSD/GPLv2 license.  When using or
0004 // redistributing this file, you may do so under either license.
0005 //
0006 // Copyright(c) 2022 Intel Corporation. All rights reserved.
0007 
0008 #include <linux/firmware.h>
0009 #include "sof-priv.h"
0010 #include "sof-audio.h"
0011 #include "ipc3-priv.h"
0012 #include "ops.h"
0013 
0014 static int ipc3_fw_ext_man_get_version(struct snd_sof_dev *sdev,
0015                        const struct sof_ext_man_elem_header *hdr)
0016 {
0017     const struct sof_ext_man_fw_version *v =
0018         container_of(hdr, struct sof_ext_man_fw_version, hdr);
0019 
0020     memcpy(&sdev->fw_ready.version, &v->version, sizeof(v->version));
0021     sdev->fw_ready.flags = v->flags;
0022 
0023     /* log ABI versions and check FW compatibility */
0024     return sof_ipc3_validate_fw_version(sdev);
0025 }
0026 
0027 static int ipc3_fw_ext_man_get_windows(struct snd_sof_dev *sdev,
0028                        const struct sof_ext_man_elem_header *hdr)
0029 {
0030     const struct sof_ext_man_window *w;
0031 
0032     w = container_of(hdr, struct sof_ext_man_window, hdr);
0033 
0034     return sof_ipc3_get_ext_windows(sdev, &w->ipc_window.ext_hdr);
0035 }
0036 
0037 static int ipc3_fw_ext_man_get_cc_info(struct snd_sof_dev *sdev,
0038                        const struct sof_ext_man_elem_header *hdr)
0039 {
0040     const struct sof_ext_man_cc_version *cc;
0041 
0042     cc = container_of(hdr, struct sof_ext_man_cc_version, hdr);
0043 
0044     return sof_ipc3_get_cc_info(sdev, &cc->cc_version.ext_hdr);
0045 }
0046 
0047 static int ipc3_fw_ext_man_get_dbg_abi_info(struct snd_sof_dev *sdev,
0048                         const struct sof_ext_man_elem_header *hdr)
0049 {
0050     const struct ext_man_dbg_abi *dbg_abi =
0051         container_of(hdr, struct ext_man_dbg_abi, hdr);
0052 
0053     if (sdev->first_boot)
0054         dev_dbg(sdev->dev,
0055             "Firmware: DBG_ABI %d:%d:%d\n",
0056             SOF_ABI_VERSION_MAJOR(dbg_abi->dbg_abi.abi_dbg_version),
0057             SOF_ABI_VERSION_MINOR(dbg_abi->dbg_abi.abi_dbg_version),
0058             SOF_ABI_VERSION_PATCH(dbg_abi->dbg_abi.abi_dbg_version));
0059 
0060     return 0;
0061 }
0062 
0063 static int ipc3_fw_ext_man_get_config_data(struct snd_sof_dev *sdev,
0064                        const struct sof_ext_man_elem_header *hdr)
0065 {
0066     const struct sof_ext_man_config_data *config =
0067         container_of(hdr, struct sof_ext_man_config_data, hdr);
0068     const struct sof_config_elem *elem;
0069     int elems_counter;
0070     int elems_size;
0071     int ret = 0;
0072     int i;
0073 
0074     /* calculate elements counter */
0075     elems_size = config->hdr.size - sizeof(struct sof_ext_man_elem_header);
0076     elems_counter = elems_size / sizeof(struct sof_config_elem);
0077 
0078     dev_dbg(sdev->dev, "manifest can hold up to %d config elements\n", elems_counter);
0079 
0080     for (i = 0; i < elems_counter; ++i) {
0081         elem = &config->elems[i];
0082         dev_dbg(sdev->dev, "get index %d token %d val %d\n",
0083             i, elem->token, elem->value);
0084         switch (elem->token) {
0085         case SOF_EXT_MAN_CONFIG_EMPTY:
0086             /* unused memory space is zero filled - mapped to EMPTY elements */
0087             break;
0088         case SOF_EXT_MAN_CONFIG_IPC_MSG_SIZE:
0089             /* TODO: use ipc msg size from config data */
0090             break;
0091         case SOF_EXT_MAN_CONFIG_MEMORY_USAGE_SCAN:
0092             if (sdev->first_boot && elem->value)
0093                 ret = snd_sof_dbg_memory_info_init(sdev);
0094             break;
0095         default:
0096             dev_info(sdev->dev,
0097                  "Unknown firmware configuration token %d value %d",
0098                  elem->token, elem->value);
0099             break;
0100         }
0101         if (ret < 0) {
0102             dev_err(sdev->dev,
0103                 "%s: processing failed for token %d value %#x, %d\n",
0104                 __func__, elem->token, elem->value, ret);
0105             return ret;
0106         }
0107     }
0108 
0109     return 0;
0110 }
0111 
0112 static ssize_t ipc3_fw_ext_man_size(struct snd_sof_dev *sdev, const struct firmware *fw)
0113 {
0114     const struct sof_ext_man_header *head;
0115 
0116     head = (struct sof_ext_man_header *)fw->data;
0117 
0118     /*
0119      * assert fw size is big enough to contain extended manifest header,
0120      * it prevents from reading unallocated memory from `head` in following
0121      * step.
0122      */
0123     if (fw->size < sizeof(*head))
0124         return -EINVAL;
0125 
0126     /*
0127      * When fw points to extended manifest,
0128      * then first u32 must be equal SOF_EXT_MAN_MAGIC_NUMBER.
0129      */
0130     if (head->magic == SOF_EXT_MAN_MAGIC_NUMBER)
0131         return head->full_size;
0132 
0133     /* otherwise given fw don't have an extended manifest */
0134     dev_dbg(sdev->dev, "Unexpected extended manifest magic number: %#x\n",
0135         head->magic);
0136     return 0;
0137 }
0138 
0139 static size_t sof_ipc3_fw_parse_ext_man(struct snd_sof_dev *sdev)
0140 {
0141     struct snd_sof_pdata *plat_data = sdev->pdata;
0142     const struct firmware *fw = plat_data->fw;
0143     const struct sof_ext_man_elem_header *elem_hdr;
0144     const struct sof_ext_man_header *head;
0145     ssize_t ext_man_size;
0146     ssize_t remaining;
0147     uintptr_t iptr;
0148     int ret = 0;
0149 
0150     head = (struct sof_ext_man_header *)fw->data;
0151     remaining = head->full_size - head->header_size;
0152     ext_man_size = ipc3_fw_ext_man_size(sdev, fw);
0153 
0154     /* Assert firmware starts with extended manifest */
0155     if (ext_man_size <= 0)
0156         return ext_man_size;
0157 
0158     /* incompatible version */
0159     if (SOF_EXT_MAN_VERSION_INCOMPATIBLE(SOF_EXT_MAN_VERSION,
0160                          head->header_version)) {
0161         dev_err(sdev->dev,
0162             "extended manifest version %#x differ from used %#x\n",
0163             head->header_version, SOF_EXT_MAN_VERSION);
0164         return -EINVAL;
0165     }
0166 
0167     /* get first extended manifest element header */
0168     iptr = (uintptr_t)fw->data + head->header_size;
0169 
0170     while (remaining > sizeof(*elem_hdr)) {
0171         elem_hdr = (struct sof_ext_man_elem_header *)iptr;
0172 
0173         dev_dbg(sdev->dev, "found sof_ext_man header type %d size %#x\n",
0174             elem_hdr->type, elem_hdr->size);
0175 
0176         if (elem_hdr->size < sizeof(*elem_hdr) ||
0177             elem_hdr->size > remaining) {
0178             dev_err(sdev->dev,
0179                 "invalid sof_ext_man header size, type %d size %#x\n",
0180                 elem_hdr->type, elem_hdr->size);
0181             return -EINVAL;
0182         }
0183 
0184         /* process structure data */
0185         switch (elem_hdr->type) {
0186         case SOF_EXT_MAN_ELEM_FW_VERSION:
0187             ret = ipc3_fw_ext_man_get_version(sdev, elem_hdr);
0188             break;
0189         case SOF_EXT_MAN_ELEM_WINDOW:
0190             ret = ipc3_fw_ext_man_get_windows(sdev, elem_hdr);
0191             break;
0192         case SOF_EXT_MAN_ELEM_CC_VERSION:
0193             ret = ipc3_fw_ext_man_get_cc_info(sdev, elem_hdr);
0194             break;
0195         case SOF_EXT_MAN_ELEM_DBG_ABI:
0196             ret = ipc3_fw_ext_man_get_dbg_abi_info(sdev, elem_hdr);
0197             break;
0198         case SOF_EXT_MAN_ELEM_CONFIG_DATA:
0199             ret = ipc3_fw_ext_man_get_config_data(sdev, elem_hdr);
0200             break;
0201         case SOF_EXT_MAN_ELEM_PLATFORM_CONFIG_DATA:
0202             ret = snd_sof_dsp_parse_platform_ext_manifest(sdev, elem_hdr);
0203             break;
0204         default:
0205             dev_info(sdev->dev,
0206                  "unknown sof_ext_man header type %d size %#x\n",
0207                  elem_hdr->type, elem_hdr->size);
0208             break;
0209         }
0210 
0211         if (ret < 0) {
0212             dev_err(sdev->dev,
0213                 "failed to parse sof_ext_man header type %d size %#x\n",
0214                 elem_hdr->type, elem_hdr->size);
0215             return ret;
0216         }
0217 
0218         remaining -= elem_hdr->size;
0219         iptr += elem_hdr->size;
0220     }
0221 
0222     if (remaining) {
0223         dev_err(sdev->dev, "error: sof_ext_man header is inconsistent\n");
0224         return -EINVAL;
0225     }
0226 
0227     return ext_man_size;
0228 }
0229 
0230 /* generic module parser for mmaped DSPs */
0231 static int sof_ipc3_parse_module_memcpy(struct snd_sof_dev *sdev,
0232                     struct snd_sof_mod_hdr *module)
0233 {
0234     struct snd_sof_blk_hdr *block;
0235     int count, ret;
0236     u32 offset;
0237     size_t remaining;
0238 
0239     dev_dbg(sdev->dev, "new module size %#x blocks %#x type %#x\n",
0240         module->size, module->num_blocks, module->type);
0241 
0242     block = (struct snd_sof_blk_hdr *)((u8 *)module + sizeof(*module));
0243 
0244     /* module->size doesn't include header size */
0245     remaining = module->size;
0246     for (count = 0; count < module->num_blocks; count++) {
0247         /* check for wrap */
0248         if (remaining < sizeof(*block)) {
0249             dev_err(sdev->dev, "not enough data remaining\n");
0250             return -EINVAL;
0251         }
0252 
0253         /* minus header size of block */
0254         remaining -= sizeof(*block);
0255 
0256         if (block->size == 0) {
0257             dev_warn(sdev->dev,
0258                  "warning: block %d size zero\n", count);
0259             dev_warn(sdev->dev, " type %#x offset %#x\n",
0260                  block->type, block->offset);
0261             continue;
0262         }
0263 
0264         switch (block->type) {
0265         case SOF_FW_BLK_TYPE_RSRVD0:
0266         case SOF_FW_BLK_TYPE_ROM...SOF_FW_BLK_TYPE_RSRVD14:
0267             continue;   /* not handled atm */
0268         case SOF_FW_BLK_TYPE_IRAM:
0269         case SOF_FW_BLK_TYPE_DRAM:
0270         case SOF_FW_BLK_TYPE_SRAM:
0271             offset = block->offset;
0272             break;
0273         default:
0274             dev_err(sdev->dev, "%s: bad type %#x for block %#x\n",
0275                 __func__, block->type, count);
0276             return -EINVAL;
0277         }
0278 
0279         dev_dbg(sdev->dev, "block %d type %#x size %#x ==>  offset %#x\n",
0280             count, block->type, block->size, offset);
0281 
0282         /* checking block->size to avoid unaligned access */
0283         if (block->size % sizeof(u32)) {
0284             dev_err(sdev->dev, "%s: invalid block size %#x\n",
0285                 __func__, block->size);
0286             return -EINVAL;
0287         }
0288         ret = snd_sof_dsp_block_write(sdev, block->type, offset,
0289                           block + 1, block->size);
0290         if (ret < 0) {
0291             dev_err(sdev->dev, "%s: write to block type %#x failed\n",
0292                 __func__, block->type);
0293             return ret;
0294         }
0295 
0296         if (remaining < block->size) {
0297             dev_err(sdev->dev, "%s: not enough data remaining\n", __func__);
0298             return -EINVAL;
0299         }
0300 
0301         /* minus body size of block */
0302         remaining -= block->size;
0303         /* next block */
0304         block = (struct snd_sof_blk_hdr *)((u8 *)block + sizeof(*block)
0305             + block->size);
0306     }
0307 
0308     return 0;
0309 }
0310 
0311 static int sof_ipc3_load_fw_to_dsp(struct snd_sof_dev *sdev)
0312 {
0313     struct snd_sof_pdata *plat_data = sdev->pdata;
0314     const struct firmware *fw = plat_data->fw;
0315     struct snd_sof_fw_header *header;
0316     struct snd_sof_mod_hdr *module;
0317     int (*load_module)(struct snd_sof_dev *sof_dev, struct snd_sof_mod_hdr *hdr);
0318     size_t remaining;
0319     int ret, count;
0320 
0321     if (!plat_data->fw)
0322         return -EINVAL;
0323 
0324     header = (struct snd_sof_fw_header *)(fw->data + plat_data->fw_offset);
0325     load_module = sof_ops(sdev)->load_module;
0326     if (!load_module) {
0327         dev_dbg(sdev->dev, "Using generic module loading\n");
0328         load_module = sof_ipc3_parse_module_memcpy;
0329     } else {
0330         dev_dbg(sdev->dev, "Using custom module loading\n");
0331     }
0332 
0333     /* parse each module */
0334     module = (struct snd_sof_mod_hdr *)(fw->data + plat_data->fw_offset +
0335                         sizeof(*header));
0336     remaining = fw->size - sizeof(*header) - plat_data->fw_offset;
0337     /* check for wrap */
0338     if (remaining > fw->size) {
0339         dev_err(sdev->dev, "%s: fw size smaller than header size\n", __func__);
0340         return -EINVAL;
0341     }
0342 
0343     for (count = 0; count < header->num_modules; count++) {
0344         /* check for wrap */
0345         if (remaining < sizeof(*module)) {
0346             dev_err(sdev->dev, "%s: not enough data for a module\n",
0347                 __func__);
0348             return -EINVAL;
0349         }
0350 
0351         /* minus header size of module */
0352         remaining -= sizeof(*module);
0353 
0354         /* module */
0355         ret = load_module(sdev, module);
0356         if (ret < 0) {
0357             dev_err(sdev->dev, "%s: invalid module %d\n", __func__, count);
0358             return ret;
0359         }
0360 
0361         if (remaining < module->size) {
0362             dev_err(sdev->dev, "%s: not enough data remaining\n", __func__);
0363             return -EINVAL;
0364         }
0365 
0366         /* minus body size of module */
0367         remaining -=  module->size;
0368         module = (struct snd_sof_mod_hdr *)((u8 *)module +
0369              sizeof(*module) + module->size);
0370     }
0371 
0372     return 0;
0373 }
0374 
0375 static int sof_ipc3_validate_firmware(struct snd_sof_dev *sdev)
0376 {
0377     struct snd_sof_pdata *plat_data = sdev->pdata;
0378     const struct firmware *fw = plat_data->fw;
0379     struct snd_sof_fw_header *header;
0380     size_t fw_size = fw->size - plat_data->fw_offset;
0381 
0382     if (fw->size <= plat_data->fw_offset) {
0383         dev_err(sdev->dev,
0384             "firmware size must be greater than firmware offset\n");
0385         return -EINVAL;
0386     }
0387 
0388     /* Read the header information from the data pointer */
0389     header = (struct snd_sof_fw_header *)(fw->data + plat_data->fw_offset);
0390 
0391     /* verify FW sig */
0392     if (strncmp(header->sig, SND_SOF_FW_SIG, SND_SOF_FW_SIG_SIZE) != 0) {
0393         dev_err(sdev->dev, "invalid firmware signature\n");
0394         return -EINVAL;
0395     }
0396 
0397     /* check size is valid */
0398     if (fw_size != header->file_size + sizeof(*header)) {
0399         dev_err(sdev->dev,
0400             "invalid filesize mismatch got 0x%zx expected 0x%zx\n",
0401             fw_size, header->file_size + sizeof(*header));
0402         return -EINVAL;
0403     }
0404 
0405     dev_dbg(sdev->dev, "header size=0x%x modules=0x%x abi=0x%x size=%zu\n",
0406         header->file_size, header->num_modules,
0407         header->abi, sizeof(*header));
0408 
0409     return 0;
0410 }
0411 
0412 const struct sof_ipc_fw_loader_ops ipc3_loader_ops = {
0413     .validate = sof_ipc3_validate_firmware,
0414     .parse_ext_manifest = sof_ipc3_fw_parse_ext_man,
0415     .load_fw_to_dsp = sof_ipc3_load_fw_to_dsp,
0416 };