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.
This commit is contained in:
commit
745a8e7cbe
|
@ -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;
|
||||
}
|
||||
|
||||
/* read the message */
|
||||
if (msg->reply_size > 0)
|
||||
sof_mailbox_read(sdev, sdev->host_box.offset,
|
||||
msg->reply_data, msg->reply_size);
|
||||
snd_sof_ipc_get_reply(sdev);
|
||||
}
|
||||
|
||||
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
|
||||
|
|
Loading…
Reference in New Issue