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

core.c (13995B)


      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
     11#include <linux/firmware.h>
     12#include <linux/module.h>
     13#include <sound/soc.h>
     14#include <sound/sof.h>
     15#include "sof-priv.h"
     16#include "ops.h"
     17
     18/* see SOF_DBG_ flags */
     19static int sof_core_debug =  IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_FIRMWARE_TRACE);
     20module_param_named(sof_debug, sof_core_debug, int, 0444);
     21MODULE_PARM_DESC(sof_debug, "SOF core debug options (0x0 all off)");
     22
     23/* SOF defaults if not provided by the platform in ms */
     24#define TIMEOUT_DEFAULT_IPC_MS  500
     25#define TIMEOUT_DEFAULT_BOOT_MS 2000
     26
     27/**
     28 * sof_debug_check_flag - check if a given flag(s) is set in sof_core_debug
     29 * @mask: Flag or combination of flags to check
     30 *
     31 * Returns true if all bits set in mask is also set in sof_core_debug, otherwise
     32 * false
     33 */
     34bool sof_debug_check_flag(int mask)
     35{
     36	if ((sof_core_debug & mask) == mask)
     37		return true;
     38
     39	return false;
     40}
     41EXPORT_SYMBOL(sof_debug_check_flag);
     42
     43/*
     44 * FW Panic/fault handling.
     45 */
     46
     47struct sof_panic_msg {
     48	u32 id;
     49	const char *msg;
     50};
     51
     52/* standard FW panic types */
     53static const struct sof_panic_msg panic_msg[] = {
     54	{SOF_IPC_PANIC_MEM, "out of memory"},
     55	{SOF_IPC_PANIC_WORK, "work subsystem init failed"},
     56	{SOF_IPC_PANIC_IPC, "IPC subsystem init failed"},
     57	{SOF_IPC_PANIC_ARCH, "arch init failed"},
     58	{SOF_IPC_PANIC_PLATFORM, "platform init failed"},
     59	{SOF_IPC_PANIC_TASK, "scheduler init failed"},
     60	{SOF_IPC_PANIC_EXCEPTION, "runtime exception"},
     61	{SOF_IPC_PANIC_DEADLOCK, "deadlock"},
     62	{SOF_IPC_PANIC_STACK, "stack overflow"},
     63	{SOF_IPC_PANIC_IDLE, "can't enter idle"},
     64	{SOF_IPC_PANIC_WFI, "invalid wait state"},
     65	{SOF_IPC_PANIC_ASSERT, "assertion failed"},
     66};
     67
     68/**
     69 * sof_print_oops_and_stack - Handle the printing of DSP oops and stack trace
     70 * @sdev: Pointer to the device's sdev
     71 * @level: prink log level to use for the printing
     72 * @panic_code: the panic code
     73 * @tracep_code: tracepoint code
     74 * @oops: Pointer to DSP specific oops data
     75 * @panic_info: Pointer to the received panic information message
     76 * @stack: Pointer to the call stack data
     77 * @stack_words: Number of words in the stack data
     78 *
     79 * helper to be called from .dbg_dump callbacks. No error code is
     80 * provided, it's left as an exercise for the caller of .dbg_dump
     81 * (typically IPC or loader)
     82 */
     83void sof_print_oops_and_stack(struct snd_sof_dev *sdev, const char *level,
     84			      u32 panic_code, u32 tracep_code, void *oops,
     85			      struct sof_ipc_panic_info *panic_info,
     86			      void *stack, size_t stack_words)
     87{
     88	u32 code;
     89	int i;
     90
     91	/* is firmware dead ? */
     92	if ((panic_code & SOF_IPC_PANIC_MAGIC_MASK) != SOF_IPC_PANIC_MAGIC) {
     93		dev_printk(level, sdev->dev, "unexpected fault %#010x trace %#010x\n",
     94			   panic_code, tracep_code);
     95		return; /* no fault ? */
     96	}
     97
     98	code = panic_code & (SOF_IPC_PANIC_MAGIC_MASK | SOF_IPC_PANIC_CODE_MASK);
     99
    100	for (i = 0; i < ARRAY_SIZE(panic_msg); i++) {
    101		if (panic_msg[i].id == code) {
    102			dev_printk(level, sdev->dev, "reason: %s (%#x)\n",
    103				   panic_msg[i].msg, code & SOF_IPC_PANIC_CODE_MASK);
    104			dev_printk(level, sdev->dev, "trace point: %#010x\n", tracep_code);
    105			goto out;
    106		}
    107	}
    108
    109	/* unknown error */
    110	dev_printk(level, sdev->dev, "unknown panic code: %#x\n",
    111		   code & SOF_IPC_PANIC_CODE_MASK);
    112	dev_printk(level, sdev->dev, "trace point: %#010x\n", tracep_code);
    113
    114out:
    115	dev_printk(level, sdev->dev, "panic at %s:%d\n", panic_info->filename,
    116		   panic_info->linenum);
    117	sof_oops(sdev, level, oops);
    118	sof_stack(sdev, level, oops, stack, stack_words);
    119}
    120EXPORT_SYMBOL(sof_print_oops_and_stack);
    121
    122/* Helper to manage DSP state */
    123void sof_set_fw_state(struct snd_sof_dev *sdev, enum sof_fw_state new_state)
    124{
    125	if (sdev->fw_state == new_state)
    126		return;
    127
    128	dev_dbg(sdev->dev, "fw_state change: %d -> %d\n", sdev->fw_state, new_state);
    129	sdev->fw_state = new_state;
    130
    131	switch (new_state) {
    132	case SOF_FW_BOOT_NOT_STARTED:
    133	case SOF_FW_BOOT_COMPLETE:
    134	case SOF_FW_CRASHED:
    135		sof_client_fw_state_dispatcher(sdev);
    136		fallthrough;
    137	default:
    138		break;
    139	}
    140}
    141EXPORT_SYMBOL(sof_set_fw_state);
    142
    143/*
    144 *			FW Boot State Transition Diagram
    145 *
    146 *    +----------------------------------------------------------------------+
    147 *    |									     |
    148 * ------------------	     ------------------				     |
    149 * |		    |	     |		      |				     |
    150 * |   BOOT_FAILED  |<-------|  READY_FAILED  |				     |
    151 * |		    |<--+    |	              |	   ------------------	     |
    152 * ------------------	|    ------------------	   |		    |	     |
    153 *	^		|	    ^		   |	CRASHED	    |---+    |
    154 *	|		|	    |		   |		    |	|    |
    155 * (FW Boot Timeout)	|	(FW_READY FAIL)	   ------------------	|    |
    156 *	|		|	    |		     ^			|    |
    157 *	|		|	    |		     |(DSP Panic)	|    |
    158 * ------------------	|	    |		   ------------------	|    |
    159 * |		    |	|	    |		   |		    |	|    |
    160 * |   IN_PROGRESS  |---------------+------------->|    COMPLETE    |	|    |
    161 * |		    | (FW Boot OK)   (FW_READY OK) |		    |	|    |
    162 * ------------------	|			   ------------------	|    |
    163 *	^		|				|		|    |
    164 *	|		|				|		|    |
    165 * (FW Loading OK)	|			(System Suspend/Runtime Suspend)
    166 *	|		|				|		|    |
    167 *	|	(FW Loading Fail)			|		|    |
    168 * ------------------	|	------------------	|		|    |
    169 * |		    |	|	|		 |<-----+		|    |
    170 * |   PREPARE	    |---+	|   NOT_STARTED  |<---------------------+    |
    171 * |		    |		|		 |<--------------------------+
    172 * ------------------		------------------
    173 *    |	    ^			    |	   ^
    174 *    |	    |			    |	   |
    175 *    |	    +-----------------------+	   |
    176 *    |		(DSP Probe OK)		   |
    177 *    |					   |
    178 *    |					   |
    179 *    +------------------------------------+
    180 *	(System Suspend/Runtime Suspend)
    181 */
    182
    183static int sof_probe_continue(struct snd_sof_dev *sdev)
    184{
    185	struct snd_sof_pdata *plat_data = sdev->pdata;
    186	int ret;
    187
    188	/* probe the DSP hardware */
    189	ret = snd_sof_probe(sdev);
    190	if (ret < 0) {
    191		dev_err(sdev->dev, "error: failed to probe DSP %d\n", ret);
    192		return ret;
    193	}
    194
    195	sof_set_fw_state(sdev, SOF_FW_BOOT_PREPARE);
    196
    197	/* check machine info */
    198	ret = sof_machine_check(sdev);
    199	if (ret < 0) {
    200		dev_err(sdev->dev, "error: failed to get machine info %d\n",
    201			ret);
    202		goto dsp_err;
    203	}
    204
    205	/* set up platform component driver */
    206	snd_sof_new_platform_drv(sdev);
    207
    208	/* register any debug/trace capabilities */
    209	ret = snd_sof_dbg_init(sdev);
    210	if (ret < 0) {
    211		/*
    212		 * debugfs issues are suppressed in snd_sof_dbg_init() since
    213		 * we cannot rely on debugfs
    214		 * here we trap errors due to memory allocation only.
    215		 */
    216		dev_err(sdev->dev, "error: failed to init DSP trace/debug %d\n",
    217			ret);
    218		goto dbg_err;
    219	}
    220
    221	/* init the IPC */
    222	sdev->ipc = snd_sof_ipc_init(sdev);
    223	if (!sdev->ipc) {
    224		ret = -ENOMEM;
    225		dev_err(sdev->dev, "error: failed to init DSP IPC %d\n", ret);
    226		goto ipc_err;
    227	}
    228
    229	/* load the firmware */
    230	ret = snd_sof_load_firmware(sdev);
    231	if (ret < 0) {
    232		dev_err(sdev->dev, "error: failed to load DSP firmware %d\n",
    233			ret);
    234		sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED);
    235		goto fw_load_err;
    236	}
    237
    238	sof_set_fw_state(sdev, SOF_FW_BOOT_IN_PROGRESS);
    239
    240	/*
    241	 * Boot the firmware. The FW boot status will be modified
    242	 * in snd_sof_run_firmware() depending on the outcome.
    243	 */
    244	ret = snd_sof_run_firmware(sdev);
    245	if (ret < 0) {
    246		dev_err(sdev->dev, "error: failed to boot DSP firmware %d\n",
    247			ret);
    248		sof_set_fw_state(sdev, SOF_FW_BOOT_FAILED);
    249		goto fw_run_err;
    250	}
    251
    252	if (sof_debug_check_flag(SOF_DBG_ENABLE_TRACE)) {
    253		sdev->fw_trace_is_supported = true;
    254
    255		/* init firmware tracing */
    256		ret = sof_fw_trace_init(sdev);
    257		if (ret < 0) {
    258			/* non fatal */
    259			dev_warn(sdev->dev, "failed to initialize firmware tracing %d\n",
    260				 ret);
    261		}
    262	} else {
    263		dev_dbg(sdev->dev, "SOF firmware trace disabled\n");
    264	}
    265
    266	/* hereafter all FW boot flows are for PM reasons */
    267	sdev->first_boot = false;
    268
    269	/* now register audio DSP platform driver and dai */
    270	ret = devm_snd_soc_register_component(sdev->dev, &sdev->plat_drv,
    271					      sof_ops(sdev)->drv,
    272					      sof_ops(sdev)->num_drv);
    273	if (ret < 0) {
    274		dev_err(sdev->dev,
    275			"error: failed to register DSP DAI driver %d\n", ret);
    276		goto fw_trace_err;
    277	}
    278
    279	ret = snd_sof_machine_register(sdev, plat_data);
    280	if (ret < 0) {
    281		dev_err(sdev->dev,
    282			"error: failed to register machine driver %d\n", ret);
    283		goto fw_trace_err;
    284	}
    285
    286	ret = sof_register_clients(sdev);
    287	if (ret < 0) {
    288		dev_err(sdev->dev, "failed to register clients %d\n", ret);
    289		goto sof_machine_err;
    290	}
    291
    292	/*
    293	 * Some platforms in SOF, ex: BYT, may not have their platform PM
    294	 * callbacks set. Increment the usage count so as to
    295	 * prevent the device from entering runtime suspend.
    296	 */
    297	if (!sof_ops(sdev)->runtime_suspend || !sof_ops(sdev)->runtime_resume)
    298		pm_runtime_get_noresume(sdev->dev);
    299
    300	if (plat_data->sof_probe_complete)
    301		plat_data->sof_probe_complete(sdev->dev);
    302
    303	sdev->probe_completed = true;
    304
    305	return 0;
    306
    307sof_machine_err:
    308	snd_sof_machine_unregister(sdev, plat_data);
    309fw_trace_err:
    310	sof_fw_trace_free(sdev);
    311fw_run_err:
    312	snd_sof_fw_unload(sdev);
    313fw_load_err:
    314	snd_sof_ipc_free(sdev);
    315ipc_err:
    316dbg_err:
    317	snd_sof_free_debug(sdev);
    318dsp_err:
    319	snd_sof_remove(sdev);
    320
    321	/* all resources freed, update state to match */
    322	sof_set_fw_state(sdev, SOF_FW_BOOT_NOT_STARTED);
    323	sdev->first_boot = true;
    324
    325	return ret;
    326}
    327
    328static void sof_probe_work(struct work_struct *work)
    329{
    330	struct snd_sof_dev *sdev =
    331		container_of(work, struct snd_sof_dev, probe_work);
    332	int ret;
    333
    334	ret = sof_probe_continue(sdev);
    335	if (ret < 0) {
    336		/* errors cannot be propagated, log */
    337		dev_err(sdev->dev, "error: %s failed err: %d\n", __func__, ret);
    338	}
    339}
    340
    341int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
    342{
    343	struct snd_sof_dev *sdev;
    344	int ret;
    345
    346	sdev = devm_kzalloc(dev, sizeof(*sdev), GFP_KERNEL);
    347	if (!sdev)
    348		return -ENOMEM;
    349
    350	/* initialize sof device */
    351	sdev->dev = dev;
    352
    353	/* initialize default DSP power state */
    354	sdev->dsp_power_state.state = SOF_DSP_PM_D0;
    355
    356	sdev->pdata = plat_data;
    357	sdev->first_boot = true;
    358	dev_set_drvdata(dev, sdev);
    359
    360	/* check IPC support */
    361	if (!(BIT(plat_data->ipc_type) & plat_data->desc->ipc_supported_mask)) {
    362		dev_err(dev, "ipc_type %d is not supported on this platform, mask is %#x\n",
    363			plat_data->ipc_type, plat_data->desc->ipc_supported_mask);
    364		return -EINVAL;
    365	}
    366
    367	/* init ops, if necessary */
    368	ret = sof_ops_init(sdev);
    369	if (ret < 0)
    370		return ret;
    371
    372	/* check all mandatory ops */
    373	if (!sof_ops(sdev) || !sof_ops(sdev)->probe || !sof_ops(sdev)->run ||
    374	    !sof_ops(sdev)->block_read || !sof_ops(sdev)->block_write ||
    375	    !sof_ops(sdev)->send_msg || !sof_ops(sdev)->load_firmware ||
    376	    !sof_ops(sdev)->ipc_msg_data) {
    377		dev_err(dev, "error: missing mandatory ops\n");
    378		return -EINVAL;
    379	}
    380
    381	INIT_LIST_HEAD(&sdev->pcm_list);
    382	INIT_LIST_HEAD(&sdev->kcontrol_list);
    383	INIT_LIST_HEAD(&sdev->widget_list);
    384	INIT_LIST_HEAD(&sdev->dai_list);
    385	INIT_LIST_HEAD(&sdev->dai_link_list);
    386	INIT_LIST_HEAD(&sdev->route_list);
    387	INIT_LIST_HEAD(&sdev->ipc_client_list);
    388	INIT_LIST_HEAD(&sdev->ipc_rx_handler_list);
    389	INIT_LIST_HEAD(&sdev->fw_state_handler_list);
    390	spin_lock_init(&sdev->ipc_lock);
    391	spin_lock_init(&sdev->hw_lock);
    392	mutex_init(&sdev->power_state_access);
    393	mutex_init(&sdev->ipc_client_mutex);
    394	mutex_init(&sdev->client_event_handler_mutex);
    395
    396	/* set default timeouts if none provided */
    397	if (plat_data->desc->ipc_timeout == 0)
    398		sdev->ipc_timeout = TIMEOUT_DEFAULT_IPC_MS;
    399	else
    400		sdev->ipc_timeout = plat_data->desc->ipc_timeout;
    401	if (plat_data->desc->boot_timeout == 0)
    402		sdev->boot_timeout = TIMEOUT_DEFAULT_BOOT_MS;
    403	else
    404		sdev->boot_timeout = plat_data->desc->boot_timeout;
    405
    406	sof_set_fw_state(sdev, SOF_FW_BOOT_NOT_STARTED);
    407
    408	if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)) {
    409		INIT_WORK(&sdev->probe_work, sof_probe_work);
    410		schedule_work(&sdev->probe_work);
    411		return 0;
    412	}
    413
    414	return sof_probe_continue(sdev);
    415}
    416EXPORT_SYMBOL(snd_sof_device_probe);
    417
    418bool snd_sof_device_probe_completed(struct device *dev)
    419{
    420	struct snd_sof_dev *sdev = dev_get_drvdata(dev);
    421
    422	return sdev->probe_completed;
    423}
    424EXPORT_SYMBOL(snd_sof_device_probe_completed);
    425
    426int snd_sof_device_remove(struct device *dev)
    427{
    428	struct snd_sof_dev *sdev = dev_get_drvdata(dev);
    429	struct snd_sof_pdata *pdata = sdev->pdata;
    430	int ret;
    431
    432	if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE))
    433		cancel_work_sync(&sdev->probe_work);
    434
    435	/*
    436	 * Unregister any registered client device first before IPC and debugfs
    437	 * to allow client drivers to be removed cleanly
    438	 */
    439	sof_unregister_clients(sdev);
    440
    441	/*
    442	 * Unregister machine driver. This will unbind the snd_card which
    443	 * will remove the component driver and unload the topology
    444	 * before freeing the snd_card.
    445	 */
    446	snd_sof_machine_unregister(sdev, pdata);
    447
    448	if (sdev->fw_state > SOF_FW_BOOT_NOT_STARTED) {
    449		sof_fw_trace_free(sdev);
    450		ret = snd_sof_dsp_power_down_notify(sdev);
    451		if (ret < 0)
    452			dev_warn(dev, "error: %d failed to prepare DSP for device removal",
    453				 ret);
    454
    455		snd_sof_ipc_free(sdev);
    456		snd_sof_free_debug(sdev);
    457		snd_sof_remove(sdev);
    458	}
    459
    460	/* release firmware */
    461	snd_sof_fw_unload(sdev);
    462
    463	return 0;
    464}
    465EXPORT_SYMBOL(snd_sof_device_remove);
    466
    467int snd_sof_device_shutdown(struct device *dev)
    468{
    469	struct snd_sof_dev *sdev = dev_get_drvdata(dev);
    470	struct snd_sof_pdata *pdata = sdev->pdata;
    471
    472	if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE))
    473		cancel_work_sync(&sdev->probe_work);
    474
    475	/*
    476	 * make sure clients and machine driver(s) are unregistered to force
    477	 * all userspace devices to be closed prior to the DSP shutdown sequence
    478	 */
    479	sof_unregister_clients(sdev);
    480
    481	snd_sof_machine_unregister(sdev, pdata);
    482
    483	if (sdev->fw_state == SOF_FW_BOOT_COMPLETE)
    484		return snd_sof_shutdown(sdev);
    485
    486	return 0;
    487}
    488EXPORT_SYMBOL(snd_sof_device_shutdown);
    489
    490MODULE_AUTHOR("Liam Girdwood");
    491MODULE_DESCRIPTION("Sound Open Firmware (SOF) Core");
    492MODULE_LICENSE("Dual BSD/GPL");
    493MODULE_ALIAS("platform:sof-audio");
    494MODULE_IMPORT_NS(SND_SOC_SOF_CLIENT);