ASoC: SOF: debug: Add support for IPC message injection
authorPeter Ujfalusi <peter.ujfalusi@linux.intel.com>
Tue, 16 Nov 2021 15:21:37 +0000 (17:21 +0200)
committerMark Brown <broonie@kernel.org>
Wed, 17 Nov 2021 13:04:51 +0000 (13:04 +0000)
In order to stress test the firmware's ability to handle (mis)crafted
IPC messages this patch adds a debugfs interface where a binary file
(message) can be written and the message is sent to the firmware as it is.

Read on the same file will return the reply from the firmware if it is
available as a binary.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Rander Wang <rander.wang@intel.com>
Reviewed-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Reviewed-by: Guennadi Liakhovetski <guennadi.liakhovetski@linux.intel.com>
Signed-off-by: Daniel Baluta <daniel.baluta@nxp.com>
Link: https://lore.kernel.org/r/20211116152137.52129-5-daniel.baluta@oss.nxp.com
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/sof/Kconfig
sound/soc/sof/debug.c
sound/soc/sof/sof-priv.h

index b6fa659179b6026028f6cbb3dd099b4226412398..89eea55581906e400225e8c589a31e8a9194653b 100644 (file)
@@ -194,6 +194,14 @@ config SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST
          Say Y if you want to enable IPC flood test.
          If unsure, select "N".
 
+config SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR
+       bool "SOF enable IPC message injector"
+       help
+         This option enables the IPC message injector which can be used to send
+         crafted IPC messages to the DSP to test its robustness.
+         Say Y if you want to enable the IPC message injector.
+         If unsure, select "N".
+
 config SND_SOC_SOF_DEBUG_RETAIN_DSP_CONTEXT
        bool "SOF retain DSP context on any FW exceptions"
        help
index dc1df5fb7b4cabc099ce2bfd7f7eb1ff12e6427f..2f8b5ac9b78a2864e49dd721fde2a6b38936ad3f 100644 (file)
@@ -336,6 +336,104 @@ static int sof_debug_ipc_flood_test(struct snd_sof_dev *sdev,
 }
 #endif
 
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR)
+static ssize_t msg_inject_read(struct file *file, char __user *buffer,
+                              size_t count, loff_t *ppos)
+{
+       struct snd_sof_dfsentry *dfse = file->private_data;
+       struct sof_ipc_reply *rhdr = dfse->msg_inject_rx;
+
+       if (!rhdr->hdr.size || !count || *ppos)
+               return 0;
+
+       if (count > rhdr->hdr.size)
+               count = rhdr->hdr.size;
+
+       if (copy_to_user(buffer, dfse->msg_inject_rx, count))
+               return -EFAULT;
+
+       *ppos += count;
+       return count;
+}
+
+static ssize_t msg_inject_write(struct file *file, const char __user *buffer,
+                               size_t count, loff_t *ppos)
+{
+       struct snd_sof_dfsentry *dfse = file->private_data;
+       struct snd_sof_dev *sdev = dfse->sdev;
+       struct sof_ipc_cmd_hdr *hdr = dfse->msg_inject_tx;
+       size_t size;
+       int ret, err;
+
+       if (*ppos)
+               return 0;
+
+       size = simple_write_to_buffer(dfse->msg_inject_tx, SOF_IPC_MSG_MAX_SIZE,
+                                     ppos, buffer, count);
+       if (size != count)
+               return size > 0 ? -EFAULT : size;
+
+       ret = pm_runtime_get_sync(sdev->dev);
+       if (ret < 0 && ret != -EACCES) {
+               dev_err_ratelimited(sdev->dev, "%s: DSP resume failed: %d\n",
+                                   __func__, ret);
+               pm_runtime_put_noidle(sdev->dev);
+               goto out;
+       }
+
+       /* send the message */
+       memset(dfse->msg_inject_rx, 0, SOF_IPC_MSG_MAX_SIZE);
+       ret = sof_ipc_tx_message(sdev->ipc, hdr->cmd, dfse->msg_inject_tx, count,
+                                dfse->msg_inject_rx, SOF_IPC_MSG_MAX_SIZE);
+
+       pm_runtime_mark_last_busy(sdev->dev);
+       err = pm_runtime_put_autosuspend(sdev->dev);
+       if (err < 0)
+               dev_err_ratelimited(sdev->dev, "%s: DSP idle failed: %d\n",
+                                   __func__, err);
+
+       /* return size if test is successful */
+       if (ret >= 0)
+               ret = size;
+
+out:
+       return ret;
+}
+
+static const struct file_operations msg_inject_fops = {
+       .open = simple_open,
+       .read = msg_inject_read,
+       .write = msg_inject_write,
+       .llseek = default_llseek,
+};
+
+static int snd_sof_debugfs_msg_inject_item(struct snd_sof_dev *sdev,
+                                          const char *name, mode_t mode,
+                                          const struct file_operations *fops)
+{
+       struct snd_sof_dfsentry *dfse;
+
+       dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL);
+       if (!dfse)
+               return -ENOMEM;
+
+       /* pre allocate the tx and rx buffers */
+       dfse->msg_inject_tx = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL);
+       dfse->msg_inject_rx = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL);
+       if (!dfse->msg_inject_tx || !dfse->msg_inject_rx)
+               return -ENOMEM;
+
+       dfse->type = SOF_DFSENTRY_TYPE_BUF;
+       dfse->sdev = sdev;
+
+       debugfs_create_file(name, mode, sdev->debugfs_root, dfse, fops);
+       /* add to dfsentry list */
+       list_add(&dfse->list, &sdev->dfsentry_list);
+
+       return 0;
+}
+#endif
+
 static ssize_t sof_dfsentry_write(struct file *file, const char __user *buffer,
                                  size_t count, loff_t *ppos)
 {
@@ -812,6 +910,15 @@ int snd_sof_dbg_init(struct snd_sof_dev *sdev)
                return err;
 #endif
 
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR)
+       err = snd_sof_debugfs_msg_inject_item(sdev, "ipc_msg_inject", 0644,
+                                             &msg_inject_fops);
+
+       /* errors are only due to memory allocation, not debugfs */
+       if (err < 0)
+               return err;
+#endif
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(snd_sof_dbg_init);
index 2c97ffa98e3eb8b2bc88aadfc944cc2fe72404a1..9a8af76b2f8b93d8a7c24b1c00f030dd1e2e8c36 100644 (file)
@@ -325,6 +325,10 @@ struct snd_sof_dfsentry {
        enum sof_debugfs_access_type access_type;
 #if ENABLE_DEBUGFS_CACHEBUF
        char *cache_buf; /* buffer to cache the contents of debugfs memory */
+#endif
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR)
+       void *msg_inject_tx;
+       void *msg_inject_rx;
 #endif
        struct snd_sof_dev *sdev;
        struct list_head list;  /* list in sdev dfsentry list */