aboutsummaryrefslogtreecommitdiff
path: root/drivers/soc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/soc')
-rw-r--r--drivers/soc/qcom/qdsp6v2/voice_svc.c26
-rw-r--r--drivers/soc/qcom/scm.c27
2 files changed, 39 insertions, 14 deletions
diff --git a/drivers/soc/qcom/qdsp6v2/voice_svc.c b/drivers/soc/qcom/qdsp6v2/voice_svc.c
index 67c58d1e6d4..c54d9dda420 100644
--- a/drivers/soc/qcom/qdsp6v2/voice_svc.c
+++ b/drivers/soc/qcom/qdsp6v2/voice_svc.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -42,6 +42,12 @@ struct voice_svc_prvt {
struct list_head response_queue;
wait_queue_head_t response_wait;
spinlock_t response_lock;
+ /*
+ * This mutex ensures responses are processed in sequential order and
+ * that no two threads access and free the same response at the same
+ * time.
+ */
+ struct mutex response_mutex_lock;
};
struct apr_data {
@@ -448,6 +454,7 @@ static ssize_t voice_svc_read(struct file *file, char __user *arg,
goto done;
}
+ mutex_lock(&prtd->response_mutex_lock);
spin_lock_irqsave(&prtd->response_lock, spin_flags);
if (list_empty(&prtd->response_queue)) {
@@ -461,7 +468,7 @@ static ssize_t voice_svc_read(struct file *file, char __user *arg,
pr_debug("%s: Read timeout\n", __func__);
ret = -ETIMEDOUT;
- goto done;
+ goto unlock;
} else if (ret > 0 && !list_empty(&prtd->response_queue)) {
pr_debug("%s: Interrupt recieved for response\n",
__func__);
@@ -469,7 +476,7 @@ static ssize_t voice_svc_read(struct file *file, char __user *arg,
pr_debug("%s: Interrupted by SIGNAL %d\n",
__func__, ret);
- goto done;
+ goto unlock;
}
spin_lock_irqsave(&prtd->response_lock, spin_flags);
@@ -488,7 +495,7 @@ static ssize_t voice_svc_read(struct file *file, char __user *arg,
__func__, count, size);
ret = -ENOMEM;
- goto done;
+ goto unlock;
}
if (!access_ok(VERIFY_WRITE, arg, size)) {
@@ -496,7 +503,7 @@ static ssize_t voice_svc_read(struct file *file, char __user *arg,
__func__);
ret = -EPERM;
- goto done;
+ goto unlock;
}
ret = copy_to_user(arg, &resp->resp,
@@ -506,7 +513,7 @@ static ssize_t voice_svc_read(struct file *file, char __user *arg,
pr_err("%s: copy_to_user failed %d\n", __func__, ret);
ret = -EPERM;
- goto done;
+ goto unlock;
}
spin_lock_irqsave(&prtd->response_lock, spin_flags);
@@ -520,6 +527,8 @@ static ssize_t voice_svc_read(struct file *file, char __user *arg,
ret = count;
+unlock:
+ mutex_unlock(&prtd->response_mutex_lock);
done:
return ret;
}
@@ -575,6 +584,7 @@ static int voice_svc_open(struct inode *inode, struct file *file)
INIT_LIST_HEAD(&prtd->response_queue);
init_waitqueue_head(&prtd->response_wait);
spin_lock_init(&prtd->response_lock);
+ mutex_init(&prtd->response_mutex_lock);
file->private_data = (void *)prtd;
/* Current APR implementation doesn't support session based
@@ -625,6 +635,7 @@ static int voice_svc_release(struct inode *inode, struct file *file)
pr_err("%s: Failed to dereg MVM %d\n", __func__, ret);
}
+ mutex_lock(&prtd->response_mutex_lock);
spin_lock_irqsave(&prtd->response_lock, spin_flags);
while (!list_empty(&prtd->response_queue)) {
@@ -638,6 +649,9 @@ static int voice_svc_release(struct inode *inode, struct file *file)
}
spin_unlock_irqrestore(&prtd->response_lock, spin_flags);
+ mutex_unlock(&prtd->response_mutex_lock);
+
+ mutex_destroy(&prtd->response_mutex_lock);
kfree(file->private_data);
file->private_data = NULL;
diff --git a/drivers/soc/qcom/scm.c b/drivers/soc/qcom/scm.c
index 05da047c526..1ffda2eca13 100644
--- a/drivers/soc/qcom/scm.c
+++ b/drivers/soc/qcom/scm.c
@@ -46,9 +46,6 @@ static DEFINE_MUTEX(scm_lock);
#define SMC_ATOMIC_MASK 0x80000000
#define IS_CALL_AVAIL_CMD 1
-#define SCM_BUF_LEN(__cmd_size, __resp_size) \
- (sizeof(struct scm_command) + sizeof(struct scm_response) + \
- __cmd_size + __resp_size)
/**
* struct scm_command - one SCM command buffer
* @len: total available memory for command and response
@@ -120,6 +117,21 @@ struct scm_response {
#endif
+/* Calculate size for buffer given cmd_size and resp_size.
+ * Returns 0 in case the result would overflow size_t.
+ */
+static size_t scm_get_buf_len(size_t cmd_size, size_t resp_size)
+{
+ size_t contents = cmd_size + resp_size;
+ size_t structs = sizeof(struct scm_command) +
+ sizeof(struct scm_response);
+ size_t buf_len = contents + structs;
+
+ if (contents < cmd_size || buf_len < contents)
+ buf_len = 0;
+ return buf_len;
+}
+
/**
* scm_command_to_response() - Get a pointer to a scm_response
* @cmd: command
@@ -342,10 +354,9 @@ int scm_call_noalloc(u32 svc_id, u32 cmd_id, const void *cmd_buf,
void *scm_buf, size_t scm_buf_len)
{
int ret;
- size_t len = SCM_BUF_LEN(cmd_len, resp_len);
+ size_t len = scm_get_buf_len(cmd_len, resp_len);
- if (cmd_len > scm_buf_len || resp_len > scm_buf_len ||
- len > scm_buf_len)
+ if (len == 0 || len > scm_buf_len)
return -EINVAL;
if (!IS_ALIGNED((unsigned long)scm_buf, PAGE_SIZE))
@@ -739,9 +750,9 @@ int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len,
{
struct scm_command *cmd;
int ret;
- size_t len = SCM_BUF_LEN(cmd_len, resp_len);
+ size_t len = scm_get_buf_len(cmd_len, resp_len);
- if (cmd_len > len || resp_len > len)
+ if (len == 0 || PAGE_ALIGN(len) < len)
return -EINVAL;
cmd = kzalloc(PAGE_ALIGN(len), GFP_KERNEL);