cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

ipc3-loader.c (11718B)


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