aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/rpmsg/omaprpc/omap_rpc_ion.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/rpmsg/omaprpc/omap_rpc_ion.c')
-rw-r--r--drivers/rpmsg/omaprpc/omap_rpc_ion.c382
1 files changed, 382 insertions, 0 deletions
diff --git a/drivers/rpmsg/omaprpc/omap_rpc_ion.c b/drivers/rpmsg/omaprpc/omap_rpc_ion.c
new file mode 100644
index 0000000..6354a4a
--- /dev/null
+++ b/drivers/rpmsg/omaprpc/omap_rpc_ion.c
@@ -0,0 +1,382 @@
+/*
+ * OMAP Remote Procedure Call Driver.
+ *
+ * Copyright(c) 2012 Texas Instruments. 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 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "omap_rpc_internal.h"
+
+static uint8_t *omaprpc_map_parameter(struct omaprpc_instance_t *rpc,
+ struct omaprpc_param_t *param)
+{
+ uint32_t pri_offset = 0;
+ uint8_t *kva = NULL;
+ uint8_t *bkva = NULL;
+
+ /* calc any primary offset if present */
+ pri_offset = param->data - param->base;
+
+ bkva = (uint8_t *) ion_map_kernel(rpc->ion_client,
+ (struct ion_handle *)param->reserved);
+
+ /*
+ * set the kernel VA equal to the base kernel
+ * VA plus the primary offset
+ */
+ kva = &bkva[pri_offset];
+
+ /*
+ * in ION case, secondary offset is ignored here
+ * because the entire region is mapped.
+ */
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO, rpc->rpcserv->dev,
+ "Mapped UVA:%p to KVA:%p+OFF:%08x SIZE:%08x "
+ "(MKVA:%p to END:%p)\n",
+ (void *)param->data,
+ (void *)kva, pri_offset, param->size,
+ (void *)bkva, (void *)&bkva[param->size]);
+
+ return kva;
+
+}
+
+static void omaprpc_unmap_parameter(struct omaprpc_instance_t *rpc,
+ struct omaprpc_param_t *param,
+ uint8_t *ptr, uint32_t sec_offset)
+{
+ ion_unmap_kernel(rpc->ion_client, (struct ion_handle *)param->reserved);
+}
+
+phys_addr_t omaprpc_buffer_lookup(struct omaprpc_instance_t *rpc,
+ uint32_t core, virt_addr_t uva,
+ virt_addr_t buva, void *reserved)
+{
+ phys_addr_t lpa = 0, rpa = 0;
+ /* User VA - Base User VA = User Offset assuming not tiler 2D */
+ /* For Tiler2D offset is corrected later */
+ long uoff = uva - buva;
+
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO, rpc->rpcserv->dev,
+ "CORE=%u BUVA=%p UVA=%p Uoff=%ld [0x%016lx] Hdl=%p\n",
+ core, (void *)buva, (void *)uva, uoff, (ulong) uoff,
+ reserved);
+
+ if (uoff < 0) {
+ OMAPRPC_ERR(rpc->rpcserv->dev,
+ "Offsets calculation for BUVA=%p from UVA=%p is a "
+ "negative number. Bad parameters!\n",
+ (void *)buva, (void *)uva);
+ rpa = 0;
+ goto to_end;
+ }
+
+ if (reserved) {
+ struct ion_handle *handle;
+ ion_phys_addr_t paddr;
+ size_t len;
+
+ /* is it an ion handle? */
+ handle = (struct ion_handle *)reserved;
+ if (!ion_phys(rpc->ion_client, handle, &paddr, &len)) {
+ lpa = (phys_addr_t) paddr;
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO, rpc->rpcserv->dev,
+ "Handle %p is an ION Handle to ARM PA %p "
+ "(Uoff=%ld) (len=%zu)\n", reserved, (void *)lpa,
+ uoff, len);
+ if ((long)len < uoff) {
+ /* if the offset is larger than the length,
+ * there is a potential security issue.
+ */
+ pr_err("Offset %ld too large, must be < %zu\n",
+ uoff, len);
+ rpa = 0;
+ goto to_end;
+ }
+ /* recalculate for 2d strides */
+ uoff = omaprpc_recalc_off(lpa, uoff);
+ lpa += uoff;
+ goto to_va;
+ } else {
+ /* is it an pvr buffer wrapping an ion handle? */
+
+ /*
+ * TODO: need to support 2 ion handles
+ * per 1 pvr handle (NV12 case)
+ */
+ struct ion_buffer *ion_buffer = NULL;
+ int num_handles = 1;
+ handle = NULL;
+ if (omap_ion_share_fd_to_buffers((int)reserved,
+ &ion_buffer,
+ &num_handles) < 0) {
+ goto to_va;
+ }
+ if (ion_buffer) {
+ handle = ion_import(rpc->ion_client,
+ ion_buffer);
+ }
+ if (handle && !ion_phys(rpc->ion_client, handle,
+ &paddr, &len)) {
+ lpa = (phys_addr_t) paddr;
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO,
+ rpc->rpcserv->dev,
+ "FD %d is an PVR Handle to ARM PA %p "
+ "(Uoff=%ld)\n", (int)reserved,
+ (void *)lpa, uoff);
+ if ((long)len < uoff) {
+ /* if the offset is larger than the
+ * length there is a security issue.
+ */
+ pr_err(
+ "Offset %ld too large, must be < %zu\n",
+ uoff, len);
+ rpa = 0;
+ goto to_end;
+ }
+ uoff = omaprpc_recalc_off(lpa, uoff);
+ lpa += uoff;
+ goto to_va;
+ }
+ /*
+ * TODO: need to do some buffer tracking and call
+ * ion_free when it is no longer being used. this
+ * will make sure the buffer is not freed while
+ * we are still using it
+ */
+ if (handle)
+ ion_free(rpc->ion_client, handle);
+ }
+ }
+
+to_va:
+ /* convert the local physical address to remote physical address */
+ rpa = rpmsg_local_to_remote_pa(rpc, lpa);
+to_end:
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO, rpc->rpcserv->dev,
+ "ARM VA %p == ARM PA %p => REMOTE[%u] PA %p (RESV %p)\n",
+ (void *)uva, (void *)lpa, core, (void *)rpa, reserved);
+ return rpa;
+}
+
+int omaprpc_xlate_buffers(struct omaprpc_instance_t *rpc,
+ struct omaprpc_call_function_t *function,
+ int direction)
+{
+ int idx = 0, start = 0, inc = 1, limit = 0, ret = 0;
+ uint32_t ptr_idx = 0, offset = 0, size = 0;
+ /* not all the parameters are pointers so this may be sparse */
+ uint8_t *base_ptrs[OMAPRPC_MAX_PARAMETERS];
+
+ if (function->num_translations == 0)
+ return 0;
+
+ limit = function->num_translations;
+ memset(base_ptrs, 0, sizeof(base_ptrs));
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO, rpc->rpcserv->dev,
+ "Operating on %d pointers\n", function->num_translations);
+ /*
+ * we may have a failure during translation, in which case we
+ * need to unwind the whole operation from here
+ */
+restart:
+ for (idx = start; idx != limit; idx += inc) {
+ /* conveinence variables */
+ ptr_idx = function->translations[idx].index;
+ offset = function->translations[idx].offset;
+
+ /* if the pointer index for this translation is invalid */
+ if (ptr_idx >= OMAPRPC_MAX_PARAMETERS) {
+ OMAPRPC_ERR(rpc->rpcserv->dev,
+ "Invalid parameter pointer index %u\n",
+ ptr_idx);
+ goto unwind;
+ } else if (function->params[ptr_idx].type !=
+ OMAPRPC_PARAM_TYPE_PTR) {
+ OMAPRPC_ERR(rpc->rpcserv->dev,
+ "Parameter index %u is not a pointer (type %u)"
+ "\n", ptr_idx,
+ function->params[ptr_idx].type);
+ goto unwind;
+ }
+
+ size = function->params[ptr_idx].size;
+
+ if (offset >= (size - sizeof(virt_addr_t))) {
+ OMAPRPC_ERR(rpc->rpcserv->dev,
+ "Offset is larger than data area! "
+ "(offset=%u size=%u)\n", offset, size);
+ goto unwind;
+ }
+
+ if (function->params[ptr_idx].data == 0) {
+ OMAPRPC_ERR(rpc->rpcserv->dev,
+ "Supplied user pointer is NULL!\n");
+ goto unwind;
+ }
+
+ /* if the KVA pointer has not been mapped */
+ if (base_ptrs[ptr_idx] == NULL) {
+ /*
+ * map the UVA pointer to KVA space, the offset could
+ * potentially be modified due to the mapping
+ */
+ base_ptrs[ptr_idx] = omaprpc_map_parameter(rpc,
+ &function->
+ params
+ [ptr_idx]);
+ }
+
+ /* if the KVA pointer is not NULL */
+ if (base_ptrs[ptr_idx] != NULL) {
+ if (direction == OMAPRPC_UVA_TO_RPA) {
+ /*
+ * get the kernel virtual pointer to
+ * the pointer to translate
+ */
+ virt_addr_t kva = (virt_addr_t)
+ &((base_ptrs[ptr_idx])[offset]);
+ virt_addr_t uva = 0;
+ virt_addr_t buva = (virt_addr_t)
+ function->translations[idx].base;
+ phys_addr_t rpa = 0;
+ void *reserved = (void *)
+ function->translations[idx].reserved;
+
+ /*
+ * make sure we won't cause
+ * an unalign mem access
+ */
+ if ((kva & 0x3) > 0) {
+ OMAPRPC_ERR(rpc->rpcserv->dev,
+ "ERROR: KVA %p is unaligned!\n",
+ (void *)kva);
+ return -EADDRNOTAVAIL;
+ }
+ /* load the user's VA */
+ uva = *(virt_addr_t *) kva;
+
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO,
+ rpc->rpcserv->dev,
+ "Replacing UVA %p at KVA %p PTRIDX:%u "
+ "OFFSET:%u IDX:%d\n",
+ (void *)uva, (void *)kva, ptr_idx,
+ offset, idx);
+
+ /* calc the new RPA (remote physical address) */
+ rpa = omaprpc_buffer_lookup(rpc, rpc->core,
+ uva, buva,
+ reserved);
+ /* save the old value */
+ function->translations[idx].reserved = uva;
+ /* replace with new RPA */
+ *(phys_addr_t *) kva = rpa;
+
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO,
+ rpc->rpcserv->dev,
+ "Replaced UVA %p with RPA %p at KVA %p\n",
+ (void *)uva, (void *)rpa,
+ (void *)kva);
+
+ if (rpa == 0) {
+ /* need to unwind all operations.. */
+ direction = OMAPRPC_RPA_TO_UVA;
+ start = idx - 1;
+ inc = -1;
+ limit = -1;
+ ret = -ENODATA;
+ goto restart;
+ }
+ } else if (direction == OMAPRPC_RPA_TO_UVA) {
+ /* address of the pointer in memory */
+ virt_addr_t kva = (virt_addr_t) &
+ ((base_ptrs[ptr_idx])[offset]);
+ virt_addr_t uva = 0;
+ phys_addr_t rpa = 0;
+ /*
+ * make sure we won't cause
+ * an unalign mem access
+ */
+ if ((kva & 0x3) > 0)
+ return -EADDRNOTAVAIL;
+ /* get what was there for debugging */
+ rpa = *(phys_addr_t *) kva;
+ /* convienence value of uva */
+ uva = (virt_addr_t)
+ function->translations[idx].reserved;
+ /*
+ * replace the translated value
+ * with the remember version
+ */
+ *(virt_addr_t *) kva = uva;
+
+ OMAPRPC_PRINT(OMAPRPC_ZONE_INFO,
+ rpc->rpcserv->dev,
+ "Replaced RPA %p with UVA %p at KVA %p\n",
+ (void *)rpa, (void *)uva,
+ (void *)kva);
+
+ if (uva == 0) {
+ /* need to unwind all operations.. */
+ direction = OMAPRPC_RPA_TO_UVA;
+ start = idx - 1;
+ inc = -1;
+ limit = -1;
+ ret = -ENODATA;
+ goto restart;
+ }
+ }
+ } else {
+ OMAPRPC_ERR(rpc->rpcserv->dev,
+ "Failed to map UVA to KVA to do translation!"
+ "\n");
+ /*
+ * we can arrive here from multiple points,
+ * but the action is the same from everywhere
+ */
+unwind:
+ if (direction == OMAPRPC_UVA_TO_RPA) {
+ /*
+ * we've encountered an error which
+ * needs to unwind all the operations
+ */
+ OMAPRPC_ERR(rpc->rpcserv->dev,
+ "Unwinding UVA to RPA translations!\n");
+ direction = OMAPRPC_RPA_TO_UVA;
+ start = idx - 1;
+ inc = -1;
+ limit = -1;
+ ret = -ENOBUFS;
+ goto restart;
+ } else if (direction == OMAPRPC_RPA_TO_UVA) {
+ /*
+ *there was a problem restoring the pointer,
+ * there's nothing to do but to continue
+ * processing
+ */
+ continue;
+ }
+ }
+ }
+ /* unmap all the pointers that were mapped and not freed yet */
+ for (idx = 0; idx < OMAPRPC_MAX_PARAMETERS; idx++) {
+ if (base_ptrs[idx]) {
+ omaprpc_unmap_parameter(rpc,
+ &function->params[idx],
+ base_ptrs[idx], 0);
+ base_ptrs[idx] = NULL;
+ }
+ }
+ return ret;
+}