summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorAmir Samuelov <amirs@codeaurora.org>2016-03-23 14:40:31 +0200
committerJeevan Shriram <jshriram@codeaurora.org>2016-04-22 11:57:49 -0700
commit1071415ec45bbb04016b8da428d4d39befa6e813 (patch)
tree67bfad27b813a59cfcd2b816d14d42649e07fa7f /drivers
parent6a50fbf32d217738e90640df3dfbb515f2571402 (diff)
spcom: add ION buffer support
Add new spcom API to allow user to send request/response with ION buffer. The spcom kernel driver modify the request/response by replacing the user ION buffer virtual address with the buffer physical address. spcom also provide API to lock/unlock the ION buffer to avoid the SP side using an ION buffer that was free when HLOS App crashed. CRs-Fixed: 986215 Change-Id: I5dca692cd3c4fac63be5ec73f89e6c39a593a50b Signed-off-by: Amir Samuelov <amirs@codeaurora.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/soc/qcom/spcom.c482
1 files changed, 227 insertions, 255 deletions
diff --git a/drivers/soc/qcom/spcom.c b/drivers/soc/qcom/spcom.c
index a1180966b674..a5e17991f122 100644
--- a/drivers/soc/qcom/spcom.c
+++ b/drivers/soc/qcom/spcom.c
@@ -74,6 +74,7 @@
#include <linux/of.h> /* of_property_count_strings() */
#include <linux/workqueue.h>
#include <linux/delay.h> /* msleep() */
+#include <linux/msm_ion.h> /* msm_ion_client_create() */
#include <soc/qcom/glink.h>
#include <soc/qcom/smem.h>
@@ -153,45 +154,6 @@ struct spcom_msg_hdr {
} __packed;
/**
- * struct spcom_load_app_req - Load App Request sent to SP
- *
- * The application image binary is placed on DDR buffer.
- * The application image is encrypted and signed.
- * The DDR buffer address and size should be 4K aligned for XPU protection.
- * The size of the app DDR buffer is larger than the image size to allow saving
- * the app heap and stack on swap in/out.
- */
-struct spcom_load_app_req {
- uint32_t cmd_id; /* SPCOM_CMD_LOAD_APP */
- uint32_t image_size;
- uint64_t buf_phys_addr;
- char ch_name[16];
- uint32_t buf_size;
-} __packed;
-
-/**
- * struct spcom_load_app_resp - Load App Response from SP.
- */
-struct spcom_load_app_resp {
- uint32_t error_code; /* 0 for success, errors based on errno.h */
-} __packed;
-
-/**
- * struct spcom_reset_cmd_req - Reset Request sent to SP
- */
-struct spcom_reset_cmd_req {
- uint32_t cmd_id;
-} __packed;
-
-/**
- * struct spcom_reset_resp - Reset Response from SP.
- */
-struct spcom_reset_resp {
- uint32_t error_code; /* 0 for success, errors based on errno.h */
-} __packed;
-
-
-/**
* struct spcom_client - Client handle
*/
struct spcom_client {
@@ -251,6 +213,10 @@ struct spcom_channel {
bool rx_buf_ready;
int actual_rx_size; /* actual data size received */
const void *glink_rx_buf;
+
+ /* ION lock/unlock support */
+ int ion_fd_table[SPCOM_MAX_ION_BUF];
+ struct ion_handle *ion_handle_table[SPCOM_MAX_ION_BUF];
};
/**
@@ -275,6 +241,9 @@ struct spcom_device {
/* Link state */
struct completion link_state_changed;
enum glink_link_state link_state;
+
+ /* ION support */
+ struct ion_client *ion_client;
};
#ifdef SPCOM_TEST_HLOS_WITH_MODEM
@@ -1290,168 +1259,6 @@ EXPORT_SYMBOL(spcom_server_send_response);
/*======================================================================*/
/**
- * spcom_handle_load_app_command() - Handle Load app command from user space.
- *
- * @cmd_buf: command buffer.
- * @cmd_size: command buffer size.
- *
- * Return: 0 on successful operation, negative value otherwise.
- */
-static int spcom_handle_load_app_command(struct spcom_channel *ch,
- void *cmd_buf,
- int cmd_size)
-{
- int ret = 0;
- struct spcom_msg_hdr *hdr;
- struct spcom_load_app_req *req;
- struct spcom_load_app_resp *resp;
- uint32_t rx_timeout_msec = 0; /* Block until data ready */
- void *tx_buf;
- int tx_buf_size;
- void *rx_buf;
- int rx_buf_size;
- struct spcom_user_load_app_command *cmd = cmd_buf;
- const char *ch_name;
- uint32_t app_buf_size;
- uint32_t app_image_size;
- char *app_buf;
- int ddr_buf_size = 0;
- char *ddr_buf = NULL;
- uint64_t phys_addr = 0;
- dma_addr_t dma_addr = 0;
- uint32_t txn_id = 0;
- uint32_t offset = 0;
-
- /* parse command buffer */
- ch_name = cmd->ch_name;
- app_image_size = cmd->app_image_size;
- app_buf_size = cmd->app_buf_size;
- app_buf = cmd->app_buf_ptr;
-
- pr_debug("Load app [%s], app_image_size [%d].\n",
- ch_name, app_image_size);
-
- if (cmd_size != sizeof(*cmd)) {
- pr_err("Load app cmd size [%d] expected size [%d].\n",
- cmd_size, (int) sizeof(*cmd));
- return -EINVAL;
- }
-
- if (app_buf_size > SPCOM_MAX_APP_SIZE) {
- pr_err("app_buf_size [%d] > max size [%d].\n",
- app_buf_size, SPCOM_MAX_APP_SIZE);
- return -EINVAL;
- }
-
- /* Check if remote side connect */
- if (!spcom_is_channel_connected(ch)) {
- pr_err("ch [%s] remote side not connect.\n", ch->name);
- return -ENOTCONN;
- }
-
- /* Allocate Buffers*/
- tx_buf_size = sizeof(*hdr) + sizeof(*req);
- rx_buf_size = sizeof(*hdr) + sizeof(*resp);
- tx_buf = kzalloc(tx_buf_size, GFP_KERNEL);
- if (!tx_buf)
- return -ENOMEM;
- rx_buf = kzalloc(rx_buf_size, GFP_KERNEL);
- if (!rx_buf) {
- kfree(tx_buf);
- return -ENOMEM;
- }
-
- /* Allocate DDR buffer for the App binary */
- ddr_buf_size = round_up(app_buf_size, PAGE_SIZE);
- ddr_buf = dma_alloc_coherent(spcom_dev->class_dev,
- ddr_buf_size,
- &dma_addr,
- GFP_KERNEL);
- if (!ddr_buf) {
- pr_err("fail to allocate DDR buffer.\n");
- return -ENOMEM;
- }
-
- phys_addr = dma_to_phys(spcom_dev->class_dev, dma_addr);
-
- /* Align DDR buf to 4K, for SPSS XPU */
- offset = ((int) phys_addr) % PAGE_SIZE;
- if (offset != 0) {
- ddr_buf += (PAGE_SIZE - offset); /* round up*/
- phys_addr += (PAGE_SIZE - offset); /* round up*/
- }
-
- memcpy(ddr_buf, app_buf, app_image_size);
-
- /* Prepare Tx Buf */
- hdr = tx_buf;
- req = (void *) &hdr->buf[0];
-
- /* Header */
- txn_id = ch->txn_id++;
- hdr->txn_id = txn_id;
-
- /* Request */
- req->cmd_id = SPCOM_CMD_LOAD_APP;
- req->buf_phys_addr = phys_addr;
- req->image_size = app_image_size;
- req->buf_size = app_buf_size;
- strlcpy(req->ch_name, cmd->ch_name, sizeof(req->ch_name));
-
- pr_debug("send request.\n");
-
- ret = spcom_tx(ch, tx_buf, tx_buf_size, TX_DONE_TIMEOUT_MSEC);
- if (ret < 0) {
- pr_err("tx error %d.\n", ret);
- goto exit_err;
- }
-
- pr_debug("get response.\n");
-
- ret = spcom_rx(ch, rx_buf, rx_buf_size, rx_timeout_msec);
- if (ret < 0) {
- pr_err("rx error %d.\n", ret);
- goto exit_err;
- }
-
- /* check response from SP */
- hdr = rx_buf;
- resp = (void *) &hdr->buf[0];
-
- if (resp->error_code != 0) {
- pr_err("response error code [%d].\n", (int) resp->error_code);
- goto exit_err;
- }
- pr_debug("rx txn_id = 0x%x .\n", hdr->txn_id);
- if (hdr->txn_id != txn_id) {
- pr_err("request txn_id [0x%x] != response txn_id [0x%x].\n",
- (int) txn_id, (int) hdr->txn_id);
- goto exit_err;
- }
-
- kfree(tx_buf);
- kfree(rx_buf);
-
- /* HACK !! Don't free DDR buf, required by SPSS for swap in/out */
-
- pr_debug("Load app completed OK.\n");
-
- /* After remote app is loaded, create /dev/<ch_name> */
- spcom_create_channel_chardev(ch_name);
-
- return 0;
-exit_err:
- kfree(tx_buf);
- kfree(rx_buf);
-
- memset(ddr_buf, 0xDEADBEEF, ddr_buf_size);
- dmam_free_coherent(spcom_dev->class_dev, ddr_buf_size,
- ddr_buf, dma_addr);
-
- return -EFAULT;
-}
-
-/**
* spcom_handle_create_channel_command() - Handle Create Channel command from
* user space.
*
@@ -1482,25 +1289,26 @@ static int spcom_handle_create_channel_command(void *cmd_buf, int cmd_size)
}
/**
- * spcom_handle_reset_command() - Handle RESET-SP command from user space.
+ * spcom_handle_send_command() - Handle send request/response from user space.
*
* @buf: command buffer.
* @buf_size: command buffer size.
*
* Return: 0 on successful operation, negative value otherwise.
*/
-static int spcom_handle_reset_command(struct spcom_channel *ch,
- void *cmd_buf, int cmd_size)
+static int spcom_handle_send_command(struct spcom_channel *ch,
+ void *cmd_buf, int size)
{
int ret = 0;
+ struct spcom_send_command *cmd = cmd_buf;
+ uint32_t buf_size;
+ void *buf;
struct spcom_msg_hdr *hdr;
- struct spcom_reset_cmd_req *req;
- struct spcom_reset_resp *resp;
- uint32_t rx_timeout_msec = 0; /* Block until data ready */
void *tx_buf;
int tx_buf_size;
- void *rx_buf;
- int rx_buf_size;
+ uint32_t timeout_msec;
+
+ pr_debug("send req/resp ch [%s] size [%d] .\n", ch->name, size);
/* Check if remote side connect */
if (!spcom_is_channel_connected(ch)) {
@@ -1508,82 +1316,131 @@ static int spcom_handle_reset_command(struct spcom_channel *ch,
return -ENOTCONN;
}
+ /* parse command buffer */
+ buf = &cmd->buf;
+ buf_size = cmd->buf_size;
+ timeout_msec = cmd->timeout_msec;
+
/* Allocate Buffers*/
- tx_buf_size = sizeof(*hdr) + sizeof(*req);
- rx_buf_size = sizeof(*hdr) + sizeof(*resp);
+ tx_buf_size = sizeof(*hdr) + buf_size;
tx_buf = kzalloc(tx_buf_size, GFP_KERNEL);
if (!tx_buf)
return -ENOMEM;
- rx_buf = kzalloc(rx_buf_size, GFP_KERNEL);
- if (!rx_buf) {
- kfree(tx_buf);
- return -ENOMEM;
- }
/* Prepare Tx Buf */
hdr = tx_buf;
- req = (void *) &hdr->buf[0];
/* Header */
- hdr->txn_id = ch->txn_id++;
-
- /* Request */
- req->cmd_id = SPCOM_CMD_RESET_SP;
+ hdr->txn_id = ch->txn_id;
+ if (!ch->is_server) {
+ ch->txn_id++; /* client sets the request txn_id */
+ ch->response_timeout_msec = timeout_msec;
+ }
- pr_debug("send request.\n");
+ /* user buf */
+ memcpy(hdr->buf, buf, buf_size);
+ /*
+ * remote side should have rx buffer ready.
+ * tx_done is expected to be received quickly.
+ */
ret = spcom_tx(ch, tx_buf, tx_buf_size, TX_DONE_TIMEOUT_MSEC);
- if (ret < 0) {
+ if (ret < 0)
pr_err("tx error %d.\n", ret);
- goto exit_err;
- }
- pr_debug("get response.\n");
+ kfree(tx_buf);
- ret = spcom_rx(ch, rx_buf, rx_buf_size, rx_timeout_msec);
- if (ret < 0) {
- pr_err("rx error %d.\n", ret);
- goto exit_err;
+ return ret;
+}
+
+/**
+ * modify_ion_addr() - replace the ION buffer virtual address with physical
+ * address in a request or response buffer.
+ *
+ * @buf: buffer to modify
+ * @buf_size: buffer size
+ * @ion_info: ION buffer info such as FD and offset in buffer.
+ *
+ * Return: 0 on successful operation, negative value otherwise.
+ */
+static int modify_ion_addr(void *buf,
+ uint32_t buf_size,
+ struct spcom_ion_info ion_info)
+{
+ struct ion_handle *handle = NULL;
+ ion_phys_addr_t ion_phys_addr;
+ size_t len;
+ int fd;
+ uint32_t buf_offset;
+ uint64_t *addr;
+ int ret;
+
+ fd = ion_info.fd;
+ buf_offset = ion_info.buf_offset;
+
+ if (fd < 0) {
+ pr_err("invalid fd [%d].\n", fd);
+ return -ENODEV;
}
- /* check response from SP */
- hdr = rx_buf;
- resp = (void *) &hdr->buf[0];
+ if (buf_offset > buf_size - sizeof(uint64_t)) {
+ pr_err("invalid buf_offset [%d].\n", buf_offset);
+ return -ENODEV;
+ }
- if (resp->error_code != 0) {
- pr_err("response error code [%d].\n", (int) resp->error_code);
- goto exit_err;
+ /* Get ION handle from fd */
+ handle = ion_import_dma_buf(spcom_dev->ion_client, fd);
+ if (handle == NULL) {
+ pr_err("fail to get ion handle.\n");
+ return -EINVAL;
}
+ pr_debug("ion handle ok.\n");
- kfree(tx_buf);
- kfree(rx_buf);
+ /* Get the ION buffer Physical Address */
+ ret = ion_phys(spcom_dev->ion_client, handle, &ion_phys_addr, &len);
+ if (ret < 0) {
+ pr_err("fail to get ion phys addr.\n");
+ ion_free(spcom_dev->ion_client, handle);
+ return -EINVAL;
+ }
+ pr_debug("buf_offset [%d].\n", buf_offset);
+ addr = (uint64_t *) ((char *) buf + buf_offset);
+
+ /* Replace the user ION Virtual Address with the Physical Address */
+ pr_debug("ion user vaddr = [0x%lx].\n", (long int) *addr);
+ *addr = (uint64_t) ion_phys_addr;
+ pr_debug("ion phys addr = [0x%lx].\n", (long int) *addr);
+
+ /* Release the ION handle */
+ ion_free(spcom_dev->ion_client, handle);
return 0;
-exit_err:
- kfree(tx_buf);
- kfree(rx_buf);
- return -EFAULT;
}
/**
- * spcom_handle_send_command() - Handle send request/response from user space.
+ * spcom_handle_send_modified_command() - send a request/response with ION
+ * buffer address. Modify the request/response by replacing the ION buffer
+ * virtual address with the physical address.
*
- * @buf: command buffer.
- * @buf_size: command buffer size.
+ * @ch: channel pointer
+ * @cmd_buf: User space command buffer
+ * @size: size of user command buffer
*
* Return: 0 on successful operation, negative value otherwise.
*/
-static int spcom_handle_send_command(struct spcom_channel *ch,
- void *cmd_buf, int size)
+static int spcom_handle_send_modified_command(struct spcom_channel *ch,
+ void *cmd_buf, int size)
{
int ret = 0;
- struct spcom_send_command *cmd = cmd_buf;
+ struct spcom_user_send_modified_command *cmd = cmd_buf;
uint32_t buf_size;
void *buf;
struct spcom_msg_hdr *hdr;
void *tx_buf;
int tx_buf_size;
uint32_t timeout_msec;
+ struct spcom_ion_info ion_info[SPCOM_MAX_ION_BUF];
+ int i;
pr_debug("send req/resp ch [%s] size [%d] .\n", ch->name, size);
@@ -1597,6 +1454,7 @@ static int spcom_handle_send_command(struct spcom_channel *ch,
buf = &cmd->buf;
buf_size = cmd->buf_size;
timeout_msec = cmd->timeout_msec;
+ memcpy(ion_info, cmd->ion_info, sizeof(ion_info));
/* Allocate Buffers*/
tx_buf_size = sizeof(*hdr) + buf_size;
@@ -1617,6 +1475,17 @@ static int spcom_handle_send_command(struct spcom_channel *ch,
/* user buf */
memcpy(hdr->buf, buf, buf_size);
+ for (i = 0 ; i < SPCOM_MAX_ION_BUF ; i++) {
+ if (ion_info[i].fd >= 0) {
+ ret = modify_ion_addr(hdr->buf, buf_size, ion_info[i]);
+ if (ret < 0) {
+ pr_err("modify_ion_addr() error [%d].\n", ret);
+ kfree(tx_buf);
+ return -EFAULT;
+ }
+ }
+ }
+
/*
* remote side should have rx buffer ready.
* tx_done is expected to be received quickly.
@@ -1630,6 +1499,100 @@ static int spcom_handle_send_command(struct spcom_channel *ch,
return ret;
}
+
+/**
+ * spcom_handle_lock_ion_buf_command() - Lock an ION buffer.
+ *
+ * Lock an ION buffer, prevent it from being free if the user space App crash,
+ * while it is used by the remote subsystem.
+ */
+static int spcom_handle_lock_ion_buf_command(struct spcom_channel *ch,
+ void *cmd_buf, int size)
+{
+ struct spcom_user_command *cmd = cmd_buf;
+ int fd = cmd->arg;
+ struct ion_handle *ion_handle;
+ int i;
+
+ /* Check ION client */
+ if (spcom_dev->ion_client == NULL) {
+ pr_err("invalid ion client.\n");
+ return -ENODEV;
+ }
+
+ /* Check if this FD is already locked */
+ for (i = 0 ; i < SPCOM_MAX_ION_BUF ; i++)
+ if (ch->ion_fd_table[i] == fd) {
+ pr_debug("fd [%d] is already locked.\n", fd);
+ return -EINVAL;
+ }
+
+ /* Get ION handle from fd - this increments the ref count */
+ ion_handle = ion_import_dma_buf(spcom_dev->ion_client, fd);
+ if (ion_handle == NULL) {
+ pr_err("fail to get ion handle.\n");
+ return -EINVAL;
+ }
+ pr_debug("ion handle ok.\n");
+
+ for (i = 0 ; i < SPCOM_MAX_ION_BUF ; i++) {
+ if (ch->ion_handle_table[i] == NULL) {
+ ch->ion_handle_table[i] = ion_handle;
+ ch->ion_fd_table[i] = fd;
+ pr_debug("locked ion buf#[%d], fd [%d].\n", i, fd);
+ return 0;
+ }
+ }
+
+ return -EFAULT;
+}
+
+/**
+ * spcom_handle_unlock_ion_buf_command() - Unlock an ION buffer.
+ *
+ * Unlock an ION buffer, let it be free, when it is no longer being used by
+ * the remote subsystem.
+ */
+static int spcom_handle_unlock_ion_buf_command(struct spcom_channel *ch,
+ void *cmd_buf, int size)
+{
+ struct spcom_user_command *cmd = cmd_buf;
+ int fd = cmd->arg;
+ struct ion_client *ion_client = spcom_dev->ion_client;
+ int i;
+
+ /* Check ION client */
+ if (ion_client == NULL) {
+ pr_err("fail to create ion client.\n");
+ return -ENODEV;
+ }
+
+ if (fd == (int) SPCOM_ION_FD_UNLOCK_ALL) {
+ /* unlock all ION buf */
+ for (i = 0 ; i < SPCOM_MAX_ION_BUF ; i++) {
+ if (ch->ion_handle_table[i] != NULL) {
+ ion_free(ion_client, ch->ion_handle_table[i]);
+ ch->ion_handle_table[i] = NULL;
+ ch->ion_fd_table[i] = -1;
+ pr_debug("unlocked ion buf#[%d].\n", i);
+ }
+ }
+ } else {
+ /* unlock specific ION buf */
+ for (i = 0 ; i < SPCOM_MAX_ION_BUF ; i++) {
+ if (ch->ion_fd_table[i] == fd) {
+ ion_free(ion_client, ch->ion_handle_table[i]);
+ ch->ion_handle_table[i] = NULL;
+ ch->ion_fd_table[i] = -1;
+ pr_debug("unlocked ion buf#[%d].\n", i);
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
/**
* spcom_handle_fake_ssr_command() - Handle fake ssr command from user space.
*/
@@ -1669,20 +1632,23 @@ static int spcom_handle_write(struct spcom_channel *ch,
cmd = (struct spcom_user_command *)buf;
cmd_id = (int) cmd->cmd_id;
swap_id = htonl(cmd->cmd_id);
- memcpy(cmd_name, &swap_id, 4);
+ memcpy(cmd_name, &swap_id, sizeof(int));
pr_debug("cmd_id [0x%x] cmd_name [%s].\n", cmd_id, cmd_name);
switch (cmd_id) {
- case SPCOM_CMD_LOAD_APP:
- ret = spcom_handle_load_app_command(ch, buf, buf_size);
- break;
- case SPCOM_CMD_RESET_SP:
- ret = spcom_handle_reset_command(ch, buf, buf_size);
- break;
case SPCOM_CMD_SEND:
ret = spcom_handle_send_command(ch, buf, buf_size);
break;
+ case SPCOM_CMD_SEND_MODIFIED:
+ ret = spcom_handle_send_modified_command(ch, buf, buf_size);
+ break;
+ case SPCOM_CMD_LOCK_ION_BUF:
+ ret = spcom_handle_lock_ion_buf_command(ch, buf, buf_size);
+ break;
+ case SPCOM_CMD_UNLOCK_ION_BUF:
+ ret = spcom_handle_unlock_ion_buf_command(ch, buf, buf_size);
+ break;
case SPCOM_CMD_FSSR:
ret = spcom_handle_fake_ssr_command(ch, cmd->arg);
break;
@@ -2374,6 +2340,12 @@ static int spcom_probe(struct platform_device *pdev)
goto fail_reg_chardev;
}
+ spcom_dev->ion_client = msm_ion_client_create(DEVICE_NAME);
+ if (spcom_dev->ion_client == NULL) {
+ pr_err("fail to create ion client.\n");
+ goto fail_reg_chardev;
+ }
+
pr_info("Driver Initialization ok.\n");
return 0;