Merge "msm: mink: Adding SMCInvoke Driver"
This commit is contained in:
commit
07dde62a73
7 changed files with 660 additions and 0 deletions
56
Documentation/misc-devices/qcom_invoke_driver.txt
Normal file
56
Documentation/misc-devices/qcom_invoke_driver.txt
Normal file
|
@ -0,0 +1,56 @@
|
|||
Introduction:
|
||||
=============
|
||||
Invoke driver is a misc driver which helps communication between non secure
|
||||
and secure world. Invoke driver communicates with secure side using SCM
|
||||
driver. To use invoke driver, open must be called on invoke device i.e.
|
||||
/dev/invoke. Invoke driver exposes only one IOCTL invoke which passes
|
||||
userspace request to TZ.
|
||||
|
||||
SW Architecture
|
||||
===============
|
||||
Following is SW stack for Invoke driver.
|
||||
|
||||
+++++++++++++++++++++++++++++++++++++++++
|
||||
+ Applications +
|
||||
+++++++++++++++++++++++++++++++++++++++++
|
||||
+ System Layer +
|
||||
+++++++++++++++++++++++++++++++++++++++++
|
||||
+ Kernel +
|
||||
+ +++++++++++++++++++ +
|
||||
+ + Invoke driver + +
|
||||
+ +++++++++++++++++++ +
|
||||
+ + SCM Driver + +
|
||||
+++++++++++++++++++++++++++++++++++++++++
|
||||
||
|
||||
||
|
||||
\/
|
||||
+++++++++++++++++++++++++++++++++++++++++
|
||||
+ Trust Zone +
|
||||
+ +++++++++++ +++++++++++ +
|
||||
+ + TZ App1 + + TZ App2 + +
|
||||
+++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
|
||||
Interfaces
|
||||
==========
|
||||
Invoke driver exposes INVOKE_IOCTL_INVOKE_REQ IOCTL for userspace to
|
||||
communicate with driver. More details of IOCTL are avilable in
|
||||
corresponding header file.
|
||||
|
||||
|
||||
Driver Parameters
|
||||
=================
|
||||
This driver is built and statically linked into the kernel; therefore,
|
||||
there are no module parameters supported by this driver.
|
||||
|
||||
There are no kernel command line parameters supported by this driver.
|
||||
|
||||
Power Management
|
||||
================
|
||||
TBD
|
||||
|
||||
Dependencies
|
||||
============
|
||||
Invoke driver depends on SCM driver to communicate with TZ.
|
||||
|
||||
|
|
@ -770,4 +770,10 @@ config WCD_DSP_GLINK
|
|||
between MSM and WCD DSP over glink transport protocol. This driver
|
||||
provides read and write interface via char device.
|
||||
|
||||
config QCOM_SMCINVOKE
|
||||
bool "Secure QSEE Support"
|
||||
help
|
||||
Enable SMCInvoke driver which supports capability based secure
|
||||
communication between QSEE and HLOS.
|
||||
|
||||
source "drivers/soc/qcom/memshare/Kconfig"
|
||||
|
|
|
@ -98,3 +98,4 @@ obj-$(CONFIG_MSM_RPM_STATS_LOG) += rpm_stats.o rpm_master_stat.o system_stats.o
|
|||
obj-$(CONFIG_MSM_RPM_LOG) += rpm_log.o
|
||||
obj-$(CONFIG_QSEE_IPC_IRQ_BRIDGE) += qsee_ipc_irq_bridge.o
|
||||
obj-$(CONFIG_WCD_DSP_GLINK) += wcd-dsp-glink.o
|
||||
obj-$(CONFIG_QCOM_SMCINVOKE) += smcinvoke.o
|
||||
|
|
500
drivers/soc/qcom/smcinvoke.c
Normal file
500
drivers/soc/qcom/smcinvoke.c
Normal file
|
@ -0,0 +1,500 @@
|
|||
/* Copyright (c) 2016, 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
|
||||
* only 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.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/fdtable.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/anon_inodes.h>
|
||||
#include <linux/smcinvoke.h>
|
||||
#include <soc/qcom/scm.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include "smcinvoke_object.h"
|
||||
|
||||
#define SMCINVOKE_TZ_PARAM_ID 0x224
|
||||
#define SMCINVOKE_TZ_CMD 0x32000600
|
||||
#define SMCINVOKE_FILE "smcinvoke"
|
||||
#define SMCINVOKE_TZ_ROOT_OBJ 1
|
||||
#define SMCINVOKE_TZ_MIN_BUF_SIZE 4096
|
||||
#define SMCINVOKE_ARGS_ALIGN_SIZE (sizeof(uint64_t))
|
||||
#define SMCINVOKE_TZ_OBJ_NULL 0
|
||||
|
||||
#define FOR_ARGS(ndxvar, counts, section) \
|
||||
for (ndxvar = object_counts_index_##section(counts); \
|
||||
ndxvar < (object_counts_index_##section(counts) \
|
||||
+ object_counts_num_##section(counts)); \
|
||||
++ndxvar)
|
||||
|
||||
static long smcinvoke_ioctl(struct file *, unsigned, unsigned long);
|
||||
static int smcinvoke_open(struct inode *, struct file *);
|
||||
static int smcinvoke_release(struct inode *, struct file *);
|
||||
|
||||
static const struct file_operations smcinvoke_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.unlocked_ioctl = smcinvoke_ioctl,
|
||||
.compat_ioctl = smcinvoke_ioctl,
|
||||
.open = smcinvoke_open,
|
||||
.release = smcinvoke_release,
|
||||
};
|
||||
|
||||
static struct miscdevice smcinvoke_miscdev = {
|
||||
.minor = MISC_DYNAMIC_MINOR,
|
||||
.name = "smcinvoke",
|
||||
.fops = &smcinvoke_fops
|
||||
};
|
||||
|
||||
struct smcinvoke_buf_hdr {
|
||||
uint32_t offset;
|
||||
uint32_t size;
|
||||
};
|
||||
|
||||
union smcinvoke_tz_args {
|
||||
struct smcinvoke_buf_hdr b;
|
||||
uint32_t tzhandle;
|
||||
};
|
||||
struct smcinvoke_msg_hdr {
|
||||
uint32_t tzhandle;
|
||||
uint32_t op;
|
||||
uint32_t counts;
|
||||
};
|
||||
|
||||
struct smcinvoke_tzobj_context {
|
||||
uint32_t tzhandle;
|
||||
};
|
||||
|
||||
/*
|
||||
* size_add saturates at SIZE_MAX. If integer overflow is detected,
|
||||
* this function would return SIZE_MAX otherwise normal a+b is returned.
|
||||
*/
|
||||
static inline size_t size_add(size_t a, size_t b)
|
||||
{
|
||||
return (b > (SIZE_MAX - a)) ? SIZE_MAX : a + b;
|
||||
}
|
||||
|
||||
/*
|
||||
* pad_size is used along with size_align to define a buffer overflow
|
||||
* protected version of ALIGN
|
||||
*/
|
||||
static inline size_t pad_size(size_t a, size_t b)
|
||||
{
|
||||
return (~a + 1) % b;
|
||||
}
|
||||
|
||||
/*
|
||||
* size_align saturates at SIZE_MAX. If integer overflow is detected, this
|
||||
* function would return SIZE_MAX otherwise next aligned size is returned.
|
||||
*/
|
||||
static inline size_t size_align(size_t a, size_t b)
|
||||
{
|
||||
return size_add(a, pad_size(a, b));
|
||||
}
|
||||
|
||||
/*
|
||||
* This function retrieves file pointer corresponding to FD provided. It stores
|
||||
* retrived file pointer until IOCTL call is concluded. Once call is completed,
|
||||
* all stored file pointers are released. file pointers are stored to prevent
|
||||
* other threads from releasing that FD while IOCTL is in progress.
|
||||
*/
|
||||
static int get_tzhandle_from_fd(int64_t fd, struct file **filp,
|
||||
uint32_t *tzhandle)
|
||||
{
|
||||
int ret = -EBADF;
|
||||
struct file *tmp_filp = NULL;
|
||||
struct smcinvoke_tzobj_context *tzobj = NULL;
|
||||
|
||||
if (fd == SMCINVOKE_USERSPACE_OBJ_NULL) {
|
||||
*tzhandle = SMCINVOKE_TZ_OBJ_NULL;
|
||||
ret = 0;
|
||||
goto out;
|
||||
} else if (fd < SMCINVOKE_USERSPACE_OBJ_NULL) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
tmp_filp = fget(fd);
|
||||
if (!tmp_filp)
|
||||
goto out;
|
||||
|
||||
/* Verify if filp is smcinvoke device's file pointer */
|
||||
if (!tmp_filp->f_op || !tmp_filp->private_data ||
|
||||
(tmp_filp->f_op != &smcinvoke_fops)) {
|
||||
fput(tmp_filp);
|
||||
goto out;
|
||||
}
|
||||
|
||||
tzobj = tmp_filp->private_data;
|
||||
*tzhandle = tzobj->tzhandle;
|
||||
*filp = tmp_filp;
|
||||
ret = 0;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int get_fd_from_tzhandle(uint32_t tzhandle, int64_t *fd)
|
||||
{
|
||||
int unused_fd = -1, ret = -1;
|
||||
struct file *f = NULL;
|
||||
struct smcinvoke_tzobj_context *cxt = NULL;
|
||||
|
||||
if (tzhandle == SMCINVOKE_TZ_OBJ_NULL) {
|
||||
*fd = SMCINVOKE_USERSPACE_OBJ_NULL;
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
cxt = kzalloc(sizeof(*cxt), GFP_KERNEL);
|
||||
if (!cxt) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
unused_fd = get_unused_fd_flags(O_RDWR);
|
||||
if (unused_fd < 0)
|
||||
goto out;
|
||||
|
||||
f = anon_inode_getfile(SMCINVOKE_FILE, &smcinvoke_fops, cxt, O_RDWR);
|
||||
if (IS_ERR(f))
|
||||
goto out;
|
||||
|
||||
*fd = unused_fd;
|
||||
fd_install(*fd, f);
|
||||
((struct smcinvoke_tzobj_context *)
|
||||
(f->private_data))->tzhandle = tzhandle;
|
||||
return 0;
|
||||
out:
|
||||
if (unused_fd >= 0)
|
||||
put_unused_fd(unused_fd);
|
||||
kfree(cxt);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int prepare_send_scm_msg(const uint8_t *in_buf, size_t in_buf_len,
|
||||
const uint8_t *out_buf, size_t out_buf_len,
|
||||
int32_t *smcinvoke_result)
|
||||
{
|
||||
int ret = 0;
|
||||
struct scm_desc desc = {0};
|
||||
size_t inbuf_flush_size = (1UL << get_order(in_buf_len)) * PAGE_SIZE;
|
||||
size_t outbuf_flush_size = (1UL << get_order(out_buf_len)) * PAGE_SIZE;
|
||||
|
||||
desc.arginfo = SMCINVOKE_TZ_PARAM_ID;
|
||||
desc.args[0] = (uint64_t)virt_to_phys(in_buf);
|
||||
desc.args[1] = in_buf_len;
|
||||
desc.args[2] = (uint64_t)virt_to_phys(out_buf);
|
||||
desc.args[3] = out_buf_len;
|
||||
|
||||
dmac_flush_range(in_buf, in_buf + inbuf_flush_size);
|
||||
dmac_flush_range(out_buf, out_buf + outbuf_flush_size);
|
||||
|
||||
ret = scm_call2(SMCINVOKE_TZ_CMD, &desc);
|
||||
*smcinvoke_result = (int32_t)desc.ret[1];
|
||||
if (ret || desc.ret[1] || desc.ret[2] || desc.ret[0]) {
|
||||
pr_err("SCM call failed with ret val = %d %d %d %d\n",
|
||||
ret, (int)desc.ret[0],
|
||||
(int)desc.ret[1], (int)desc.ret[2]);
|
||||
ret = ret | desc.ret[0] | desc.ret[1] | desc.ret[2];
|
||||
}
|
||||
dmac_inv_range(in_buf, in_buf + inbuf_flush_size);
|
||||
dmac_inv_range(out_buf, out_buf + outbuf_flush_size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int marshal_out(void *buf, uint32_t buf_size,
|
||||
struct smcinvoke_cmd_req *req,
|
||||
union smcinvoke_arg *args_buf)
|
||||
{
|
||||
int ret = -EINVAL, i = 0;
|
||||
union smcinvoke_tz_args *tz_args = NULL;
|
||||
size_t offset = sizeof(struct smcinvoke_msg_hdr) +
|
||||
object_counts_total(req->counts) *
|
||||
sizeof(union smcinvoke_tz_args);
|
||||
|
||||
if (offset > buf_size)
|
||||
goto out;
|
||||
|
||||
tz_args = (union smcinvoke_tz_args *)
|
||||
(buf + sizeof(struct smcinvoke_msg_hdr));
|
||||
|
||||
tz_args += object_counts_num_BI(req->counts);
|
||||
|
||||
FOR_ARGS(i, req->counts, BO) {
|
||||
args_buf[i].b.size = tz_args->b.size;
|
||||
if ((buf_size - tz_args->b.offset < tz_args->b.size) ||
|
||||
tz_args->b.offset > buf_size) {
|
||||
pr_err("%s: buffer overflow detected\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
if (copy_to_user((void __user *)(args_buf[i].b.addr),
|
||||
(uint8_t *)(buf) + tz_args->b.offset,
|
||||
tz_args->b.size)) {
|
||||
pr_err("Error %d copying ctxt to user\n", ret);
|
||||
goto out;
|
||||
}
|
||||
tz_args++;
|
||||
}
|
||||
tz_args += object_counts_num_OI(req->counts);
|
||||
|
||||
FOR_ARGS(i, req->counts, OO) {
|
||||
/*
|
||||
* create a new FD and assign to output object's
|
||||
* context
|
||||
*/
|
||||
ret = get_fd_from_tzhandle(tz_args->tzhandle,
|
||||
&(args_buf[i].o.fd));
|
||||
if (ret)
|
||||
goto out;
|
||||
tz_args++;
|
||||
}
|
||||
ret = 0;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* SMC expects arguments in following format
|
||||
* ---------------------------------------------------------------------------
|
||||
* | cxt | op | counts | ptr|size |ptr|size...|ORef|ORef|...| rest of payload |
|
||||
* ---------------------------------------------------------------------------
|
||||
* cxt: target, op: operation, counts: total arguments
|
||||
* offset: offset is from beginning of buffer i.e. cxt
|
||||
* size: size is 8 bytes aligned value
|
||||
*/
|
||||
static size_t compute_in_msg_size(const struct smcinvoke_cmd_req *req,
|
||||
const union smcinvoke_arg *args_buf)
|
||||
{
|
||||
uint32_t i = 0;
|
||||
|
||||
size_t total_size = sizeof(struct smcinvoke_msg_hdr) +
|
||||
object_counts_total(req->counts) *
|
||||
sizeof(union smcinvoke_tz_args);
|
||||
|
||||
/* Computed total_size should be 8 bytes aligned from start of buf */
|
||||
total_size = ALIGN(total_size, SMCINVOKE_ARGS_ALIGN_SIZE);
|
||||
|
||||
/* each buffer has to be 8 bytes aligned */
|
||||
while (i < object_counts_num_buffers(req->counts))
|
||||
total_size = size_add(total_size,
|
||||
size_align(args_buf[i++].b.size, SMCINVOKE_ARGS_ALIGN_SIZE));
|
||||
|
||||
/* Since we're using get_free_pages, no need for explicit PAGE align */
|
||||
return total_size;
|
||||
}
|
||||
|
||||
static int marshal_in(const struct smcinvoke_cmd_req *req,
|
||||
const union smcinvoke_arg *args_buf, uint32_t tzhandle,
|
||||
uint8_t *buf, size_t buf_size, struct file **arr_filp)
|
||||
{
|
||||
int ret = -EINVAL, i = 0;
|
||||
union smcinvoke_tz_args *tz_args = NULL;
|
||||
struct smcinvoke_msg_hdr msg_hdr = {tzhandle, req->op, req->counts};
|
||||
uint32_t offset = sizeof(struct smcinvoke_msg_hdr) +
|
||||
sizeof(union smcinvoke_tz_args) *
|
||||
object_counts_total(req->counts);
|
||||
|
||||
if (buf_size < offset)
|
||||
goto out;
|
||||
|
||||
*(struct smcinvoke_msg_hdr *)buf = msg_hdr;
|
||||
tz_args = (union smcinvoke_tz_args *)
|
||||
(buf + sizeof(struct smcinvoke_msg_hdr));
|
||||
|
||||
FOR_ARGS(i, req->counts, BI) {
|
||||
offset = size_align(offset, SMCINVOKE_ARGS_ALIGN_SIZE);
|
||||
if ((offset > buf_size) ||
|
||||
(args_buf[i].b.size > (buf_size - offset)))
|
||||
goto out;
|
||||
|
||||
tz_args->b.offset = offset;
|
||||
tz_args->b.size = args_buf[i].b.size;
|
||||
tz_args++;
|
||||
|
||||
if (copy_from_user(buf+offset,
|
||||
(void __user *)(args_buf[i].b.addr),
|
||||
args_buf[i].b.size))
|
||||
goto out;
|
||||
|
||||
offset += args_buf[i].b.size;
|
||||
}
|
||||
FOR_ARGS(i, req->counts, BO) {
|
||||
offset = size_align(offset, SMCINVOKE_ARGS_ALIGN_SIZE);
|
||||
if ((offset > buf_size) ||
|
||||
(args_buf[i].b.size > (buf_size - offset)))
|
||||
goto out;
|
||||
|
||||
tz_args->b.offset = offset;
|
||||
tz_args->b.size = args_buf[i].b.size;
|
||||
tz_args++;
|
||||
|
||||
offset += args_buf[i].b.size;
|
||||
}
|
||||
FOR_ARGS(i, req->counts, OI) {
|
||||
if (get_tzhandle_from_fd(args_buf[i].o.fd,
|
||||
&arr_filp[i], &(tz_args->tzhandle)))
|
||||
goto out;
|
||||
tz_args++;
|
||||
}
|
||||
ret = 0;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
long smcinvoke_ioctl(struct file *filp, unsigned cmd, unsigned long arg)
|
||||
{
|
||||
int ret = -1, i = 0, nr_args = 0;
|
||||
struct smcinvoke_cmd_req req = {0};
|
||||
void *in_msg = NULL;
|
||||
size_t inmsg_size = 0;
|
||||
void *out_msg = NULL;
|
||||
union smcinvoke_arg *args_buf = NULL;
|
||||
struct file *filp_to_release[object_counts_max_OO] = {NULL};
|
||||
struct smcinvoke_tzobj_context *tzobj = filp->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case SMCINVOKE_IOCTL_INVOKE_REQ:
|
||||
if (_IOC_SIZE(cmd) != sizeof(req)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
ret = copy_from_user(&req, (void __user *)arg, sizeof(req));
|
||||
if (ret) {
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
nr_args = object_counts_num_buffers(req.counts) +
|
||||
object_counts_num_objects(req.counts);
|
||||
|
||||
if (!nr_args || req.argsize != sizeof(union smcinvoke_arg)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
args_buf = kzalloc(nr_args * req.argsize, GFP_KERNEL);
|
||||
if (!args_buf) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = copy_from_user(args_buf, (void __user *)(req.args),
|
||||
nr_args * req.argsize);
|
||||
|
||||
if (ret) {
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
inmsg_size = compute_in_msg_size(&req, args_buf);
|
||||
in_msg = (void *)__get_free_pages(GFP_KERNEL,
|
||||
get_order(inmsg_size));
|
||||
if (!in_msg) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out_msg = (void *)__get_free_page(GFP_KERNEL);
|
||||
if (!out_msg) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = marshal_in(&req, args_buf, tzobj->tzhandle, in_msg,
|
||||
inmsg_size, filp_to_release);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = prepare_send_scm_msg(in_msg, inmsg_size, out_msg,
|
||||
SMCINVOKE_TZ_MIN_BUF_SIZE, &req.result);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = marshal_out(in_msg, inmsg_size, &req, args_buf);
|
||||
|
||||
ret |= copy_to_user((void __user *)(req.args), args_buf,
|
||||
nr_args * req.argsize);
|
||||
ret |= copy_to_user((void __user *)arg, &req, sizeof(req));
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
break;
|
||||
default:
|
||||
ret = -ENOIOCTLCMD;
|
||||
break;
|
||||
}
|
||||
out:
|
||||
free_page((long)out_msg);
|
||||
free_pages((long)in_msg, get_order(inmsg_size));
|
||||
kfree(args_buf);
|
||||
for (i = 0; i < object_counts_max_OO; i++) {
|
||||
if (filp_to_release[i])
|
||||
fput(filp_to_release[i]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int smcinvoke_open(struct inode *nodp, struct file *filp)
|
||||
{
|
||||
struct smcinvoke_tzobj_context *tzcxt = NULL;
|
||||
|
||||
tzcxt = kzalloc(sizeof(*tzcxt), GFP_KERNEL);
|
||||
if (!tzcxt)
|
||||
return -ENOMEM;
|
||||
|
||||
tzcxt->tzhandle = SMCINVOKE_TZ_ROOT_OBJ;
|
||||
filp->private_data = tzcxt;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int smcinvoke_release(struct inode *nodp, struct file *filp)
|
||||
{
|
||||
int ret = 0, smcinvoke_result = 0;
|
||||
uint8_t *in_buf = NULL;
|
||||
uint8_t *out_buf = NULL;
|
||||
struct smcinvoke_msg_hdr hdr = {0};
|
||||
struct smcinvoke_tzobj_context *tzobj = filp->private_data;
|
||||
uint32_t tzhandle = tzobj->tzhandle;
|
||||
|
||||
/* Root object is special in sense it is indestructible */
|
||||
if (!tzhandle || tzhandle == SMCINVOKE_TZ_ROOT_OBJ)
|
||||
goto out;
|
||||
|
||||
in_buf = (uint8_t *)__get_free_page(GFP_KERNEL);
|
||||
out_buf = (uint8_t *)__get_free_page(GFP_KERNEL);
|
||||
if (!in_buf || !out_buf)
|
||||
goto out;
|
||||
|
||||
hdr.tzhandle = tzhandle;
|
||||
hdr.op = object_op_RELEASE;
|
||||
hdr.counts = 0;
|
||||
*(struct smcinvoke_msg_hdr *)in_buf = hdr;
|
||||
|
||||
ret = prepare_send_scm_msg(in_buf, SMCINVOKE_TZ_MIN_BUF_SIZE,
|
||||
out_buf, SMCINVOKE_TZ_MIN_BUF_SIZE, &smcinvoke_result);
|
||||
out:
|
||||
kfree(filp->private_data);
|
||||
free_page((long)in_buf);
|
||||
free_page((long)out_buf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __init smcinvoke_init(void)
|
||||
{
|
||||
return misc_register(&smcinvoke_miscdev);
|
||||
}
|
||||
|
||||
device_initcall(smcinvoke_init);
|
||||
MODULE_LICENSE("GPL v2");
|
51
drivers/soc/qcom/smcinvoke_object.h
Normal file
51
drivers/soc/qcom/smcinvoke_object.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
/* Copyright (c) 2016, 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
|
||||
* only 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.
|
||||
*/
|
||||
#ifndef __SMCINVOKE_OBJECT_H
|
||||
#define __SMCINVOKE_OBJECT_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#define object_op_METHOD_MASK ((uint32_t)0x0000FFFFu)
|
||||
#define object_op_RELEASE (object_op_METHOD_MASK - 0)
|
||||
#define object_op_RETAIN (object_op_METHOD_MASK - 1)
|
||||
|
||||
#define object_counts_max_BI 0xF
|
||||
#define object_counts_max_BO 0xF
|
||||
#define object_counts_max_OI 0xF
|
||||
#define object_counts_max_OO 0xF
|
||||
|
||||
/* unpack counts */
|
||||
|
||||
#define object_counts_num_BI(k) ((size_t) (((k) >> 0) & object_counts_max_BI))
|
||||
#define object_counts_num_BO(k) ((size_t) (((k) >> 4) & object_counts_max_BO))
|
||||
#define object_counts_num_OI(k) ((size_t) (((k) >> 8) & object_counts_max_OI))
|
||||
#define object_counts_num_OO(k) ((size_t) (((k) >> 12) & object_counts_max_OO))
|
||||
#define object_counts_num_buffers(k) \
|
||||
(object_counts_num_BI(k) + object_counts_num_BO(k))
|
||||
|
||||
#define object_counts_num_objects(k) \
|
||||
(object_counts_num_OI(k) + object_counts_num_OO(k))
|
||||
|
||||
/* Indices into args[] */
|
||||
|
||||
#define object_counts_index_BI(k) 0
|
||||
#define object_counts_index_BO(k) \
|
||||
(object_counts_index_BI(k) + object_counts_num_BI(k))
|
||||
#define object_counts_index_OI(k) \
|
||||
(object_counts_index_BO(k) + object_counts_num_BO(k))
|
||||
#define object_counts_index_OO(k) \
|
||||
(object_counts_index_OI(k) + object_counts_num_OI(k))
|
||||
#define object_counts_total(k) \
|
||||
(object_counts_index_OO(k) + object_counts_num_OO(k))
|
||||
|
||||
|
||||
#endif /* __SMCINVOKE_OBJECT_H */
|
|
@ -520,3 +520,4 @@ header-y += android_pmem.h
|
|||
header-y += ipa_qmi_service_v01.h
|
||||
header-y += rmnet_ipa_fd_ioctl.h
|
||||
header-y += msm_ipa.h
|
||||
header-y += smcinvoke.h
|
||||
|
|
45
include/uapi/linux/smcinvoke.h
Normal file
45
include/uapi/linux/smcinvoke.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
#ifndef _UAPI_SMCINVOKE_H_
|
||||
#define _UAPI_SMCINVOKE_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/ioctl.h>
|
||||
|
||||
#define SMCINVOKE_USERSPACE_OBJ_NULL -1
|
||||
|
||||
struct smcinvoke_buf {
|
||||
uint64_t addr;
|
||||
uint64_t size;
|
||||
};
|
||||
|
||||
struct smcinvoke_obj {
|
||||
int64_t fd;
|
||||
int64_t reserved;
|
||||
};
|
||||
|
||||
union smcinvoke_arg {
|
||||
struct smcinvoke_buf b;
|
||||
struct smcinvoke_obj o;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct smcinvoke_cmd_req: This structure is transparently sent to TEE
|
||||
* @op - Operation to be performed
|
||||
* @counts - number of aruments passed
|
||||
* @result - result of invoke operation
|
||||
* @argsize - size of each of arguments
|
||||
* @args - args is pointer to buffer having all arguments
|
||||
*/
|
||||
struct smcinvoke_cmd_req {
|
||||
uint32_t op;
|
||||
uint32_t counts;
|
||||
int32_t result;
|
||||
uint32_t argsize;
|
||||
uint64_t __user args;
|
||||
};
|
||||
|
||||
#define SMCINVOKE_IOC_MAGIC 0x98
|
||||
|
||||
#define SMCINVOKE_IOCTL_INVOKE_REQ \
|
||||
_IOWR(SMCINVOKE_IOC_MAGIC, 1, struct smcinvoke_cmd_req)
|
||||
|
||||
#endif /* _UAPI_SMCINVOKE_H_ */
|
Loading…
Add table
Reference in a new issue