Commit a2d9214c authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'armsoc-tee' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc

Pull TEE driver infrastructure and OP-TEE drivers from Arnd Bergmann:
 "This introduces a generic TEE framework in the kernel, to handle
  trusted environemtns (security coprocessor or software implementations
  such as OP-TEE/TrustZone). I'm sending it separately from the other
  arm-soc driver changes to give it a little more visibility, once the
  subsystem is merged, we will likely keep this in the arm₋soc drivers
  branch or have the maintainers submit pull requests directly,
  depending on the patch volume.

  I have reviewed earlier versions in the past, and have reviewed the
  latest version in person during Linaro Connect BUD17.

  Here is my overall assessment of the subsystem:

   - There is clearly demand for this, both for the generic
     infrastructure and the specific OP-TEE implementation.

   - The code has gone through a large number of reviews, and the review
     comments have all been addressed, but the reviews were not coming
     up with serious issues any more and nobody volunteered to vouch for
     the quality.

   - The user space ioctl interface is sufficient to work with the
     OP-TEE driver, and it should in principle work with other TEE
     implementations that follow the GlobalPlatform[1] standards, but it
     might need to be extended in minor ways depending on specific
     requirements of future TEE implementations

   - The main downside of the API to me is how the user space is tied to
     the TEE implementation in hardware or firmware, but uses a generic
     way to communicate with it. This seems to be an inherent problem
     with what it is trying to do, and I could not come up with any
     better solution than what is implemented here.

  For a detailed history of the patch series, see

    https://lkml.org/lkml/2017/3/10/1277"

* tag 'armsoc-tee' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc:
  arm64: dt: hikey: Add optee node
  Documentation: tee subsystem and op-tee driver
  tee: add OP-TEE driver
  tee: generic TEE subsystem
  dt/bindings: add bindings for optee
parents de4d1953 414d06ac
......@@ -412,6 +412,8 @@ sysctl/
- directory with info on the /proc/sys/* files.
target/
- directory with info on generating TCM v4 fabric .ko modules
tee.txt
- info on the TEE subsystem and drivers
this_cpu_ops.txt
- List rationale behind and the way to use this_cpu operations.
thermal/
......
OP-TEE Device Tree Bindings
OP-TEE is a piece of software using hardware features to provide a Trusted
Execution Environment. The security can be provided with ARM TrustZone, but
also by virtualization or a separate chip.
We're using "linaro" as the first part of the compatible property for
the reference implementation maintained by Linaro.
* OP-TEE based on ARM TrustZone required properties:
- compatible : should contain "linaro,optee-tz"
- method : The method of calling the OP-TEE Trusted OS. Permitted
values are:
"smc" : SMC #0, with the register assignments specified
in drivers/tee/optee/optee_smc.h
"hvc" : HVC #0, with the register assignments specified
in drivers/tee/optee/optee_smc.h
Example:
firmware {
optee {
compatible = "linaro,optee-tz";
method = "smc";
};
};
......@@ -173,6 +173,7 @@ lego LEGO Systems A/S
lenovo Lenovo Group Ltd.
lg LG Corporation
licheepi Lichee Pi
linaro Linaro Limited
linux Linux-specific binding
lltc Linear Technology Corporation
lsi LSI Corp. (LSI Logic)
......
......@@ -309,6 +309,7 @@ Code Seq#(hex) Include File Comments
0xA3 80-8F Port ACL in development:
<mailto:tlewis@mindspring.com>
0xA3 90-9F linux/dtlk.h
0xA4 00-1F uapi/linux/tee.h Generic TEE subsystem
0xAA 00-3F linux/uapi/linux/userfaultfd.h
0xAB 00-1F linux/nbd.h
0xAC 00-1F linux/raw.h
......
TEE subsystem
This document describes the TEE subsystem in Linux.
A TEE (Trusted Execution Environment) is a trusted OS running in some
secure environment, for example, TrustZone on ARM CPUs, or a separate
secure co-processor etc. A TEE driver handles the details needed to
communicate with the TEE.
This subsystem deals with:
- Registration of TEE drivers
- Managing shared memory between Linux and the TEE
- Providing a generic API to the TEE
The TEE interface
=================
include/uapi/linux/tee.h defines the generic interface to a TEE.
User space (the client) connects to the driver by opening /dev/tee[0-9]* or
/dev/teepriv[0-9]*.
- TEE_IOC_SHM_ALLOC allocates shared memory and returns a file descriptor
which user space can mmap. When user space doesn't need the file
descriptor any more, it should be closed. When shared memory isn't needed
any longer it should be unmapped with munmap() to allow the reuse of
memory.
- TEE_IOC_VERSION lets user space know which TEE this driver handles and
the its capabilities.
- TEE_IOC_OPEN_SESSION opens a new session to a Trusted Application.
- TEE_IOC_INVOKE invokes a function in a Trusted Application.
- TEE_IOC_CANCEL may cancel an ongoing TEE_IOC_OPEN_SESSION or TEE_IOC_INVOKE.
- TEE_IOC_CLOSE_SESSION closes a session to a Trusted Application.
There are two classes of clients, normal clients and supplicants. The latter is
a helper process for the TEE to access resources in Linux, for example file
system access. A normal client opens /dev/tee[0-9]* and a supplicant opens
/dev/teepriv[0-9].
Much of the communication between clients and the TEE is opaque to the
driver. The main job for the driver is to receive requests from the
clients, forward them to the TEE and send back the results. In the case of
supplicants the communication goes in the other direction, the TEE sends
requests to the supplicant which then sends back the result.
OP-TEE driver
=============
The OP-TEE driver handles OP-TEE [1] based TEEs. Currently it is only the ARM
TrustZone based OP-TEE solution that is supported.
Lowest level of communication with OP-TEE builds on ARM SMC Calling
Convention (SMCCC) [2], which is the foundation for OP-TEE's SMC interface
[3] used internally by the driver. Stacked on top of that is OP-TEE Message
Protocol [4].
OP-TEE SMC interface provides the basic functions required by SMCCC and some
additional functions specific for OP-TEE. The most interesting functions are:
- OPTEE_SMC_FUNCID_CALLS_UID (part of SMCCC) returns the version information
which is then returned by TEE_IOC_VERSION
- OPTEE_SMC_CALL_GET_OS_UUID returns the particular OP-TEE implementation, used
to tell, for instance, a TrustZone OP-TEE apart from an OP-TEE running on a
separate secure co-processor.
- OPTEE_SMC_CALL_WITH_ARG drives the OP-TEE message protocol
- OPTEE_SMC_GET_SHM_CONFIG lets the driver and OP-TEE agree on which memory
range to used for shared memory between Linux and OP-TEE.
The GlobalPlatform TEE Client API [5] is implemented on top of the generic
TEE API.
Picture of the relationship between the different components in the
OP-TEE architecture.
User space Kernel Secure world
~~~~~~~~~~ ~~~~~~ ~~~~~~~~~~~~
+--------+ +-------------+
| Client | | Trusted |
+--------+ | Application |
/\ +-------------+
|| +----------+ /\
|| |tee- | ||
|| |supplicant| \/
|| +----------+ +-------------+
\/ /\ | TEE Internal|
+-------+ || | API |
+ TEE | || +--------+--------+ +-------------+
| Client| || | TEE | OP-TEE | | OP-TEE |
| API | \/ | subsys | driver | | Trusted OS |
+-------+----------------+----+-------+----+-----------+-------------+
| Generic TEE API | | OP-TEE MSG |
| IOCTL (TEE_IOC_*) | | SMCCC (OPTEE_SMC_CALL_*) |
+-----------------------------+ +------------------------------+
RPC (Remote Procedure Call) are requests from secure world to kernel driver
or tee-supplicant. An RPC is identified by a special range of SMCCC return
values from OPTEE_SMC_CALL_WITH_ARG. RPC messages which are intended for the
kernel are handled by the kernel driver. Other RPC messages will be forwarded to
tee-supplicant without further involvement of the driver, except switching
shared memory buffer representation.
References:
[1] https://github.com/OP-TEE/optee_os
[2] http://infocenter.arm.com/help/topic/com.arm.doc.den0028a/index.html
[3] drivers/tee/optee/optee_smc.h
[4] drivers/tee/optee/optee_msg.h
[5] http://www.globalplatform.org/specificationsdevice.asp look for
"TEE Client API Specification v1.0" and click download.
......@@ -9518,6 +9518,11 @@ F: arch/*/oprofile/
F: drivers/oprofile/
F: include/linux/oprofile.h
OP-TEE DRIVER
M: Jens Wiklander <jens.wiklander@linaro.org>
S: Maintained
F: drivers/tee/optee/
ORACLE CLUSTER FILESYSTEM 2 (OCFS2)
M: Mark Fasheh <mfasheh@versity.com>
M: Joel Becker <jlbec@evilplan.org>
......@@ -11299,6 +11304,14 @@ F: drivers/hwtracing/stm/
F: include/linux/stm.h
F: include/uapi/linux/stm.h
TEE SUBSYSTEM
M: Jens Wiklander <jens.wiklander@linaro.org>
S: Maintained
F: include/linux/tee_drv.h
F: include/uapi/linux/tee.h
F: drivers/tee/
F: Documentation/tee.txt
THUNDERBOLT DRIVER
M: Andreas Noever <andreas.noever@gmail.com>
S: Maintained
......
......@@ -411,6 +411,13 @@ ldo22: LDO22 {
};
};
};
firmware {
optee {
compatible = "linaro,optee-tz";
method = "smc";
};
};
};
&uart2 {
......
......@@ -204,4 +204,6 @@ source "drivers/fpga/Kconfig"
source "drivers/fsi/Kconfig"
source "drivers/tee/Kconfig"
endmenu
......@@ -180,3 +180,4 @@ obj-$(CONFIG_ANDROID) += android/
obj-$(CONFIG_NVMEM) += nvmem/
obj-$(CONFIG_FPGA) += fpga/
obj-$(CONFIG_FSI) += fsi/
obj-$(CONFIG_TEE) += tee/
# Generic Trusted Execution Environment Configuration
config TEE
tristate "Trusted Execution Environment support"
select DMA_SHARED_BUFFER
select GENERIC_ALLOCATOR
help
This implements a generic interface towards a Trusted Execution
Environment (TEE).
if TEE
menu "TEE drivers"
source "drivers/tee/optee/Kconfig"
endmenu
endif
obj-$(CONFIG_TEE) += tee.o
tee-objs += tee_core.o
tee-objs += tee_shm.o
tee-objs += tee_shm_pool.o
obj-$(CONFIG_OPTEE) += optee/
# OP-TEE Trusted Execution Environment Configuration
config OPTEE
tristate "OP-TEE"
depends on HAVE_ARM_SMCCC
help
This implements the OP-TEE Trusted Execution Environment (TEE)
driver.
obj-$(CONFIG_OPTEE) += optee.o
optee-objs += core.o
optee-objs += call.o
optee-objs += rpc.o
optee-objs += supp.o
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/*
* Copyright (c) 2015, Linaro Limited
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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 OPTEE_PRIVATE_H
#define OPTEE_PRIVATE_H
#include <linux/arm-smccc.h>
#include <linux/semaphore.h>
#include <linux/tee_drv.h>
#include <linux/types.h>
#include "optee_msg.h"
#define OPTEE_MAX_ARG_SIZE 1024
/* Some Global Platform error codes used in this driver */
#define TEEC_SUCCESS 0x00000000
#define TEEC_ERROR_BAD_PARAMETERS 0xFFFF0006
#define TEEC_ERROR_COMMUNICATION 0xFFFF000E
#define TEEC_ERROR_OUT_OF_MEMORY 0xFFFF000C
#define TEEC_ORIGIN_COMMS 0x00000002
typedef void (optee_invoke_fn)(unsigned long, unsigned long, unsigned long,
unsigned long, unsigned long, unsigned long,
unsigned long, unsigned long,
struct arm_smccc_res *);
struct optee_call_queue {
/* Serializes access to this struct */
struct mutex mutex;
struct list_head waiters;
};
struct optee_wait_queue {
/* Serializes access to this struct */
struct mutex mu;
struct list_head db;
};
/**
* struct optee_supp - supplicant synchronization struct
* @ctx the context of current connected supplicant.
* if !NULL the supplicant device is available for use,
* else busy
* @ctx_mutex: held while accessing @ctx
* @func: supplicant function id to call
* @ret: call return value
* @num_params: number of elements in @param
* @param: parameters for @func
* @req_posted: if true, a request has been posted to the supplicant
* @supp_next_send: if true, next step is for supplicant to send response
* @thrd_mutex: held by the thread doing a request to supplicant
* @supp_mutex: held by supplicant while operating on this struct
* @data_to_supp: supplicant is waiting on this for next request
* @data_from_supp: requesting thread is waiting on this to get the result
*/
struct optee_supp {
struct tee_context *ctx;
/* Serializes access of ctx */
struct mutex ctx_mutex;
u32 func;
u32 ret;
size_t num_params;
struct tee_param *param;
bool req_posted;
bool supp_next_send;
/* Serializes access to this struct for requesting thread */
struct mutex thrd_mutex;
/* Serializes access to this struct for supplicant threads */
struct mutex supp_mutex;
struct completion data_to_supp;
struct completion data_from_supp;
};
/**
* struct optee - main service struct
* @supp_teedev: supplicant device
* @teedev: client device
* @invoke_fn: function to issue smc or hvc
* @call_queue: queue of threads waiting to call @invoke_fn
* @wait_queue: queue of threads from secure world waiting for a
* secure world sync object
* @supp: supplicant synchronization struct for RPC to supplicant
* @pool: shared memory pool
* @memremaped_shm virtual address of memory in shared memory pool
*/
struct optee {
struct tee_device *supp_teedev;
struct tee_device *teedev;
optee_invoke_fn *invoke_fn;
struct optee_call_queue call_queue;
struct optee_wait_queue wait_queue;
struct optee_supp supp;
struct tee_shm_pool *pool;
void *memremaped_shm;
};
struct optee_session {
struct list_head list_node;
u32 session_id;
};
struct optee_context_data {
/* Serializes access to this struct */
struct mutex mutex;
struct list_head sess_list;
};
struct optee_rpc_param {
u32 a0;
u32 a1;
u32 a2;
u32 a3;
u32 a4;
u32 a5;
u32 a6;
u32 a7;
};
void optee_handle_rpc(struct tee_context *ctx, struct optee_rpc_param *param);
void optee_wait_queue_init(struct optee_wait_queue *wq);
void optee_wait_queue_exit(struct optee_wait_queue *wq);
u32 optee_supp_thrd_req(struct tee_context *ctx, u32 func, size_t num_params,
struct tee_param *param);
int optee_supp_read(struct tee_context *ctx, void __user *buf, size_t len);
int optee_supp_write(struct tee_context *ctx, void __user *buf, size_t len);
void optee_supp_init(struct optee_supp *supp);
void optee_supp_uninit(struct optee_supp *supp);
int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params,
struct tee_param *param);
int optee_supp_send(struct tee_context *ctx, u32 ret, u32 num_params,
struct tee_param *param);
u32 optee_do_call_with_arg(struct tee_context *ctx, phys_addr_t parg);
int optee_open_session(struct tee_context *ctx,
struct tee_ioctl_open_session_arg *arg,
struct tee_param *param);
int optee_close_session(struct tee_context *ctx, u32 session);
int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg,
struct tee_param *param);
int optee_cancel_req(struct tee_context *ctx, u32 cancel_id, u32 session);
void optee_enable_shm_cache(struct optee *optee);
void optee_disable_shm_cache(struct optee *optee);
int optee_from_msg_param(struct tee_param *params, size_t num_params,
const struct optee_msg_param *msg_params);
int optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params,
const struct tee_param *params);
/*
* Small helpers
*/
static inline void *reg_pair_to_ptr(u32 reg0, u32 reg1)
{
return (void *)(unsigned long)(((u64)reg0 << 32) | reg1);
}
static inline void reg_pair_from_64(u32 *reg0, u32 *reg1, u64 val)
{
*reg0 = val >> 32;
*reg1 = val;
}
#endif /*OPTEE_PRIVATE_H*/
This diff is collapsed.
/*
* Copyright (c) 2015-2016, Linaro Limited
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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.
*
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/tee_drv.h>
#include "optee_private.h"
#include "optee_smc.h"
struct wq_entry {
struct list_head link;
struct completion c;
u32 key;
};
void optee_wait_queue_init(struct optee_wait_queue *priv)
{
mutex_init(&priv->mu);
INIT_LIST_HEAD(&priv->db);
}
void optee_wait_queue_exit(struct optee_wait_queue *priv)
{
mutex_destroy(&priv->mu);
}
static void handle_rpc_func_cmd_get_time(struct optee_msg_arg *arg)
{
struct timespec64 ts;
if (arg->num_params != 1)
goto bad;
if ((arg->params[0].attr & OPTEE_MSG_ATTR_TYPE_MASK) !=
OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT)
goto bad;
getnstimeofday64(&ts);
arg->params[0].u.value.a = ts.tv_sec;
arg->params[0].u.value.b = ts.tv_nsec;
arg->ret = TEEC_SUCCESS;
return;
bad:
arg->ret = TEEC_ERROR_BAD_PARAMETERS;
}
static struct wq_entry *wq_entry_get(struct optee_wait_queue *wq, u32 key)
{
struct wq_entry *w;
mutex_lock(&wq->mu);
list_for_each_entry(w, &wq->db, link)
if (w->key == key)
goto out;
w = kmalloc(sizeof(*w), GFP_KERNEL);
if (w) {
init_completion(&w->c);
w->key = key;
list_add_tail(&w->link, &wq->db);
}
out:
mutex_unlock(&wq->mu);
return w;
}
static void wq_sleep(struct optee_wait_queue *wq, u32 key)
{
struct wq_entry *w = wq_entry_get(wq, key);
if (w) {
wait_for_completion(&w->c);
mutex_lock(&wq->mu);
list_del(&w->link);
mutex_unlock(&wq->mu);
kfree(w);
}
}
static void wq_wakeup(struct optee_wait_queue *wq, u32 key)
{
struct wq_entry *w = wq_entry_get(wq, key);
if (w)
complete(&w->c);
}
static void handle_rpc_func_cmd_wq(struct optee *optee,
struct optee_msg_arg *arg)
{
if (arg->num_params != 1)
goto bad;
if ((arg->params[0].attr & OPTEE_MSG_ATTR_TYPE_MASK) !=
OPTEE_MSG_ATTR_TYPE_VALUE_INPUT)
goto bad;
switch (arg->params[0].u.value.a) {
case OPTEE_MSG_RPC_WAIT_QUEUE_SLEEP:
wq_sleep(&optee->wait_queue, arg->params[0].u.value.b);
break;
case OPTEE_MSG_RPC_WAIT_QUEUE_WAKEUP:
wq_wakeup(&optee->wait_queue, arg->params[0].u.value.b);
break;
default:
goto bad;
}
arg->ret = TEEC_SUCCESS;
return;
bad:
arg->ret = TEEC_ERROR_BAD_PARAMETERS;
}
static void handle_rpc_func_cmd_wait(struct optee_msg_arg *arg)
{
u32 msec_to_wait;
if (arg->num_params != 1)
goto bad;
if ((arg->params[0].attr & OPTEE_MSG_ATTR_TYPE_MASK) !=
OPTEE_MSG_ATTR_TYPE_VALUE_INPUT)
goto bad;
msec_to_wait = arg->params[0].u.value.a;
/* set task's state to interruptible sleep */
set_current_state(TASK_INTERRUPTIBLE);
/* take a nap */
msleep(msec_to_wait);
arg->ret = TEEC_SUCCESS;
return;
bad:
arg->ret = TEEC_ERROR_BAD_PARAMETERS;
}
static void handle_rpc_supp_cmd(struct tee_context *ctx,
struct optee_msg_arg *arg)
{
struct tee_param *params;
arg->ret_origin = TEEC_ORIGIN_COMMS;
params = kmalloc_array(arg->num_params, sizeof(struct tee_param),
GFP_KERNEL);
if (!params) {
arg->ret = TEEC_ERROR_OUT_OF_MEMORY;
return;
}
if (optee_from_msg_param(params, arg->num_params, arg->params)) {
arg->ret = TEEC_ERROR_BAD_PARAMETERS;
goto out;
}
arg->ret = optee_supp_thrd_req(ctx, arg->cmd, arg->num_params, params);
if (optee_to_msg_param(arg->params, arg->num_params, params))
arg->ret = TEEC_ERROR_BAD_PARAMETERS;
out:
kfree(params);
}
static struct tee_shm *cmd_alloc_suppl(struct tee_context *ctx, size_t sz)
{
u32 ret;
struct tee_param param;
struct optee *optee = tee_get_drvdata(ctx->teedev);
struct tee_shm *shm;
param.attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT;
param.u.value.a = OPTEE_MSG_RPC_SHM_TYPE_APPL;
param.u.value.b = sz;
param.u.value.c = 0;
ret = optee_supp_thrd_req(ctx, OPTEE_MSG_RPC_CMD_SHM_ALLOC, 1, &param);
if (ret)
return ERR_PTR(-ENOMEM);
mutex_lock(&optee->supp.ctx_mutex);
/* Increases count as secure world doesn't have a reference */
shm = tee_shm_get_from_id(optee->supp.ctx, param.u.value.c);
mutex_unlock(&optee->supp.ctx_mutex);
return shm;
}
static void handle_rpc_func_cmd_shm_alloc(struct tee_context *ctx,
struct optee_msg_arg *arg)
{
phys_addr_t pa;
struct tee_shm *shm;
size_t sz;
size_t n;
arg->ret_origin = TEEC_ORIGIN_COMMS;
if (!arg->num_params ||
arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_VALUE_INPUT) {
arg->ret = TEEC_ERROR_BAD_PARAMETERS;
return;
}
for (n = 1; n < arg->num_params; n++) {
if (arg->params[n].attr != OPTEE_MSG_ATTR_TYPE_NONE) {
arg->ret = TEEC_ERROR_BAD_PARAMETERS;
return;
}
}
sz = arg->params[0].u.value.b;
switch (arg->params[0].u.value.a) {
case OPTEE_MSG_RPC_SHM_TYPE_APPL:
shm = cmd_alloc_suppl(ctx, sz);
break;
case OPTEE_MSG_RPC_SHM_TYPE_KERNEL:
shm = tee_shm_alloc(ctx, sz, TEE_SHM_MAPPED);
break;
default:
arg->ret = TEEC_ERROR_BAD_PARAMETERS;
return;
}
if (IS_ERR(shm)) {
arg->ret = TEEC_ERROR_OUT_OF_MEMORY;
return;
}
if (tee_shm_get_pa(shm, 0, &pa)) {
arg->ret = TEEC_ERROR_BAD_PARAMETERS;
goto bad;
}
arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT;
arg->params[0].u.tmem.buf_ptr = pa;
arg->params[0].u.tmem.size = sz;
arg->params[0].u.tmem.shm_ref = (unsigned long)shm;
arg->ret = TEEC_SUCCESS;
return;
bad:
tee_shm_free(shm);
}
static void cmd_free_suppl(struct tee_context *ctx, struct tee_shm *shm)
{
struct tee_param param;
param.attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT;
param.u.value.a = OPTEE_MSG_RPC_SHM_TYPE_APPL;
param.u.value.b = tee_shm_get_id(shm);
param.u.value.c = 0;
/*
* Match the tee_shm_get_from_id() in cmd_alloc_suppl() as secure
* world has released its reference.
*
* It's better to do this before sending the request to supplicant
* as we'd like to let the process doing the initial allocation to
* do release the last reference too in order to avoid stacking
* many pending fput() on the client process. This could otherwise
* happen if secure world does many allocate and free in a single
* invoke.
*/
tee_shm_put(shm);
optee_supp_thrd_req(ctx, OPTEE_MSG_RPC_CMD_SHM_FREE, 1, &param);
}
static void handle_rpc_func_cmd_shm_free(struct tee_context *ctx,
struct optee_msg_arg *arg)
{
struct tee_shm *shm;
arg->ret_origin = TEEC_ORIGIN_COMMS;
if (arg->num_params != 1 ||
arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_VALUE_INPUT) {
arg->ret = TEEC_ERROR_BAD_PARAMETERS;
return;
}
shm = (struct tee_shm *)(unsigned long)arg->params[0].u.value.b;
switch (arg->params[0].u.value.a) {
case OPTEE_MSG_RPC_SHM_TYPE_APPL:
cmd_free_suppl(ctx, shm);
break;
case OPTEE_MSG_RPC_SHM_TYPE_KERNEL:
tee_shm_free(shm);
break;
default:
arg->ret = TEEC_ERROR_BAD_PARAMETERS;
}
arg->ret = TEEC_SUCCESS;
}
static void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee,
struct tee_shm *shm)
{
struct optee_msg_arg *arg;
arg = tee_shm_get_va(shm, 0);
if (IS_ERR(arg)) {
pr_err("%s: tee_shm_get_va %p failed\n", __func__, shm);
return;
}
switch (arg->cmd) {
case OPTEE_MSG_RPC_CMD_GET_TIME:
handle_rpc_func_cmd_get_time(arg);
break;
case OPTEE_MSG_RPC_CMD_WAIT_QUEUE:
handle_rpc_func_cmd_wq(optee, arg);
break;
case OPTEE_MSG_RPC_CMD_SUSPEND:
handle_rpc_func_cmd_wait(arg);
break;
case OPTEE_MSG_RPC_CMD_SHM_ALLOC:
handle_rpc_func_cmd_shm_alloc(ctx, arg);
break;
case OPTEE_MSG_RPC_CMD_SHM_FREE:
handle_rpc_func_cmd_shm_free(ctx, arg);
break;
default:
handle_rpc_supp_cmd(ctx, arg);
}
}
/**
* optee_handle_rpc() - handle RPC from secure world
* @ctx: context doing the RPC
* @param: value of registers for the RPC
*
* Result of RPC is written back into @param.
*/
void optee_handle_rpc(struct tee_context *ctx, struct optee_rpc_param *param)
{
struct tee_device *teedev = ctx->teedev;
struct optee *optee = tee_get_drvdata(teedev);
struct tee_shm *shm;
phys_addr_t pa;
switch (OPTEE_SMC_RETURN_GET_RPC_FUNC(param->a0)) {
case OPTEE_SMC_RPC_FUNC_ALLOC:
shm = tee_shm_alloc(ctx, param->a1, TEE_SHM_MAPPED);
if (!IS_ERR(shm) && !tee_shm_get_pa(shm, 0, &pa)) {
reg_pair_from_64(&param->a1, &param->a2, pa);
reg_pair_from_64(&param->a4, &param->a5,
(unsigned long)shm);
} else {
param->a1 = 0;
param->a2 = 0;
param->a4 = 0;
param->a5 = 0;
}
break;
case OPTEE_SMC_RPC_FUNC_FREE:
shm = reg_pair_to_ptr(param->a1, param->a2);
tee_shm_free(shm);
break;
case OPTEE_SMC_RPC_FUNC_IRQ:
/*
* An IRQ was raised while secure world was executing,
* since all IRQs are handled in Linux a dummy RPC is
* performed to let Linux take the IRQ through the normal
* vector.
*/
break;
case OPTEE_SMC_RPC_FUNC_CMD:
shm = reg_pair_to_ptr(param->a1, param->a2);
handle_rpc_func_cmd(ctx, optee, shm);
break;
default:
pr_warn("Unknown RPC func 0x%x\n",
(u32)OPTEE_SMC_RETURN_GET_RPC_FUNC(param->a0));
break;
}
param->a0 = OPTEE_SMC_CALL_RETURN_FROM_RPC;
}
/*
* Copyright (c) 2015, Linaro Limited
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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/device.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include "optee_private.h"
void optee_supp_init(struct optee_supp *supp)
{
memset(supp, 0, sizeof(*supp));
mutex_init(&supp->ctx_mutex);
mutex_init(&supp->thrd_mutex);
mutex_init(&supp->supp_mutex);
init_completion(&supp->data_to_supp);
init_completion(&supp->data_from_supp);
}
void optee_supp_uninit(struct optee_supp *supp)
{
mutex_destroy(&supp->ctx_mutex);
mutex_destroy(&supp->thrd_mutex);
mutex_destroy(&supp->supp_mutex);
}
/**
* optee_supp_thrd_req() - request service from supplicant
* @ctx: context doing the request
* @func: function requested
* @num_params: number of elements in @param array
* @param: parameters for function
*
* Returns result of operation to be passed to secure world
*/
u32 optee_supp_thrd_req(struct tee_context *ctx, u32 func, size_t num_params,
struct tee_param *param)
{
bool interruptable;
struct optee *optee = tee_get_drvdata(ctx->teedev);
struct optee_supp *supp = &optee->supp;
u32 ret;
/*
* Other threads blocks here until we've copied our answer from
* supplicant.
*/
while (mutex_lock_interruptible(&supp->thrd_mutex)) {
/* See comment below on when the RPC can be interrupted. */
mutex_lock(&supp->ctx_mutex);
interruptable = !supp->ctx;
mutex_unlock(&supp->ctx_mutex);
if (interruptable)
return TEEC_ERROR_COMMUNICATION;
}
/*
* We have exclusive access now since the supplicant at this
* point is either doing a
* wait_for_completion_interruptible(&supp->data_to_supp) or is in
* userspace still about to do the ioctl() to enter
* optee_supp_recv() below.
*/
supp->func = func;
supp->num_params = num_params;
supp->param = param;
supp->req_posted = true;
/* Let supplicant get the data */
complete(&supp->data_to_supp);
/*
* Wait for supplicant to process and return result, once we've
* returned from wait_for_completion(data_from_supp) we have
* exclusive access again.
*/
while (wait_for_completion_interruptible(&supp->data_from_supp)) {
mutex_lock(&supp->ctx_mutex);
interruptable = !supp->ctx;
if (interruptable) {
/*
* There's no supplicant available and since the
* supp->ctx_mutex currently is held none can
* become available until the mutex released
* again.
*
* Interrupting an RPC to supplicant is only
* allowed as a way of slightly improving the user
* experience in case the supplicant hasn't been
* started yet. During normal operation the supplicant
* will serve all requests in a timely manner and
* interrupting then wouldn't make sense.
*/
supp->ret = TEEC_ERROR_COMMUNICATION;
init_completion(&supp->data_to_supp);
}
mutex_unlock(&supp->ctx_mutex);
if (interruptable)
break;
}
ret = supp->ret;
supp->param = NULL;
supp->req_posted = false;
/* We're done, let someone else talk to the supplicant now. */
mutex_unlock(&supp->thrd_mutex);
return ret;
}
/**
* optee_supp_recv() - receive request for supplicant
* @ctx: context receiving the request
* @func: requested function in supplicant
* @num_params: number of elements allocated in @param, updated with number
* used elements
* @param: space for parameters for @func
*
* Returns 0 on success or <0 on failure
*/
int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params,
struct tee_param *param)
{
struct tee_device *teedev = ctx->teedev;
struct optee *optee = tee_get_drvdata(teedev);
struct optee_supp *supp = &optee->supp;
int rc;
/*
* In case two threads in one supplicant is calling this function
* simultaneously we need to protect the data with a mutex which
* we'll release before returning.
*/
mutex_lock(&supp->supp_mutex);
if (supp->supp_next_send) {
/*
* optee_supp_recv() has been called again without
* a optee_supp_send() in between. Supplicant has
* probably been restarted before it was able to
* write back last result. Abort last request and
* wait for a new.
*/
if (supp->req_posted) {
supp->ret = TEEC_ERROR_COMMUNICATION;
supp->supp_next_send = false;
complete(&supp->data_from_supp);
}
}
/*
* This is where supplicant will be hanging most of the
* time, let's make this interruptable so we can easily
* restart supplicant if needed.
*/
if (wait_for_completion_interruptible(&supp->data_to_supp)) {
rc = -ERESTARTSYS;
goto out;
}
/* We have exlusive access to the data */
if (*num_params < supp->num_params) {
/*
* Not enough room for parameters, tell supplicant
* it failed and abort last request.
*/
supp->ret = TEEC_ERROR_COMMUNICATION;
rc = -EINVAL;
complete(&supp->data_from_supp);
goto out;
}
*func = supp->func;
*num_params = supp->num_params;
memcpy(param, supp->param,
sizeof(struct tee_param) * supp->num_params);
/* Allow optee_supp_send() below to do its work */
supp->supp_next_send = true;
rc = 0;
out:
mutex_unlock(&supp->supp_mutex);
return rc;
}
/**
* optee_supp_send() - send result of request from supplicant
* @ctx: context sending result
* @ret: return value of request
* @num_params: number of parameters returned
* @param: returned parameters
*
* Returns 0 on success or <0 on failure.
*/
int optee_supp_send(struct tee_context *ctx, u32 ret, u32 num_params,
struct tee_param *param)
{
struct tee_device *teedev = ctx->teedev;
struct optee *optee = tee_get_drvdata(teedev);
struct optee_supp *supp = &optee->supp;
size_t n;
int rc = 0;
/*
* We still have exclusive access to the data since that's how we
* left it when returning from optee_supp_read().
*/
/* See comment on mutex in optee_supp_read() above */
mutex_lock(&supp->supp_mutex);
if (!supp->supp_next_send) {
/*
* Something strange is going on, supplicant shouldn't
* enter optee_supp_send() in this state
*/
rc = -ENOENT;
goto out;
}
if (num_params != supp->num_params) {
/*
* Something is wrong, let supplicant restart. Next call to
* optee_supp_recv() will give an error to the requesting
* thread and release it.
*/
rc = -EINVAL;
goto out;
}
/* Update out and in/out parameters */
for (n = 0; n < num_params; n++) {
struct tee_param *p = supp->param + n;
switch (p->attr) {
case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT:
case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT:
p->u.value.a = param[n].u.value.a;
p->u.value.b = param[n].u.value.b;
p->u.value.c = param[n].u.value.c;
break;
case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT:
case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT:
p->u.memref.size = param[n].u.memref.size;
break;
default:
break;
}
}
supp->ret = ret;
/* Allow optee_supp_recv() above to do its work */
supp->supp_next_send = false;
/* Let the requesting thread continue */
complete(&supp->data_from_supp);
out:
mutex_unlock(&supp->supp_mutex);
return rc;
}
This diff is collapsed.
/*
* Copyright (c) 2015-2016, Linaro Limited
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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 TEE_PRIVATE_H
#define TEE_PRIVATE_H
#include <linux/cdev.h>
#include <linux/completion.h>
#include <linux/device.h>
#include <linux/kref.h>
#include <linux/mutex.h>
#include <linux/types.h>
struct tee_device;
/**
* struct tee_shm - shared memory object
* @teedev: device used to allocate the object
* @ctx: context using the object, if NULL the context is gone
* @link link element
* @paddr: physical address of the shared memory
* @kaddr: virtual address of the shared memory
* @size: size of shared memory
* @dmabuf: dmabuf used to for exporting to user space
* @flags: defined by TEE_SHM_* in tee_drv.h
* @id: unique id of a shared memory object on this device
*/
struct tee_shm {
struct tee_device *teedev;
struct tee_context *ctx;
struct list_head link;
phys_addr_t paddr;
void *kaddr;
size_t size;
struct dma_buf *dmabuf;
u32 flags;
int id;
};
struct tee_shm_pool_mgr;
/**
* struct tee_shm_pool_mgr_ops - shared memory pool manager operations
* @alloc: called when allocating shared memory
* @free: called when freeing shared memory
*/
struct tee_shm_pool_mgr_ops {
int (*alloc)(struct tee_shm_pool_mgr *poolmgr, struct tee_shm *shm,
size_t size);
void (*free)(struct tee_shm_pool_mgr *poolmgr, struct tee_shm *shm);
};
/**
* struct tee_shm_pool_mgr - shared memory manager
* @ops: operations
* @private_data: private data for the shared memory manager
*/
struct tee_shm_pool_mgr {
const struct tee_shm_pool_mgr_ops *ops;
void *private_data;
};
/**
* struct tee_shm_pool - shared memory pool
* @private_mgr: pool manager for shared memory only between kernel
* and secure world
* @dma_buf_mgr: pool manager for shared memory exported to user space
* @destroy: called when destroying the pool
* @private_data: private data for the pool
*/
struct tee_shm_pool {
struct tee_shm_pool_mgr private_mgr;
struct tee_shm_pool_mgr dma_buf_mgr;
void (*destroy)(struct tee_shm_pool *pool);
void *private_data;
};
#define TEE_DEVICE_FLAG_REGISTERED 0x1
#define TEE_MAX_DEV_NAME_LEN 32
/**
* struct tee_device - TEE Device representation
* @name: name of device
* @desc: description of device
* @id: unique id of device
* @flags: represented by TEE_DEVICE_FLAG_REGISTERED above
* @dev: embedded basic device structure
* @cdev: embedded cdev
* @num_users: number of active users of this device
* @c_no_user: completion used when unregistering the device
* @mutex: mutex protecting @num_users and @idr
* @idr: register of shared memory object allocated on this device
* @pool: shared memory pool
*/
struct tee_device {
char name[TEE_MAX_DEV_NAME_LEN];
const struct tee_desc *desc;
int id;
unsigned int flags;
struct device dev;
struct cdev cdev;
size_t num_users;
struct completion c_no_users;
struct mutex mutex; /* protects num_users and idr */
struct idr idr;
struct tee_shm_pool *pool;
};
int tee_shm_init(void);
int tee_shm_get_fd(struct tee_shm *shm);
bool tee_device_get(struct tee_device *teedev);
void tee_device_put(struct tee_device *teedev);
#endif /*TEE_PRIVATE_H*/
/*
* Copyright (c) 2015-2016, Linaro Limited
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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/device.h>
#include <linux/dma-buf.h>
#include <linux/fdtable.h>
#include <linux/idr.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/tee_drv.h>
#include "tee_private.h"
static void tee_shm_release(struct tee_shm *shm)
{
struct tee_device *teedev = shm->teedev;
struct tee_shm_pool_mgr *poolm;
mutex_lock(&teedev->mutex);
idr_remove(&teedev->idr, shm->id);
if (shm->ctx)
list_del(&shm->link);
mutex_unlock(&teedev->mutex);
if (shm->flags & TEE_SHM_DMA_BUF)
poolm = &teedev->pool->dma_buf_mgr;
else
poolm = &teedev->pool->private_mgr;
poolm->ops->free(poolm, shm);
kfree(shm);
tee_device_put(teedev);
}
static struct sg_table *tee_shm_op_map_dma_buf(struct dma_buf_attachment
*attach, enum dma_data_direction dir)
{
return NULL;
}
static void tee_shm_op_unmap_dma_buf(struct dma_buf_attachment *attach,
struct sg_table *table,
enum dma_data_direction dir)
{
}
static void tee_shm_op_release(struct dma_buf *dmabuf)
{
struct tee_shm *shm = dmabuf->priv;
tee_shm_release(shm);
}
static void *tee_shm_op_map_atomic(struct dma_buf *dmabuf, unsigned long pgnum)
{
return NULL;
}
static void *tee_shm_op_map(struct dma_buf *dmabuf, unsigned long pgnum)
{
return NULL;
}
static int tee_shm_op_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
{
struct tee_shm *shm = dmabuf->priv;
size_t size = vma->vm_end - vma->vm_start;
return remap_pfn_range(vma, vma->vm_start, shm->paddr >> PAGE_SHIFT,
size, vma->vm_page_prot);
}
static struct dma_buf_ops tee_shm_dma_buf_ops = {
.map_dma_buf = tee_shm_op_map_dma_buf,
.unmap_dma_buf = tee_shm_op_unmap_dma_buf,
.release = tee_shm_op_release,
.map_atomic = tee_shm_op_map_atomic,
.map = tee_shm_op_map,
.mmap = tee_shm_op_mmap,
};
/**
* tee_shm_alloc() - Allocate shared memory
* @ctx: Context that allocates the shared memory
* @size: Requested size of shared memory
* @flags: Flags setting properties for the requested shared memory.
*
* Memory allocated as global shared memory is automatically freed when the
* TEE file pointer is closed. The @flags field uses the bits defined by
* TEE_SHM_* in <linux/tee_drv.h>. TEE_SHM_MAPPED must currently always be
* set. If TEE_SHM_DMA_BUF global shared memory will be allocated and
* associated with a dma-buf handle, else driver private memory.
*/
struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags)
{
struct tee_device *teedev = ctx->teedev;
struct tee_shm_pool_mgr *poolm = NULL;
struct tee_shm *shm;
void *ret;
int rc;
if (!(flags & TEE_SHM_MAPPED)) {
dev_err(teedev->dev.parent,
"only mapped allocations supported\n");
return ERR_PTR(-EINVAL);
}
if ((flags & ~(TEE_SHM_MAPPED | TEE_SHM_DMA_BUF))) {
dev_err(teedev->dev.parent, "invalid shm flags 0x%x", flags);
return ERR_PTR(-EINVAL);
}
if (!tee_device_get(teedev))
return ERR_PTR(-EINVAL);
if (!teedev->pool) {
/* teedev has been detached from driver */
ret = ERR_PTR(-EINVAL);
goto err_dev_put;
}
shm = kzalloc(sizeof(*shm), GFP_KERNEL);
if (!shm) {
ret = ERR_PTR(-ENOMEM);
goto err_dev_put;
}
shm->flags = flags;
shm->teedev = teedev;
shm->ctx = ctx;
if (flags & TEE_SHM_DMA_BUF)
poolm = &teedev->pool->dma_buf_mgr;
else
poolm = &teedev->pool->private_mgr;
rc = poolm->ops->alloc(poolm, shm, size);
if (rc) {
ret = ERR_PTR(rc);
goto err_kfree;
}
mutex_lock(&teedev->mutex);
shm->id = idr_alloc(&teedev->idr, shm, 1, 0, GFP_KERNEL);
mutex_unlock(&teedev->mutex);
if (shm->id < 0) {
ret = ERR_PTR(shm->id);
goto err_pool_free;
}
if (flags & TEE_SHM_DMA_BUF) {
DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
exp_info.ops = &tee_shm_dma_buf_ops;
exp_info.size = shm->size;
exp_info.flags = O_RDWR;
exp_info.priv = shm;
shm->dmabuf = dma_buf_export(&exp_info);
if (IS_ERR(shm->dmabuf)) {
ret = ERR_CAST(shm->dmabuf);
goto err_rem;
}
}
mutex_lock(&teedev->mutex);
list_add_tail(&shm->link, &ctx->list_shm);
mutex_unlock(&teedev->mutex);
return shm;
err_rem:
mutex_lock(&teedev->mutex);
idr_remove(&teedev->idr, shm->id);
mutex_unlock(&teedev->mutex);
err_pool_free:
poolm->ops->free(poolm, shm);
err_kfree:
kfree(shm);
err_dev_put:
tee_device_put(teedev);
return ret;
}
EXPORT_SYMBOL_GPL(tee_shm_alloc);
/**
* tee_shm_get_fd() - Increase reference count and return file descriptor
* @shm: Shared memory handle
* @returns user space file descriptor to shared memory
*/
int tee_shm_get_fd(struct tee_shm *shm)
{
u32 req_flags = TEE_SHM_MAPPED | TEE_SHM_DMA_BUF;
int fd;
if ((shm->flags & req_flags) != req_flags)
return -EINVAL;
fd = dma_buf_fd(shm->dmabuf, O_CLOEXEC);
if (fd >= 0)
get_dma_buf(shm->dmabuf);
return fd;
}
/**
* tee_shm_free() - Free shared memory
* @shm: Handle to shared memory to free
*/
void tee_shm_free(struct tee_shm *shm)
{
/*
* dma_buf_put() decreases the dmabuf reference counter and will
* call tee_shm_release() when the last reference is gone.
*
* In the case of driver private memory we call tee_shm_release
* directly instead as it doesn't have a reference counter.
*/
if (shm->flags & TEE_SHM_DMA_BUF)
dma_buf_put(shm->dmabuf);
else
tee_shm_release(shm);
}
EXPORT_SYMBOL_GPL(tee_shm_free);
/**
* tee_shm_va2pa() - Get physical address of a virtual address
* @shm: Shared memory handle
* @va: Virtual address to tranlsate
* @pa: Returned physical address
* @returns 0 on success and < 0 on failure
*/
int tee_shm_va2pa(struct tee_shm *shm, void *va, phys_addr_t *pa)
{
/* Check that we're in the range of the shm */
if ((char *)va < (char *)shm->kaddr)
return -EINVAL;
if ((char *)va >= ((char *)shm->kaddr + shm->size))
return -EINVAL;
return tee_shm_get_pa(
shm, (unsigned long)va - (unsigned long)shm->kaddr, pa);
}
EXPORT_SYMBOL_GPL(tee_shm_va2pa);
/**
* tee_shm_pa2va() - Get virtual address of a physical address
* @shm: Shared memory handle
* @pa: Physical address to tranlsate
* @va: Returned virtual address
* @returns 0 on success and < 0 on failure
*/
int tee_shm_pa2va(struct tee_shm *shm, phys_addr_t pa, void **va)
{
/* Check that we're in the range of the shm */
if (pa < shm->paddr)
return -EINVAL;
if (pa >= (shm->paddr + shm->size))
return -EINVAL;
if (va) {
void *v = tee_shm_get_va(shm, pa - shm->paddr);
if (IS_ERR(v))
return PTR_ERR(v);
*va = v;
}
return 0;
}
EXPORT_SYMBOL_GPL(tee_shm_pa2va);
/**
* tee_shm_get_va() - Get virtual address of a shared memory plus an offset
* @shm: Shared memory handle
* @offs: Offset from start of this shared memory
* @returns virtual address of the shared memory + offs if offs is within
* the bounds of this shared memory, else an ERR_PTR
*/
void *tee_shm_get_va(struct tee_shm *shm, size_t offs)
{
if (offs >= shm->size)
return ERR_PTR(-EINVAL);
return (char *)shm->kaddr + offs;
}
EXPORT_SYMBOL_GPL(tee_shm_get_va);
/**
* tee_shm_get_pa() - Get physical address of a shared memory plus an offset
* @shm: Shared memory handle
* @offs: Offset from start of this shared memory
* @pa: Physical address to return
* @returns 0 if offs is within the bounds of this shared memory, else an
* error code.
*/
int tee_shm_get_pa(struct tee_shm *shm, size_t offs, phys_addr_t *pa)
{
if (offs >= shm->size)
return -EINVAL;
if (pa)
*pa = shm->paddr + offs;
return 0;
}
EXPORT_SYMBOL_GPL(tee_shm_get_pa);
/**
* tee_shm_get_from_id() - Find shared memory object and increase reference
* count
* @ctx: Context owning the shared memory
* @id: Id of shared memory object
* @returns a pointer to 'struct tee_shm' on success or an ERR_PTR on failure
*/
struct tee_shm *tee_shm_get_from_id(struct tee_context *ctx, int id)
{
struct tee_device *teedev;
struct tee_shm *shm;
if (!ctx)
return ERR_PTR(-EINVAL);
teedev = ctx->teedev;
mutex_lock(&teedev->mutex);
shm = idr_find(&teedev->idr, id);
if (!shm || shm->ctx != ctx)
shm = ERR_PTR(-EINVAL);
else if (shm->flags & TEE_SHM_DMA_BUF)
get_dma_buf(shm->dmabuf);
mutex_unlock(&teedev->mutex);
return shm;
}
EXPORT_SYMBOL_GPL(tee_shm_get_from_id);
/**
* tee_shm_get_id() - Get id of a shared memory object
* @shm: Shared memory handle
* @returns id
*/
int tee_shm_get_id(struct tee_shm *shm)
{
return shm->id;
}
EXPORT_SYMBOL_GPL(tee_shm_get_id);
/**
* tee_shm_put() - Decrease reference count on a shared memory handle
* @shm: Shared memory handle
*/
void tee_shm_put(struct tee_shm *shm)
{
if (shm->flags & TEE_SHM_DMA_BUF)
dma_buf_put(shm->dmabuf);
}
EXPORT_SYMBOL_GPL(tee_shm_put);
/*
* Copyright (c) 2015, Linaro Limited
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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/device.h>
#include <linux/dma-buf.h>
#include <linux/genalloc.h>
#include <linux/slab.h>
#include <linux/tee_drv.h>
#include "tee_private.h"
static int pool_op_gen_alloc(struct tee_shm_pool_mgr *poolm,
struct tee_shm *shm, size_t size)
{
unsigned long va;
struct gen_pool *genpool = poolm->private_data;
size_t s = roundup(size, 1 << genpool->min_alloc_order);
va = gen_pool_alloc(genpool, s);
if (!va)
return -ENOMEM;
memset((void *)va, 0, s);
shm->kaddr = (void *)va;
shm->paddr = gen_pool_virt_to_phys(genpool, va);
shm->size = s;
return 0;
}
static void pool_op_gen_free(struct tee_shm_pool_mgr *poolm,
struct tee_shm *shm)
{
gen_pool_free(poolm->private_data, (unsigned long)shm->kaddr,
shm->size);
shm->kaddr = NULL;
}
static const struct tee_shm_pool_mgr_ops pool_ops_generic = {
.alloc = pool_op_gen_alloc,
.free = pool_op_gen_free,
};
static void pool_res_mem_destroy(struct tee_shm_pool *pool)
{
gen_pool_destroy(pool->private_mgr.private_data);
gen_pool_destroy(pool->dma_buf_mgr.private_data);
}
static int pool_res_mem_mgr_init(struct tee_shm_pool_mgr *mgr,
struct tee_shm_pool_mem_info *info,
int min_alloc_order)
{
size_t page_mask = PAGE_SIZE - 1;
struct gen_pool *genpool = NULL;
int rc;
/*
* Start and end must be page aligned
*/
if ((info->vaddr & page_mask) || (info->paddr & page_mask) ||
(info->size & page_mask))
return -EINVAL;
genpool = gen_pool_create(min_alloc_order, -1);
if (!genpool)
return -ENOMEM;
gen_pool_set_algo(genpool, gen_pool_best_fit, NULL);
rc = gen_pool_add_virt(genpool, info->vaddr, info->paddr, info->size,
-1);
if (rc) {
gen_pool_destroy(genpool);
return rc;
}
mgr->private_data = genpool;
mgr->ops = &pool_ops_generic;
return 0;
}
/**
* tee_shm_pool_alloc_res_mem() - Create a shared memory pool from reserved
* memory range
* @priv_info: Information for driver private shared memory pool
* @dmabuf_info: Information for dma-buf shared memory pool
*
* Start and end of pools will must be page aligned.
*
* Allocation with the flag TEE_SHM_DMA_BUF set will use the range supplied
* in @dmabuf, others will use the range provided by @priv.
*
* @returns pointer to a 'struct tee_shm_pool' or an ERR_PTR on failure.
*/
struct tee_shm_pool *
tee_shm_pool_alloc_res_mem(struct tee_shm_pool_mem_info *priv_info,
struct tee_shm_pool_mem_info *dmabuf_info)
{
struct tee_shm_pool *pool = NULL;
int ret;
pool = kzalloc(sizeof(*pool), GFP_KERNEL);
if (!pool) {
ret = -ENOMEM;
goto err;
}
/*
* Create the pool for driver private shared memory
*/
ret = pool_res_mem_mgr_init(&pool->private_mgr, priv_info,
3 /* 8 byte aligned */);
if (ret)
goto err;
/*
* Create the pool for dma_buf shared memory
*/
ret = pool_res_mem_mgr_init(&pool->dma_buf_mgr, dmabuf_info,
PAGE_SHIFT);
if (ret)
goto err;
pool->destroy = pool_res_mem_destroy;
return pool;
err:
if (ret == -ENOMEM)
pr_err("%s: can't allocate memory for res_mem shared memory pool\n", __func__);
if (pool && pool->private_mgr.private_data)
gen_pool_destroy(pool->private_mgr.private_data);
kfree(pool);
return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(tee_shm_pool_alloc_res_mem);
/**
* tee_shm_pool_free() - Free a shared memory pool
* @pool: The shared memory pool to free
*
* There must be no remaining shared memory allocated from this pool when
* this function is called.
*/
void tee_shm_pool_free(struct tee_shm_pool *pool)
{
pool->destroy(pool);
kfree(pool);
}
EXPORT_SYMBOL_GPL(tee_shm_pool_free);
/*
* Copyright (c) 2015-2016, Linaro Limited
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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 __TEE_DRV_H
#define __TEE_DRV_H
#include <linux/types.h>
#include <linux/idr.h>
#include <linux/list.h>
#include <linux/tee.h>
/*
* The file describes the API provided by the generic TEE driver to the
* specific TEE driver.
*/
#define TEE_SHM_MAPPED 0x1 /* Memory mapped by the kernel */
#define TEE_SHM_DMA_BUF 0x2 /* Memory with dma-buf handle */
struct tee_device;
struct tee_shm;
struct tee_shm_pool;
/**
* struct tee_context - driver specific context on file pointer data
* @teedev: pointer to this drivers struct tee_device
* @list_shm: List of shared memory object owned by this context
* @data: driver specific context data, managed by the driver
*/
struct tee_context {
struct tee_device *teedev;
struct list_head list_shm;
void *data;
};
struct tee_param_memref {
size_t shm_offs;
size_t size;
struct tee_shm *shm;
};
struct tee_param_value {
u64 a;
u64 b;
u64 c;
};
struct tee_param {
u64 attr;
union {
struct tee_param_memref memref;
struct tee_param_value value;
} u;
};
/**
* struct tee_driver_ops - driver operations vtable
* @get_version: returns version of driver
* @open: called when the device file is opened
* @release: release this open file
* @open_session: open a new session
* @close_session: close a session
* @invoke_func: invoke a trusted function
* @cancel_req: request cancel of an ongoing invoke or open
* @supp_revc: called for supplicant to get a command
* @supp_send: called for supplicant to send a response
*/
struct tee_driver_ops {
void (*get_version)(struct tee_device *teedev,
struct tee_ioctl_version_data *vers);
int (*open)(struct tee_context *ctx);
void (*release)(struct tee_context *ctx);
int (*open_session)(struct tee_context *ctx,
struct tee_ioctl_open_session_arg *arg,
struct tee_param *param);
int (*close_session)(struct tee_context *ctx, u32 session);
int (*invoke_func)(struct tee_context *ctx,
struct tee_ioctl_invoke_arg *arg,
struct tee_param *param);
int (*cancel_req)(struct tee_context *ctx, u32 cancel_id, u32 session);
int (*supp_recv)(struct tee_context *ctx, u32 *func, u32 *num_params,
struct tee_param *param);
int (*supp_send)(struct tee_context *ctx, u32 ret, u32 num_params,
struct tee_param *param);
};
/**
* struct tee_desc - Describes the TEE driver to the subsystem
* @name: name of driver
* @ops: driver operations vtable
* @owner: module providing the driver
* @flags: Extra properties of driver, defined by TEE_DESC_* below
*/
#define TEE_DESC_PRIVILEGED 0x1
struct tee_desc {
const char *name;
const struct tee_driver_ops *ops;
struct module *owner;
u32 flags;
};
/**
* tee_device_alloc() - Allocate a new struct tee_device instance
* @teedesc: Descriptor for this driver
* @dev: Parent device for this device
* @pool: Shared memory pool, NULL if not used
* @driver_data: Private driver data for this device
*
* Allocates a new struct tee_device instance. The device is
* removed by tee_device_unregister().
*
* @returns a pointer to a 'struct tee_device' or an ERR_PTR on failure
*/
struct tee_device *tee_device_alloc(const struct tee_desc *teedesc,
struct device *dev,
struct tee_shm_pool *pool,
void *driver_data);
/**
* tee_device_register() - Registers a TEE device
* @teedev: Device to register
*
* tee_device_unregister() need to be called to remove the @teedev if
* this function fails.
*
* @returns < 0 on failure
*/
int tee_device_register(struct tee_device *teedev);
/**
* tee_device_unregister() - Removes a TEE device
* @teedev: Device to unregister
*
* This function should be called to remove the @teedev even if
* tee_device_register() hasn't been called yet. Does nothing if
* @teedev is NULL.
*/
void tee_device_unregister(struct tee_device *teedev);
/**
* struct tee_shm_pool_mem_info - holds information needed to create a shared
* memory pool
* @vaddr: Virtual address of start of pool
* @paddr: Physical address of start of pool
* @size: Size in bytes of the pool
*/
struct tee_shm_pool_mem_info {
unsigned long vaddr;
phys_addr_t paddr;
size_t size;
};
/**
* tee_shm_pool_alloc_res_mem() - Create a shared memory pool from reserved
* memory range
* @priv_info: Information for driver private shared memory pool
* @dmabuf_info: Information for dma-buf shared memory pool
*
* Start and end of pools will must be page aligned.
*
* Allocation with the flag TEE_SHM_DMA_BUF set will use the range supplied
* in @dmabuf, others will use the range provided by @priv.
*
* @returns pointer to a 'struct tee_shm_pool' or an ERR_PTR on failure.
*/
struct tee_shm_pool *
tee_shm_pool_alloc_res_mem(struct tee_shm_pool_mem_info *priv_info,
struct tee_shm_pool_mem_info *dmabuf_info);
/**
* tee_shm_pool_free() - Free a shared memory pool
* @pool: The shared memory pool to free
*
* The must be no remaining shared memory allocated from this pool when
* this function is called.
*/
void tee_shm_pool_free(struct tee_shm_pool *pool);
/**
* tee_get_drvdata() - Return driver_data pointer
* @returns the driver_data pointer supplied to tee_register().
*/
void *tee_get_drvdata(struct tee_device *teedev);
/**
* tee_shm_alloc() - Allocate shared memory
* @ctx: Context that allocates the shared memory
* @size: Requested size of shared memory
* @flags: Flags setting properties for the requested shared memory.
*
* Memory allocated as global shared memory is automatically freed when the
* TEE file pointer is closed. The @flags field uses the bits defined by
* TEE_SHM_* above. TEE_SHM_MAPPED must currently always be set. If
* TEE_SHM_DMA_BUF global shared memory will be allocated and associated
* with a dma-buf handle, else driver private memory.
*
* @returns a pointer to 'struct tee_shm'
*/
struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags);
/**
* tee_shm_free() - Free shared memory
* @shm: Handle to shared memory to free
*/
void tee_shm_free(struct tee_shm *shm);
/**
* tee_shm_put() - Decrease reference count on a shared memory handle
* @shm: Shared memory handle
*/
void tee_shm_put(struct tee_shm *shm);
/**
* tee_shm_va2pa() - Get physical address of a virtual address
* @shm: Shared memory handle
* @va: Virtual address to tranlsate
* @pa: Returned physical address
* @returns 0 on success and < 0 on failure
*/
int tee_shm_va2pa(struct tee_shm *shm, void *va, phys_addr_t *pa);
/**
* tee_shm_pa2va() - Get virtual address of a physical address
* @shm: Shared memory handle
* @pa: Physical address to tranlsate
* @va: Returned virtual address
* @returns 0 on success and < 0 on failure
*/
int tee_shm_pa2va(struct tee_shm *shm, phys_addr_t pa, void **va);
/**
* tee_shm_get_va() - Get virtual address of a shared memory plus an offset
* @shm: Shared memory handle
* @offs: Offset from start of this shared memory
* @returns virtual address of the shared memory + offs if offs is within
* the bounds of this shared memory, else an ERR_PTR
*/
void *tee_shm_get_va(struct tee_shm *shm, size_t offs);
/**
* tee_shm_get_pa() - Get physical address of a shared memory plus an offset
* @shm: Shared memory handle
* @offs: Offset from start of this shared memory
* @pa: Physical address to return
* @returns 0 if offs is within the bounds of this shared memory, else an
* error code.
*/
int tee_shm_get_pa(struct tee_shm *shm, size_t offs, phys_addr_t *pa);
/**
* tee_shm_get_id() - Get id of a shared memory object
* @shm: Shared memory handle
* @returns id
*/
int tee_shm_get_id(struct tee_shm *shm);
/**
* tee_shm_get_from_id() - Find shared memory object and increase reference
* count
* @ctx: Context owning the shared memory
* @id: Id of shared memory object
* @returns a pointer to 'struct tee_shm' on success or an ERR_PTR on failure
*/
struct tee_shm *tee_shm_get_from_id(struct tee_context *ctx, int id);
#endif /*__TEE_DRV_H*/
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment