Commit 745a8e7c authored by Mark Brown's avatar Mark Brown

ASoC: SOF: New debug feature: IPC message injector

Merge series from Daniel Baluta <daniel.baluta@oss.nxp.com>:

Add a test/debug feature allowing IPC messages to the DSP to be injected
via debugfs.
parents 63eb4626 2f0b1b01
......@@ -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
......
......@@ -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);
......
......@@ -59,40 +59,6 @@ struct imx8_priv {
};
static void imx8_get_reply(struct snd_sof_dev *sdev)
{
struct snd_sof_ipc_msg *msg = sdev->msg;
struct sof_ipc_reply reply;
int ret = 0;
if (!msg) {
dev_warn(sdev->dev, "unexpected ipc interrupt\n");
return;
}
/* get reply */
sof_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply));
if (reply.error < 0) {
memcpy(msg->reply_data, &reply, sizeof(reply));
ret = reply.error;
} else {
/* reply has correct size? */
if (reply.hdr.size != msg->reply_size) {
dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n",
msg->reply_size, reply.hdr.size);
ret = -EINVAL;
}
/* read the message */
if (msg->reply_size > 0)
sof_mailbox_read(sdev, sdev->host_box.offset,
msg->reply_data, msg->reply_size);
}
msg->reply_error = ret;
}
static int imx8_get_mailbox_offset(struct snd_sof_dev *sdev)
{
return MBOX_OFFSET;
......@@ -109,8 +75,7 @@ static void imx8_dsp_handle_reply(struct imx_dsp_ipc *ipc)
unsigned long flags;
spin_lock_irqsave(&priv->sdev->ipc_lock, flags);
imx8_get_reply(priv->sdev);
snd_sof_ipc_reply(priv->sdev, 0);
snd_sof_ipc_process_reply(priv->sdev, 0);
spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags);
}
......
......@@ -32,40 +32,6 @@ struct imx8m_priv {
struct platform_device *ipc_dev;
};
static void imx8m_get_reply(struct snd_sof_dev *sdev)
{
struct snd_sof_ipc_msg *msg = sdev->msg;
struct sof_ipc_reply reply;
int ret = 0;
if (!msg) {
dev_warn(sdev->dev, "unexpected ipc interrupt\n");
return;
}
/* get reply */
sof_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply));
if (reply.error < 0) {
memcpy(msg->reply_data, &reply, sizeof(reply));
ret = reply.error;
} else {
/* reply has correct size? */
if (reply.hdr.size != msg->reply_size) {
dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n",
msg->reply_size, reply.hdr.size);
ret = -EINVAL;
}
/* read the message */
if (msg->reply_size > 0)
sof_mailbox_read(sdev, sdev->host_box.offset,
msg->reply_data, msg->reply_size);
}
msg->reply_error = ret;
}
static int imx8m_get_mailbox_offset(struct snd_sof_dev *sdev)
{
return MBOX_OFFSET;
......@@ -82,8 +48,7 @@ static void imx8m_dsp_handle_reply(struct imx_dsp_ipc *ipc)
unsigned long flags;
spin_lock_irqsave(&priv->sdev->ipc_lock, flags);
imx8m_get_reply(priv->sdev);
snd_sof_ipc_reply(priv->sdev, 0);
snd_sof_ipc_process_reply(priv->sdev, 0);
spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags);
}
......
......@@ -27,7 +27,6 @@
static void atom_host_done(struct snd_sof_dev *sdev);
static void atom_dsp_done(struct snd_sof_dev *sdev);
static void atom_get_reply(struct snd_sof_dev *sdev);
/*
* Debug
......@@ -154,8 +153,7 @@ irqreturn_t atom_irq_thread(int irq, void *context)
* because the done bit can't be set in cmd_done function
* which is triggered by msg
*/
atom_get_reply(sdev);
snd_sof_ipc_reply(sdev, ipcx);
snd_sof_ipc_process_reply(sdev, ipcx);
atom_dsp_done(sdev);
......@@ -195,45 +193,6 @@ int atom_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
}
EXPORT_SYMBOL_NS(atom_send_msg, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);
static void atom_get_reply(struct snd_sof_dev *sdev)
{
struct snd_sof_ipc_msg *msg = sdev->msg;
struct sof_ipc_reply reply;
int ret = 0;
/*
* Sometimes, there is unexpected reply ipc arriving. The reply
* ipc belongs to none of the ipcs sent from driver.
* In this case, the driver must ignore the ipc.
*/
if (!msg) {
dev_warn(sdev->dev, "unexpected ipc interrupt raised!\n");
return;
}
/* get reply */
sof_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply));
if (reply.error < 0) {
memcpy(msg->reply_data, &reply, sizeof(reply));
ret = reply.error;
} else {
/* reply correct size ? */
if (reply.hdr.size != msg->reply_size) {
dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n",
msg->reply_size, reply.hdr.size);
ret = -EINVAL;
}
/* read the message */
if (msg->reply_size > 0)
sof_mailbox_read(sdev, sdev->host_box.offset,
msg->reply_data, msg->reply_size);
}
msg->reply_error = ret;
}
int atom_get_mailbox_offset(struct snd_sof_dev *sdev)
{
return MBOX_OFFSET;
......
......@@ -75,7 +75,6 @@ static const struct snd_sof_debugfs_map bdw_debugfs[] = {
static void bdw_host_done(struct snd_sof_dev *sdev);
static void bdw_dsp_done(struct snd_sof_dev *sdev);
static void bdw_get_reply(struct snd_sof_dev *sdev);
/*
* DSP Control.
......@@ -326,8 +325,7 @@ static irqreturn_t bdw_irq_thread(int irq, void *context)
* because the done bit can't be set in cmd_done function
* which is triggered by msg
*/
bdw_get_reply(sdev);
snd_sof_ipc_reply(sdev, ipcx);
snd_sof_ipc_process_reply(sdev, ipcx);
bdw_dsp_done(sdev);
......@@ -372,45 +370,6 @@ static int bdw_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
return 0;
}
static void bdw_get_reply(struct snd_sof_dev *sdev)
{
struct snd_sof_ipc_msg *msg = sdev->msg;
struct sof_ipc_reply reply;
int ret = 0;
/*
* Sometimes, there is unexpected reply ipc arriving. The reply
* ipc belongs to none of the ipcs sent from driver.
* In this case, the driver must ignore the ipc.
*/
if (!msg) {
dev_warn(sdev->dev, "unexpected ipc interrupt raised!\n");
return;
}
/* get reply */
sof_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply));
if (reply.error < 0) {
memcpy(msg->reply_data, &reply, sizeof(reply));
ret = reply.error;
} else {
/* reply correct size ? */
if (reply.hdr.size != msg->reply_size) {
dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n",
msg->reply_size, reply.hdr.size);
ret = -EINVAL;
}
/* read the message */
if (msg->reply_size > 0)
sof_mailbox_read(sdev, sdev->host_box.offset,
msg->reply_data, msg->reply_size);
}
msg->reply_error = ret;
}
static int bdw_get_mailbox_offset(struct snd_sof_dev *sdev)
{
return MBOX_OFFSET;
......
......@@ -70,7 +70,6 @@ void hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev)
struct snd_sof_ipc_msg *msg = sdev->msg;
struct sof_ipc_reply reply;
struct sof_ipc_cmd_hdr *hdr;
int ret = 0;
/*
* Sometimes, there is unexpected reply ipc arriving. The reply
......@@ -94,35 +93,11 @@ void hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev)
reply.hdr.cmd = SOF_IPC_GLB_REPLY;
reply.hdr.size = sizeof(reply);
memcpy(msg->reply_data, &reply, sizeof(reply));
goto out;
}
/* get IPC reply from DSP in the mailbox */
sof_mailbox_read(sdev, sdev->host_box.offset, &reply,
sizeof(reply));
if (reply.error < 0) {
memcpy(msg->reply_data, &reply, sizeof(reply));
ret = reply.error;
msg->reply_error = 0;
} else {
/* reply correct size ? */
if (reply.hdr.size != msg->reply_size &&
/* getter payload is never known upfront */
((reply.hdr.cmd & SOF_GLB_TYPE_MASK) != SOF_IPC_GLB_PROBE)) {
dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n",
msg->reply_size, reply.hdr.size);
ret = -EINVAL;
snd_sof_ipc_get_reply(sdev);
}
/* read the message */
if (msg->reply_size > 0)
sof_mailbox_read(sdev, sdev->host_box.offset,
msg->reply_data, msg->reply_size);
}
out:
msg->reply_error = ret;
}
/* IPC handler thread */
......
......@@ -394,6 +394,67 @@ int sof_ipc_tx_message_no_pm(struct snd_sof_ipc *ipc, u32 header,
}
EXPORT_SYMBOL(sof_ipc_tx_message_no_pm);
/* Generic helper function to retrieve the reply */
void snd_sof_ipc_get_reply(struct snd_sof_dev *sdev)
{
struct snd_sof_ipc_msg *msg = sdev->msg;
struct sof_ipc_reply reply;
int ret = 0;
/*
* Sometimes, there is unexpected reply ipc arriving. The reply
* ipc belongs to none of the ipcs sent from driver.
* In this case, the driver must ignore the ipc.
*/
if (!msg) {
dev_warn(sdev->dev, "unexpected ipc interrupt raised!\n");
return;
}
/* get the generic reply */
snd_sof_dsp_mailbox_read(sdev, sdev->host_box.offset, &reply,
sizeof(reply));
if (reply.error < 0) {
memcpy(msg->reply_data, &reply, sizeof(reply));
ret = reply.error;
} else if (!reply.hdr.size) {
/* Reply should always be >= sizeof(struct sof_ipc_reply) */
if (msg->reply_size)
dev_err(sdev->dev,
"empty reply received, expected %zu bytes\n",
msg->reply_size);
else
dev_err(sdev->dev, "empty reply received\n");
ret = -EINVAL;
} else if (msg->reply_size > 0) {
if (reply.hdr.size == msg->reply_size) {
ret = 0;
} else if (reply.hdr.size < msg->reply_size) {
dev_dbg(sdev->dev,
"reply size (%u) is less than expected (%zu)\n",
reply.hdr.size, msg->reply_size);
msg->reply_size = reply.hdr.size;
ret = 0;
} else {
dev_err(sdev->dev,
"reply size (%u) exceeds the buffer size (%zu)\n",
reply.hdr.size, msg->reply_size);
ret = -EINVAL;
}
/* get the full message if reply.hdr.size <= msg->reply_size */
if (!ret)
snd_sof_dsp_mailbox_read(sdev, sdev->host_box.offset,
msg->reply_data, msg->reply_size);
}
msg->reply_error = ret;
}
EXPORT_SYMBOL(snd_sof_ipc_get_reply);
/* handle reply message from DSP */
void snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id)
{
......
......@@ -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 */
......@@ -515,6 +519,7 @@ void snd_sof_fw_unload(struct snd_sof_dev *sdev);
*/
struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev);
void snd_sof_ipc_free(struct snd_sof_dev *sdev);
void snd_sof_ipc_get_reply(struct snd_sof_dev *sdev);
void snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id);
void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev);
int snd_sof_ipc_stream_pcm_params(struct snd_sof_dev *sdev,
......@@ -527,6 +532,11 @@ int sof_ipc_tx_message_no_pm(struct snd_sof_ipc *ipc, u32 header,
void *msg_data, size_t msg_bytes,
void *reply_data, size_t reply_bytes);
int sof_ipc_init_msg_memory(struct snd_sof_dev *sdev);
static inline void snd_sof_ipc_process_reply(struct snd_sof_dev *sdev, u32 msg_id)
{
snd_sof_ipc_get_reply(sdev);
snd_sof_ipc_reply(sdev, msg_id);
}
/*
* Trace/debug
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment