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

loader.c (5070B)


      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) 2018 Intel Corporation. All rights reserved.
      7//
      8// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
      9//
     10// Generic firmware loader.
     11//
     12
     13#include <linux/firmware.h>
     14#include "sof-priv.h"
     15#include "ops.h"
     16
     17int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev)
     18{
     19	struct snd_sof_pdata *plat_data = sdev->pdata;
     20	const char *fw_filename;
     21	ssize_t ext_man_size;
     22	int ret;
     23
     24	/* Don't request firmware again if firmware is already requested */
     25	if (plat_data->fw)
     26		return 0;
     27
     28	fw_filename = kasprintf(GFP_KERNEL, "%s/%s",
     29				plat_data->fw_filename_prefix,
     30				plat_data->fw_filename);
     31	if (!fw_filename)
     32		return -ENOMEM;
     33
     34	ret = request_firmware(&plat_data->fw, fw_filename, sdev->dev);
     35
     36	if (ret < 0) {
     37		dev_err(sdev->dev,
     38			"error: sof firmware file is missing, you might need to\n");
     39		dev_err(sdev->dev,
     40			"       download it from https://github.com/thesofproject/sof-bin/\n");
     41		goto err;
     42	} else {
     43		dev_dbg(sdev->dev, "request_firmware %s successful\n",
     44			fw_filename);
     45	}
     46
     47	/* check for extended manifest */
     48	ext_man_size = sdev->ipc->ops->fw_loader->parse_ext_manifest(sdev);
     49	if (ext_man_size > 0) {
     50		/* when no error occurred, drop extended manifest */
     51		plat_data->fw_offset = ext_man_size;
     52	} else if (!ext_man_size) {
     53		/* No extended manifest, so nothing to skip during FW load */
     54		dev_dbg(sdev->dev, "firmware doesn't contain extended manifest\n");
     55	} else {
     56		ret = ext_man_size;
     57		dev_err(sdev->dev, "error: firmware %s contains unsupported or invalid extended manifest: %d\n",
     58			fw_filename, ret);
     59	}
     60
     61err:
     62	kfree(fw_filename);
     63
     64	return ret;
     65}
     66EXPORT_SYMBOL(snd_sof_load_firmware_raw);
     67
     68int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev)
     69{
     70	struct snd_sof_pdata *plat_data = sdev->pdata;
     71	int ret;
     72
     73	ret = snd_sof_load_firmware_raw(sdev);
     74	if (ret < 0)
     75		return ret;
     76
     77	/* make sure the FW header and file is valid */
     78	ret = sdev->ipc->ops->fw_loader->validate(sdev);
     79	if (ret < 0) {
     80		dev_err(sdev->dev, "error: invalid FW header\n");
     81		goto error;
     82	}
     83
     84	/* prepare the DSP for FW loading */
     85	ret = snd_sof_dsp_reset(sdev);
     86	if (ret < 0) {
     87		dev_err(sdev->dev, "error: failed to reset DSP\n");
     88		goto error;
     89	}
     90
     91	/* parse and load firmware modules to DSP */
     92	if (sdev->ipc->ops->fw_loader->load_fw_to_dsp) {
     93		ret = sdev->ipc->ops->fw_loader->load_fw_to_dsp(sdev);
     94		if (ret < 0) {
     95			dev_err(sdev->dev, "Firmware loading failed\n");
     96			goto error;
     97		}
     98	}
     99
    100	return 0;
    101
    102error:
    103	release_firmware(plat_data->fw);
    104	plat_data->fw = NULL;
    105	return ret;
    106
    107}
    108EXPORT_SYMBOL(snd_sof_load_firmware_memcpy);
    109
    110int snd_sof_run_firmware(struct snd_sof_dev *sdev)
    111{
    112	int ret;
    113
    114	init_waitqueue_head(&sdev->boot_wait);
    115
    116	/* (re-)enable dsp dump */
    117	sdev->dbg_dump_printed = false;
    118	sdev->ipc_dump_printed = false;
    119
    120	/* create read-only fw_version debugfs to store boot version info */
    121	if (sdev->first_boot) {
    122		ret = snd_sof_debugfs_buf_item(sdev, &sdev->fw_version,
    123					       sizeof(sdev->fw_version),
    124					       "fw_version", 0444);
    125		/* errors are only due to memory allocation, not debugfs */
    126		if (ret < 0) {
    127			dev_err(sdev->dev, "error: snd_sof_debugfs_buf_item failed\n");
    128			return ret;
    129		}
    130	}
    131
    132	/* perform pre fw run operations */
    133	ret = snd_sof_dsp_pre_fw_run(sdev);
    134	if (ret < 0) {
    135		dev_err(sdev->dev, "error: failed pre fw run op\n");
    136		return ret;
    137	}
    138
    139	dev_dbg(sdev->dev, "booting DSP firmware\n");
    140
    141	/* boot the firmware on the DSP */
    142	ret = snd_sof_dsp_run(sdev);
    143	if (ret < 0) {
    144		snd_sof_dsp_dbg_dump(sdev, "Failed to start DSP",
    145				     SOF_DBG_DUMP_MBOX | SOF_DBG_DUMP_PCI);
    146		return ret;
    147	}
    148
    149	/*
    150	 * now wait for the DSP to boot. There are 3 possible outcomes:
    151	 * 1. Boot wait times out indicating FW boot failure.
    152	 * 2. FW boots successfully and fw_ready op succeeds.
    153	 * 3. FW boots but fw_ready op fails.
    154	 */
    155	ret = wait_event_timeout(sdev->boot_wait,
    156				 sdev->fw_state > SOF_FW_BOOT_IN_PROGRESS,
    157				 msecs_to_jiffies(sdev->boot_timeout));
    158	if (ret == 0) {
    159		snd_sof_dsp_dbg_dump(sdev, "Firmware boot failure due to timeout",
    160				     SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX |
    161				     SOF_DBG_DUMP_TEXT | SOF_DBG_DUMP_PCI);
    162		return -EIO;
    163	}
    164
    165	if (sdev->fw_state == SOF_FW_BOOT_READY_FAILED)
    166		return -EIO; /* FW boots but fw_ready op failed */
    167
    168	/* perform post fw run operations */
    169	ret = snd_sof_dsp_post_fw_run(sdev);
    170	if (ret < 0) {
    171		dev_err(sdev->dev, "error: failed post fw run op\n");
    172		return ret;
    173	}
    174
    175	dev_dbg(sdev->dev, "firmware boot complete\n");
    176	sof_set_fw_state(sdev, SOF_FW_BOOT_COMPLETE);
    177
    178	if (sdev->first_boot && sdev->ipc->ops->fw_loader->query_fw_configuration)
    179		return sdev->ipc->ops->fw_loader->query_fw_configuration(sdev);
    180
    181	return 0;
    182}
    183EXPORT_SYMBOL(snd_sof_run_firmware);
    184
    185void snd_sof_fw_unload(struct snd_sof_dev *sdev)
    186{
    187	/* TODO: support module unloading at runtime */
    188	release_firmware(sdev->pdata->fw);
    189	sdev->pdata->fw = NULL;
    190}
    191EXPORT_SYMBOL(snd_sof_fw_unload);