Commit 20f6d958 authored by Arnd Bergmann's avatar Arnd Bergmann

Merge tag 'optee-ffa-for-v5.16' of...

Merge tag 'optee-ffa-for-v5.16' of git://git.linaro.org/people/jens.wiklander/linux-tee into arm/drivers

Add FF-A support in OP-TEE driver

Adds supports for the OP-TEE driver to communicate with secure world
using FF-A [1] as transport.

[1] https://developer.arm.com/documentation/den0077/latest

* tag 'optee-ffa-for-v5.16' of git://git.linaro.org/people/jens.wiklander/linux-tee:
  optee: add FF-A support
  optee: isolate smc abi
  optee: refactor driver with internal callbacks
  optee: simplify optee_release()
  tee: add sec_world_id to struct tee_shm
  tee: optee: Fix missing devices unregister during optee_remove
  tee/optee/shm_pool: fix application of sizeof to pointer

Link: https://lore.kernel.org/r/20211018121324.GA2943530@jadeSigned-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parents 9645ccc7 4615e5a3
...@@ -4,8 +4,9 @@ optee-objs += core.o ...@@ -4,8 +4,9 @@ optee-objs += core.o
optee-objs += call.o optee-objs += call.o
optee-objs += rpc.o optee-objs += rpc.o
optee-objs += supp.o optee-objs += supp.o
optee-objs += shm_pool.o
optee-objs += device.o optee-objs += device.o
optee-objs += smc_abi.o
optee-objs += ffa_abi.o
# for tracing framework to find optee_trace.h # for tracing framework to find optee_trace.h
CFLAGS_call.o := -I$(src) CFLAGS_smc_abi.o := -I$(src)
This diff is collapsed.
This diff is collapsed.
...@@ -53,6 +53,13 @@ static int get_devices(struct tee_context *ctx, u32 session, ...@@ -53,6 +53,13 @@ static int get_devices(struct tee_context *ctx, u32 session,
return 0; return 0;
} }
static void optee_release_device(struct device *dev)
{
struct tee_client_device *optee_device = to_tee_client_device(dev);
kfree(optee_device);
}
static int optee_register_device(const uuid_t *device_uuid) static int optee_register_device(const uuid_t *device_uuid)
{ {
struct tee_client_device *optee_device = NULL; struct tee_client_device *optee_device = NULL;
...@@ -63,6 +70,7 @@ static int optee_register_device(const uuid_t *device_uuid) ...@@ -63,6 +70,7 @@ static int optee_register_device(const uuid_t *device_uuid)
return -ENOMEM; return -ENOMEM;
optee_device->dev.bus = &tee_bus_type; optee_device->dev.bus = &tee_bus_type;
optee_device->dev.release = optee_release_device;
if (dev_set_name(&optee_device->dev, "optee-ta-%pUb", device_uuid)) { if (dev_set_name(&optee_device->dev, "optee-ta-%pUb", device_uuid)) {
kfree(optee_device); kfree(optee_device);
return -ENOMEM; return -ENOMEM;
...@@ -154,3 +162,17 @@ int optee_enumerate_devices(u32 func) ...@@ -154,3 +162,17 @@ int optee_enumerate_devices(u32 func)
{ {
return __optee_enumerate_devices(func); return __optee_enumerate_devices(func);
} }
static int __optee_unregister_device(struct device *dev, void *data)
{
if (!strncmp(dev_name(dev), "optee-ta", strlen("optee-ta")))
device_unregister(dev);
return 0;
}
void optee_unregister_devices(void)
{
bus_for_each_dev(&tee_bus_type, NULL, NULL,
__optee_unregister_device);
}
This diff is collapsed.
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2019-2021, Linaro Limited
*/
/*
* This file is exported by OP-TEE and is kept in sync between secure world
* and normal world drivers. We're using ARM FF-A 1.0 specification.
*/
#ifndef __OPTEE_FFA_H
#define __OPTEE_FFA_H
#include <linux/arm_ffa.h>
/*
* Normal world sends requests with FFA_MSG_SEND_DIRECT_REQ and
* responses are returned with FFA_MSG_SEND_DIRECT_RESP for normal
* messages.
*
* All requests with FFA_MSG_SEND_DIRECT_REQ and FFA_MSG_SEND_DIRECT_RESP
* are using the AArch32 SMC calling convention with register usage as
* defined in FF-A specification:
* w0: Function ID (0x8400006F or 0x84000070)
* w1: Source/Destination IDs
* w2: Reserved (MBZ)
* w3-w7: Implementation defined, free to be used below
*/
#define OPTEE_FFA_VERSION_MAJOR 1
#define OPTEE_FFA_VERSION_MINOR 0
#define OPTEE_FFA_BLOCKING_CALL(id) (id)
#define OPTEE_FFA_YIELDING_CALL_BIT 31
#define OPTEE_FFA_YIELDING_CALL(id) ((id) | BIT(OPTEE_FFA_YIELDING_CALL_BIT))
/*
* Returns the API version implemented, currently follows the FF-A version.
* Call register usage:
* w3: Service ID, OPTEE_FFA_GET_API_VERSION
* w4-w7: Not used (MBZ)
*
* Return register usage:
* w3: OPTEE_FFA_VERSION_MAJOR
* w4: OPTEE_FFA_VERSION_MINOR
* w5-w7: Not used (MBZ)
*/
#define OPTEE_FFA_GET_API_VERSION OPTEE_FFA_BLOCKING_CALL(0)
/*
* Returns the revision of OP-TEE.
*
* Used by non-secure world to figure out which version of the Trusted OS
* is installed. Note that the returned revision is the revision of the
* Trusted OS, not of the API.
*
* Call register usage:
* w3: Service ID, OPTEE_FFA_GET_OS_VERSION
* w4-w7: Unused (MBZ)
*
* Return register usage:
* w3: CFG_OPTEE_REVISION_MAJOR
* w4: CFG_OPTEE_REVISION_MINOR
* w5: TEE_IMPL_GIT_SHA1 (or zero if not supported)
*/
#define OPTEE_FFA_GET_OS_VERSION OPTEE_FFA_BLOCKING_CALL(1)
/*
* Exchange capabilities between normal world and secure world.
*
* Currently there are no defined capabilities. When features are added new
* capabilities may be added.
*
* Call register usage:
* w3: Service ID, OPTEE_FFA_EXCHANGE_CAPABILITIES
* w4-w7: Note used (MBZ)
*
* Return register usage:
* w3: Error code, 0 on success
* w4: Bit[7:0]: Number of parameters needed for RPC to be supplied
* as the second MSG arg struct for
* OPTEE_FFA_YIELDING_CALL_WITH_ARG.
* Bit[31:8]: Reserved (MBZ)
* w5-w7: Note used (MBZ)
*/
#define OPTEE_FFA_EXCHANGE_CAPABILITIES OPTEE_FFA_BLOCKING_CALL(2)
/*
* Unregister shared memory
*
* Call register usage:
* w3: Service ID, OPTEE_FFA_YIELDING_CALL_UNREGISTER_SHM
* w4: Shared memory handle, lower bits
* w5: Shared memory handle, higher bits
* w6-w7: Not used (MBZ)
*
* Return register usage:
* w3: Error code, 0 on success
* w4-w7: Note used (MBZ)
*/
#define OPTEE_FFA_UNREGISTER_SHM OPTEE_FFA_BLOCKING_CALL(3)
/*
* Call with struct optee_msg_arg as argument in the supplied shared memory
* with a zero internal offset and normal cached memory attributes.
* Register usage:
* w3: Service ID, OPTEE_FFA_YIELDING_CALL_WITH_ARG
* w4: Lower 32 bits of a 64-bit Shared memory handle
* w5: Upper 32 bits of a 64-bit Shared memory handle
* w6: Offset into shared memory pointing to a struct optee_msg_arg
* right after the parameters of this struct (at offset
* OPTEE_MSG_GET_ARG_SIZE(num_params) follows a struct optee_msg_arg
* for RPC, this struct has reserved space for the number of RPC
* parameters as returned by OPTEE_FFA_EXCHANGE_CAPABILITIES.
* w7: Not used (MBZ)
* Resume from RPC. Register usage:
* w3: Service ID, OPTEE_FFA_YIELDING_CALL_RESUME
* w4-w6: Not used (MBZ)
* w7: Resume info
*
* Normal return (yielding call is completed). Register usage:
* w3: Error code, 0 on success
* w4: OPTEE_FFA_YIELDING_CALL_RETURN_DONE
* w5-w7: Not used (MBZ)
*
* RPC interrupt return (RPC from secure world). Register usage:
* w3: Error code == 0
* w4: Any defined RPC code but OPTEE_FFA_YIELDING_CALL_RETURN_DONE
* w5-w6: Not used (MBZ)
* w7: Resume info
*
* Possible error codes in register w3:
* 0: Success
* FFA_DENIED: w4 isn't one of OPTEE_FFA_YIELDING_CALL_START
* OPTEE_FFA_YIELDING_CALL_RESUME
*
* Possible error codes for OPTEE_FFA_YIELDING_CALL_START,
* FFA_BUSY: Number of OP-TEE OS threads exceeded,
* try again later
* FFA_DENIED: RPC shared memory object not found
* FFA_INVALID_PARAMETER: Bad shared memory handle or offset into the memory
*
* Possible error codes for OPTEE_FFA_YIELDING_CALL_RESUME
* FFA_INVALID_PARAMETER: Bad resume info
*/
#define OPTEE_FFA_YIELDING_CALL_WITH_ARG OPTEE_FFA_YIELDING_CALL(0)
#define OPTEE_FFA_YIELDING_CALL_RESUME OPTEE_FFA_YIELDING_CALL(1)
#define OPTEE_FFA_YIELDING_CALL_RETURN_DONE 0
#define OPTEE_FFA_YIELDING_CALL_RETURN_RPC_CMD 1
#define OPTEE_FFA_YIELDING_CALL_RETURN_INTERRUPT 2
#endif /*__OPTEE_FFA_H*/
...@@ -28,6 +28,9 @@ ...@@ -28,6 +28,9 @@
#define OPTEE_MSG_ATTR_TYPE_RMEM_INPUT 0x5 #define OPTEE_MSG_ATTR_TYPE_RMEM_INPUT 0x5
#define OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT 0x6 #define OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT 0x6
#define OPTEE_MSG_ATTR_TYPE_RMEM_INOUT 0x7 #define OPTEE_MSG_ATTR_TYPE_RMEM_INOUT 0x7
#define OPTEE_MSG_ATTR_TYPE_FMEM_INPUT OPTEE_MSG_ATTR_TYPE_RMEM_INPUT
#define OPTEE_MSG_ATTR_TYPE_FMEM_OUTPUT OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT
#define OPTEE_MSG_ATTR_TYPE_FMEM_INOUT OPTEE_MSG_ATTR_TYPE_RMEM_INOUT
#define OPTEE_MSG_ATTR_TYPE_TMEM_INPUT 0x9 #define OPTEE_MSG_ATTR_TYPE_TMEM_INPUT 0x9
#define OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT 0xa #define OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT 0xa
#define OPTEE_MSG_ATTR_TYPE_TMEM_INOUT 0xb #define OPTEE_MSG_ATTR_TYPE_TMEM_INOUT 0xb
...@@ -96,6 +99,8 @@ ...@@ -96,6 +99,8 @@
*/ */
#define OPTEE_MSG_NONCONTIG_PAGE_SIZE 4096 #define OPTEE_MSG_NONCONTIG_PAGE_SIZE 4096
#define OPTEE_MSG_FMEM_INVALID_GLOBAL_ID 0xffffffffffffffff
/** /**
* struct optee_msg_param_tmem - temporary memory reference parameter * struct optee_msg_param_tmem - temporary memory reference parameter
* @buf_ptr: Address of the buffer * @buf_ptr: Address of the buffer
...@@ -127,6 +132,23 @@ struct optee_msg_param_rmem { ...@@ -127,6 +132,23 @@ struct optee_msg_param_rmem {
u64 shm_ref; u64 shm_ref;
}; };
/**
* struct optee_msg_param_fmem - ffa memory reference parameter
* @offs_lower: Lower bits of offset into shared memory reference
* @offs_upper: Upper bits of offset into shared memory reference
* @internal_offs: Internal offset into the first page of shared memory
* reference
* @size: Size of the buffer
* @global_id: Global identifier of Shared memory
*/
struct optee_msg_param_fmem {
u32 offs_low;
u16 offs_high;
u16 internal_offs;
u64 size;
u64 global_id;
};
/** /**
* struct optee_msg_param_value - opaque value parameter * struct optee_msg_param_value - opaque value parameter
* *
...@@ -143,13 +165,15 @@ struct optee_msg_param_value { ...@@ -143,13 +165,15 @@ struct optee_msg_param_value {
* @attr: attributes * @attr: attributes
* @tmem: parameter by temporary memory reference * @tmem: parameter by temporary memory reference
* @rmem: parameter by registered memory reference * @rmem: parameter by registered memory reference
* @fmem: parameter by ffa registered memory reference
* @value: parameter by opaque value * @value: parameter by opaque value
* @octets: parameter by octet string * @octets: parameter by octet string
* *
* @attr & OPTEE_MSG_ATTR_TYPE_MASK indicates if tmem, rmem or value is used in * @attr & OPTEE_MSG_ATTR_TYPE_MASK indicates if tmem, rmem or value is used in
* the union. OPTEE_MSG_ATTR_TYPE_VALUE_* indicates value or octets, * the union. OPTEE_MSG_ATTR_TYPE_VALUE_* indicates value or octets,
* OPTEE_MSG_ATTR_TYPE_TMEM_* indicates @tmem and * OPTEE_MSG_ATTR_TYPE_TMEM_* indicates @tmem and
* OPTEE_MSG_ATTR_TYPE_RMEM_* indicates @rmem, * OPTEE_MSG_ATTR_TYPE_RMEM_* or the alias PTEE_MSG_ATTR_TYPE_FMEM_* indicates
* @rmem or @fmem depending on the conduit.
* OPTEE_MSG_ATTR_TYPE_NONE indicates that none of the members are used. * OPTEE_MSG_ATTR_TYPE_NONE indicates that none of the members are used.
*/ */
struct optee_msg_param { struct optee_msg_param {
...@@ -157,6 +181,7 @@ struct optee_msg_param { ...@@ -157,6 +181,7 @@ struct optee_msg_param {
union { union {
struct optee_msg_param_tmem tmem; struct optee_msg_param_tmem tmem;
struct optee_msg_param_rmem rmem; struct optee_msg_param_rmem rmem;
struct optee_msg_param_fmem fmem;
struct optee_msg_param_value value; struct optee_msg_param_value value;
u8 octets[24]; u8 octets[24];
} u; } u;
......
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* /*
* Copyright (c) 2015, Linaro Limited * Copyright (c) 2015-2021, Linaro Limited
*/ */
#ifndef OPTEE_PRIVATE_H #ifndef OPTEE_PRIVATE_H
#define OPTEE_PRIVATE_H #define OPTEE_PRIVATE_H
#include <linux/arm-smccc.h> #include <linux/arm-smccc.h>
#include <linux/rhashtable.h>
#include <linux/semaphore.h> #include <linux/semaphore.h>
#include <linux/tee_drv.h> #include <linux/tee_drv.h>
#include <linux/types.h> #include <linux/types.h>
#include "optee_msg.h" #include "optee_msg.h"
#define DRIVER_NAME "optee"
#define OPTEE_MAX_ARG_SIZE 1024 #define OPTEE_MAX_ARG_SIZE 1024
/* Some Global Platform error codes used in this driver */ /* Some Global Platform error codes used in this driver */
...@@ -20,6 +23,7 @@ ...@@ -20,6 +23,7 @@
#define TEEC_ERROR_NOT_SUPPORTED 0xFFFF000A #define TEEC_ERROR_NOT_SUPPORTED 0xFFFF000A
#define TEEC_ERROR_COMMUNICATION 0xFFFF000E #define TEEC_ERROR_COMMUNICATION 0xFFFF000E
#define TEEC_ERROR_OUT_OF_MEMORY 0xFFFF000C #define TEEC_ERROR_OUT_OF_MEMORY 0xFFFF000C
#define TEEC_ERROR_BUSY 0xFFFF000D
#define TEEC_ERROR_SHORT_BUFFER 0xFFFF0010 #define TEEC_ERROR_SHORT_BUFFER 0xFFFF0010
#define TEEC_ORIGIN_COMMS 0x00000002 #define TEEC_ORIGIN_COMMS 0x00000002
...@@ -29,6 +33,11 @@ typedef void (optee_invoke_fn)(unsigned long, unsigned long, unsigned long, ...@@ -29,6 +33,11 @@ typedef void (optee_invoke_fn)(unsigned long, unsigned long, unsigned long,
unsigned long, unsigned long, unsigned long, unsigned long,
struct arm_smccc_res *); struct arm_smccc_res *);
struct optee_call_waiter {
struct list_head list_node;
struct completion c;
};
struct optee_call_queue { struct optee_call_queue {
/* Serializes access to this struct */ /* Serializes access to this struct */
struct mutex mutex; struct mutex mutex;
...@@ -66,19 +75,65 @@ struct optee_supp { ...@@ -66,19 +75,65 @@ struct optee_supp {
struct completion reqs_c; struct completion reqs_c;
}; };
struct optee_smc {
optee_invoke_fn *invoke_fn;
void *memremaped_shm;
u32 sec_caps;
};
/**
* struct optee_ffa_data - FFA communication struct
* @ffa_dev FFA device, contains the destination id, the id of
* OP-TEE in secure world
* @ffa_ops FFA operations
* @mutex Serializes access to @global_ids
* @global_ids FF-A shared memory global handle translation
*/
struct optee_ffa {
struct ffa_device *ffa_dev;
const struct ffa_dev_ops *ffa_ops;
/* Serializes access to @global_ids */
struct mutex mutex;
struct rhashtable global_ids;
};
struct optee;
/**
* struct optee_ops - OP-TEE driver internal operations
* @do_call_with_arg: enters OP-TEE in secure world
* @to_msg_param: converts from struct tee_param to OPTEE_MSG parameters
* @from_msg_param: converts from OPTEE_MSG parameters to struct tee_param
*
* These OPs are only supposed to be used internally in the OP-TEE driver
* as a way of abstracting the different methogs of entering OP-TEE in
* secure world.
*/
struct optee_ops {
int (*do_call_with_arg)(struct tee_context *ctx,
struct tee_shm *shm_arg);
int (*to_msg_param)(struct optee *optee,
struct optee_msg_param *msg_params,
size_t num_params, const struct tee_param *params);
int (*from_msg_param)(struct optee *optee, struct tee_param *params,
size_t num_params,
const struct optee_msg_param *msg_params);
};
/** /**
* struct optee - main service struct * struct optee - main service struct
* @supp_teedev: supplicant device * @supp_teedev: supplicant device
* @ops: internal callbacks for different ways to reach secure
* world
* @teedev: client device * @teedev: client device
* @invoke_fn: function to issue smc or hvc * @smc: specific to SMC ABI
* @ffa: specific to FF-A ABI
* @call_queue: queue of threads waiting to call @invoke_fn * @call_queue: queue of threads waiting to call @invoke_fn
* @wait_queue: queue of threads from secure world waiting for a * @wait_queue: queue of threads from secure world waiting for a
* secure world sync object * secure world sync object
* @supp: supplicant synchronization struct for RPC to supplicant * @supp: supplicant synchronization struct for RPC to supplicant
* @pool: shared memory pool * @pool: shared memory pool
* @memremaped_shm virtual address of memory in shared memory pool * @rpc_arg_count: If > 0 number of RPC parameters to make room for
* @sec_caps: secure world capabilities defined by
* OPTEE_SMC_SEC_CAP_* in optee_smc.h
* @scan_bus_done flag if device registation was already done. * @scan_bus_done flag if device registation was already done.
* @scan_bus_wq workqueue to scan optee bus and register optee drivers * @scan_bus_wq workqueue to scan optee bus and register optee drivers
* @scan_bus_work workq to scan optee bus and register optee drivers * @scan_bus_work workq to scan optee bus and register optee drivers
...@@ -86,13 +141,16 @@ struct optee_supp { ...@@ -86,13 +141,16 @@ struct optee_supp {
struct optee { struct optee {
struct tee_device *supp_teedev; struct tee_device *supp_teedev;
struct tee_device *teedev; struct tee_device *teedev;
optee_invoke_fn *invoke_fn; const struct optee_ops *ops;
union {
struct optee_smc smc;
struct optee_ffa ffa;
};
struct optee_call_queue call_queue; struct optee_call_queue call_queue;
struct optee_wait_queue wait_queue; struct optee_wait_queue wait_queue;
struct optee_supp supp; struct optee_supp supp;
struct tee_shm_pool *pool; struct tee_shm_pool *pool;
void *memremaped_shm; unsigned int rpc_arg_count;
u32 sec_caps;
bool scan_bus_done; bool scan_bus_done;
struct workqueue_struct *scan_bus_wq; struct workqueue_struct *scan_bus_wq;
struct work_struct scan_bus_work; struct work_struct scan_bus_work;
...@@ -127,10 +185,6 @@ struct optee_call_ctx { ...@@ -127,10 +185,6 @@ struct optee_call_ctx {
size_t num_entries; size_t num_entries;
}; };
void optee_handle_rpc(struct tee_context *ctx, struct optee_rpc_param *param,
struct optee_call_ctx *call_ctx);
void optee_rpc_finalize_call(struct optee_call_ctx *call_ctx);
void optee_wait_queue_init(struct optee_wait_queue *wq); void optee_wait_queue_init(struct optee_wait_queue *wq);
void optee_wait_queue_exit(struct optee_wait_queue *wq); void optee_wait_queue_exit(struct optee_wait_queue *wq);
...@@ -148,42 +202,68 @@ int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params, ...@@ -148,42 +202,68 @@ int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params,
int optee_supp_send(struct tee_context *ctx, u32 ret, u32 num_params, int optee_supp_send(struct tee_context *ctx, u32 ret, u32 num_params,
struct tee_param *param); 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, int optee_open_session(struct tee_context *ctx,
struct tee_ioctl_open_session_arg *arg, struct tee_ioctl_open_session_arg *arg,
struct tee_param *param); struct tee_param *param);
int optee_close_session_helper(struct tee_context *ctx, u32 session);
int optee_close_session(struct tee_context *ctx, u32 session); int optee_close_session(struct tee_context *ctx, u32 session);
int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg,
struct tee_param *param); struct tee_param *param);
int optee_cancel_req(struct tee_context *ctx, u32 cancel_id, u32 session); int optee_cancel_req(struct tee_context *ctx, u32 cancel_id, u32 session);
void optee_enable_shm_cache(struct optee *optee); #define PTA_CMD_GET_DEVICES 0x0
void optee_disable_shm_cache(struct optee *optee); #define PTA_CMD_GET_DEVICES_SUPP 0x1
void optee_disable_unmapped_shm_cache(struct optee *optee); int optee_enumerate_devices(u32 func);
void optee_unregister_devices(void);
int optee_pool_op_alloc_helper(struct tee_shm_pool_mgr *poolm,
struct tee_shm *shm, size_t size,
int (*shm_register)(struct tee_context *ctx,
struct tee_shm *shm,
struct page **pages,
size_t num_pages,
unsigned long start));
int optee_shm_register(struct tee_context *ctx, struct tee_shm *shm,
struct page **pages, size_t num_pages,
unsigned long start);
int optee_shm_unregister(struct tee_context *ctx, struct tee_shm *shm);
int optee_shm_register_supp(struct tee_context *ctx, struct tee_shm *shm, void optee_remove_common(struct optee *optee);
struct page **pages, size_t num_pages, int optee_open(struct tee_context *ctx, bool cap_memref_null);
unsigned long start); void optee_release(struct tee_context *ctx);
int optee_shm_unregister_supp(struct tee_context *ctx, struct tee_shm *shm); void optee_release_supp(struct tee_context *ctx);
int optee_from_msg_param(struct tee_param *params, size_t num_params, static inline void optee_from_msg_param_value(struct tee_param *p, u32 attr,
const struct optee_msg_param *msg_params); const struct optee_msg_param *mp)
int optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params, {
const struct tee_param *params); p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT +
attr - OPTEE_MSG_ATTR_TYPE_VALUE_INPUT;
p->u.value.a = mp->u.value.a;
p->u.value.b = mp->u.value.b;
p->u.value.c = mp->u.value.c;
}
u64 *optee_allocate_pages_list(size_t num_entries); static inline void optee_to_msg_param_value(struct optee_msg_param *mp,
void optee_free_pages_list(void *array, size_t num_entries); const struct tee_param *p)
void optee_fill_pages_list(u64 *dst, struct page **pages, int num_pages, {
size_t page_offset); mp->attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT + p->attr -
TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;
mp->u.value.a = p->u.value.a;
mp->u.value.b = p->u.value.b;
mp->u.value.c = p->u.value.c;
}
#define PTA_CMD_GET_DEVICES 0x0 void optee_cq_wait_init(struct optee_call_queue *cq,
#define PTA_CMD_GET_DEVICES_SUPP 0x1 struct optee_call_waiter *w);
int optee_enumerate_devices(u32 func); void optee_cq_wait_for_completion(struct optee_call_queue *cq,
struct optee_call_waiter *w);
void optee_cq_wait_final(struct optee_call_queue *cq,
struct optee_call_waiter *w);
int optee_check_mem_type(unsigned long start, size_t num_pages);
struct tee_shm *optee_get_msg_arg(struct tee_context *ctx, size_t num_params,
struct optee_msg_arg **msg_arg);
struct tee_shm *optee_rpc_cmd_alloc_suppl(struct tee_context *ctx, size_t sz);
void optee_rpc_cmd_free_suppl(struct tee_context *ctx, struct tee_shm *shm);
void optee_rpc_cmd(struct tee_context *ctx, struct optee *optee,
struct optee_msg_arg *arg);
/* /*
* Small helpers * Small helpers
...@@ -200,4 +280,10 @@ static inline void reg_pair_from_64(u32 *reg0, u32 *reg1, u64 val) ...@@ -200,4 +280,10 @@ static inline void reg_pair_from_64(u32 *reg0, u32 *reg1, u64 val)
*reg1 = val; *reg1 = val;
} }
/* Registration of the ABIs */
int optee_smc_abi_register(void);
void optee_smc_abi_unregister(void);
int optee_ffa_abi_register(void);
void optee_ffa_abi_unregister(void);
#endif /*OPTEE_PRIVATE_H*/ #endif /*OPTEE_PRIVATE_H*/
// SPDX-License-Identifier: GPL-2.0-only // SPDX-License-Identifier: GPL-2.0-only
/* /*
* Copyright (c) 2015-2016, Linaro Limited * Copyright (c) 2015-2021, Linaro Limited
*/ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/device.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/tee_drv.h> #include <linux/tee_drv.h>
#include "optee_private.h" #include "optee_private.h"
#include "optee_smc.h"
#include "optee_rpc_cmd.h" #include "optee_rpc_cmd.h"
struct wq_entry { struct wq_entry {
...@@ -55,6 +53,7 @@ static void handle_rpc_func_cmd_get_time(struct optee_msg_arg *arg) ...@@ -55,6 +53,7 @@ static void handle_rpc_func_cmd_get_time(struct optee_msg_arg *arg)
static void handle_rpc_func_cmd_i2c_transfer(struct tee_context *ctx, static void handle_rpc_func_cmd_i2c_transfer(struct tee_context *ctx,
struct optee_msg_arg *arg) struct optee_msg_arg *arg)
{ {
struct optee *optee = tee_get_drvdata(ctx->teedev);
struct tee_param *params; struct tee_param *params;
struct i2c_adapter *adapter; struct i2c_adapter *adapter;
struct i2c_msg msg = { }; struct i2c_msg msg = { };
...@@ -79,7 +78,8 @@ static void handle_rpc_func_cmd_i2c_transfer(struct tee_context *ctx, ...@@ -79,7 +78,8 @@ static void handle_rpc_func_cmd_i2c_transfer(struct tee_context *ctx,
return; return;
} }
if (optee_from_msg_param(params, arg->num_params, arg->params)) if (optee->ops->from_msg_param(optee, params, arg->num_params,
arg->params))
goto bad; goto bad;
for (i = 0; i < arg->num_params; i++) { for (i = 0; i < arg->num_params; i++) {
...@@ -122,7 +122,8 @@ static void handle_rpc_func_cmd_i2c_transfer(struct tee_context *ctx, ...@@ -122,7 +122,8 @@ static void handle_rpc_func_cmd_i2c_transfer(struct tee_context *ctx,
arg->ret = TEEC_ERROR_COMMUNICATION; arg->ret = TEEC_ERROR_COMMUNICATION;
} else { } else {
params[3].u.value.a = msg.len; params[3].u.value.a = msg.len;
if (optee_to_msg_param(arg->params, arg->num_params, params)) if (optee->ops->to_msg_param(optee, arg->params,
arg->num_params, params))
arg->ret = TEEC_ERROR_BAD_PARAMETERS; arg->ret = TEEC_ERROR_BAD_PARAMETERS;
else else
arg->ret = TEEC_SUCCESS; arg->ret = TEEC_SUCCESS;
...@@ -234,7 +235,7 @@ static void handle_rpc_func_cmd_wait(struct optee_msg_arg *arg) ...@@ -234,7 +235,7 @@ static void handle_rpc_func_cmd_wait(struct optee_msg_arg *arg)
arg->ret = TEEC_ERROR_BAD_PARAMETERS; arg->ret = TEEC_ERROR_BAD_PARAMETERS;
} }
static void handle_rpc_supp_cmd(struct tee_context *ctx, static void handle_rpc_supp_cmd(struct tee_context *ctx, struct optee *optee,
struct optee_msg_arg *arg) struct optee_msg_arg *arg)
{ {
struct tee_param *params; struct tee_param *params;
...@@ -248,20 +249,22 @@ static void handle_rpc_supp_cmd(struct tee_context *ctx, ...@@ -248,20 +249,22 @@ static void handle_rpc_supp_cmd(struct tee_context *ctx,
return; return;
} }
if (optee_from_msg_param(params, arg->num_params, arg->params)) { if (optee->ops->from_msg_param(optee, params, arg->num_params,
arg->params)) {
arg->ret = TEEC_ERROR_BAD_PARAMETERS; arg->ret = TEEC_ERROR_BAD_PARAMETERS;
goto out; goto out;
} }
arg->ret = optee_supp_thrd_req(ctx, arg->cmd, arg->num_params, params); arg->ret = optee_supp_thrd_req(ctx, arg->cmd, arg->num_params, params);
if (optee_to_msg_param(arg->params, arg->num_params, params)) if (optee->ops->to_msg_param(optee, arg->params, arg->num_params,
params))
arg->ret = TEEC_ERROR_BAD_PARAMETERS; arg->ret = TEEC_ERROR_BAD_PARAMETERS;
out: out:
kfree(params); kfree(params);
} }
static struct tee_shm *cmd_alloc_suppl(struct tee_context *ctx, size_t sz) struct tee_shm *optee_rpc_cmd_alloc_suppl(struct tee_context *ctx, size_t sz)
{ {
u32 ret; u32 ret;
struct tee_param param; struct tee_param param;
...@@ -284,103 +287,7 @@ static struct tee_shm *cmd_alloc_suppl(struct tee_context *ctx, size_t sz) ...@@ -284,103 +287,7 @@ static struct tee_shm *cmd_alloc_suppl(struct tee_context *ctx, size_t sz)
return shm; return shm;
} }
static void handle_rpc_func_cmd_shm_alloc(struct tee_context *ctx, void optee_rpc_cmd_free_suppl(struct tee_context *ctx, struct tee_shm *shm)
struct optee_msg_arg *arg,
struct optee_call_ctx *call_ctx)
{
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_RPC_SHM_TYPE_APPL:
shm = cmd_alloc_suppl(ctx, sz);
break;
case OPTEE_RPC_SHM_TYPE_KERNEL:
shm = tee_shm_alloc(ctx, sz, TEE_SHM_MAPPED | TEE_SHM_PRIV);
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;
}
sz = tee_shm_get_size(shm);
if (tee_shm_is_registered(shm)) {
struct page **pages;
u64 *pages_list;
size_t page_num;
pages = tee_shm_get_pages(shm, &page_num);
if (!pages || !page_num) {
arg->ret = TEEC_ERROR_OUT_OF_MEMORY;
goto bad;
}
pages_list = optee_allocate_pages_list(page_num);
if (!pages_list) {
arg->ret = TEEC_ERROR_OUT_OF_MEMORY;
goto bad;
}
call_ctx->pages_list = pages_list;
call_ctx->num_entries = page_num;
arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT |
OPTEE_MSG_ATTR_NONCONTIG;
/*
* In the least bits of u.tmem.buf_ptr we store buffer offset
* from 4k page, as described in OP-TEE ABI.
*/
arg->params[0].u.tmem.buf_ptr = virt_to_phys(pages_list) |
(tee_shm_get_page_offset(shm) &
(OPTEE_MSG_NONCONTIG_PAGE_SIZE - 1));
arg->params[0].u.tmem.size = tee_shm_get_size(shm);
arg->params[0].u.tmem.shm_ref = (unsigned long)shm;
optee_fill_pages_list(pages_list, pages, page_num,
tee_shm_get_page_offset(shm));
} else {
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; struct tee_param param;
...@@ -405,60 +312,9 @@ static void cmd_free_suppl(struct tee_context *ctx, struct tee_shm *shm) ...@@ -405,60 +312,9 @@ static void cmd_free_suppl(struct tee_context *ctx, struct tee_shm *shm)
optee_supp_thrd_req(ctx, OPTEE_RPC_CMD_SHM_FREE, 1, &param); optee_supp_thrd_req(ctx, OPTEE_RPC_CMD_SHM_FREE, 1, &param);
} }
static void handle_rpc_func_cmd_shm_free(struct tee_context *ctx, void optee_rpc_cmd(struct tee_context *ctx, struct optee *optee,
struct optee_msg_arg *arg) 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_RPC_SHM_TYPE_APPL:
cmd_free_suppl(ctx, shm);
break;
case OPTEE_RPC_SHM_TYPE_KERNEL:
tee_shm_free(shm);
break;
default:
arg->ret = TEEC_ERROR_BAD_PARAMETERS;
}
arg->ret = TEEC_SUCCESS;
}
static void free_pages_list(struct optee_call_ctx *call_ctx)
{
if (call_ctx->pages_list) {
optee_free_pages_list(call_ctx->pages_list,
call_ctx->num_entries);
call_ctx->pages_list = NULL;
call_ctx->num_entries = 0;
}
}
void optee_rpc_finalize_call(struct optee_call_ctx *call_ctx)
{
free_pages_list(call_ctx);
}
static void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee,
struct tee_shm *shm,
struct optee_call_ctx *call_ctx)
{ {
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) { switch (arg->cmd) {
case OPTEE_RPC_CMD_GET_TIME: case OPTEE_RPC_CMD_GET_TIME:
handle_rpc_func_cmd_get_time(arg); handle_rpc_func_cmd_get_time(arg);
...@@ -469,73 +325,12 @@ static void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee, ...@@ -469,73 +325,12 @@ static void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee,
case OPTEE_RPC_CMD_SUSPEND: case OPTEE_RPC_CMD_SUSPEND:
handle_rpc_func_cmd_wait(arg); handle_rpc_func_cmd_wait(arg);
break; break;
case OPTEE_RPC_CMD_SHM_ALLOC:
free_pages_list(call_ctx);
handle_rpc_func_cmd_shm_alloc(ctx, arg, call_ctx);
break;
case OPTEE_RPC_CMD_SHM_FREE:
handle_rpc_func_cmd_shm_free(ctx, arg);
break;
case OPTEE_RPC_CMD_I2C_TRANSFER: case OPTEE_RPC_CMD_I2C_TRANSFER:
handle_rpc_func_cmd_i2c_transfer(ctx, arg); handle_rpc_func_cmd_i2c_transfer(ctx, arg);
break; break;
default: default:
handle_rpc_supp_cmd(ctx, arg); handle_rpc_supp_cmd(ctx, optee, arg);
} }
} }
/**
* optee_handle_rpc() - handle RPC from secure world
* @ctx: context doing the RPC
* @param: value of registers for the RPC
* @call_ctx: call context. Preserved during one OP-TEE invocation
*
* Result of RPC is written back into @param.
*/
void optee_handle_rpc(struct tee_context *ctx, struct optee_rpc_param *param,
struct optee_call_ctx *call_ctx)
{
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 | TEE_SHM_PRIV);
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_FOREIGN_INTR:
/*
* A foreign interrupt was raised while secure world was
* executing, since they are handled in Linux a dummy RPC is
* performed to let Linux take the interrupt 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, call_ctx);
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;
}
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2015, Linaro Limited
* Copyright (c) 2017, EPAM Systems
*/
#include <linux/device.h>
#include <linux/dma-buf.h>
#include <linux/genalloc.h>
#include <linux/slab.h>
#include <linux/tee_drv.h>
#include "optee_private.h"
#include "optee_smc.h"
#include "shm_pool.h"
static int pool_op_alloc(struct tee_shm_pool_mgr *poolm,
struct tee_shm *shm, size_t size)
{
unsigned int order = get_order(size);
struct page *page;
int rc = 0;
page = alloc_pages(GFP_KERNEL | __GFP_ZERO, order);
if (!page)
return -ENOMEM;
shm->kaddr = page_address(page);
shm->paddr = page_to_phys(page);
shm->size = PAGE_SIZE << order;
/*
* Shared memory private to the OP-TEE driver doesn't need
* to be registered with OP-TEE.
*/
if (!(shm->flags & TEE_SHM_PRIV)) {
unsigned int nr_pages = 1 << order, i;
struct page **pages;
pages = kcalloc(nr_pages, sizeof(pages), GFP_KERNEL);
if (!pages) {
rc = -ENOMEM;
goto err;
}
for (i = 0; i < nr_pages; i++) {
pages[i] = page;
page++;
}
shm->flags |= TEE_SHM_REGISTER;
rc = optee_shm_register(shm->ctx, shm, pages, nr_pages,
(unsigned long)shm->kaddr);
kfree(pages);
if (rc)
goto err;
}
return 0;
err:
__free_pages(page, order);
return rc;
}
static void pool_op_free(struct tee_shm_pool_mgr *poolm,
struct tee_shm *shm)
{
if (!(shm->flags & TEE_SHM_PRIV))
optee_shm_unregister(shm->ctx, shm);
free_pages((unsigned long)shm->kaddr, get_order(shm->size));
shm->kaddr = NULL;
}
static void pool_op_destroy_poolmgr(struct tee_shm_pool_mgr *poolm)
{
kfree(poolm);
}
static const struct tee_shm_pool_mgr_ops pool_ops = {
.alloc = pool_op_alloc,
.free = pool_op_free,
.destroy_poolmgr = pool_op_destroy_poolmgr,
};
/**
* optee_shm_pool_alloc_pages() - create page-based allocator pool
*
* This pool is used when OP-TEE supports dymanic SHM. In this case
* command buffers and such are allocated from kernel's own memory.
*/
struct tee_shm_pool_mgr *optee_shm_pool_alloc_pages(void)
{
struct tee_shm_pool_mgr *mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
if (!mgr)
return ERR_PTR(-ENOMEM);
mgr->ops = &pool_ops;
return mgr;
}
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2015, Linaro Limited
* Copyright (c) 2016, EPAM Systems
*/
#ifndef SHM_POOL_H
#define SHM_POOL_H
#include <linux/tee_drv.h>
struct tee_shm_pool_mgr *optee_shm_pool_alloc_pages(void);
#endif
This diff is collapsed.
...@@ -197,7 +197,11 @@ int tee_session_calc_client_uuid(uuid_t *uuid, u32 connection_method, ...@@ -197,7 +197,11 @@ int tee_session_calc_client_uuid(uuid_t *uuid, u32 connection_method,
* @num_pages: number of locked pages * @num_pages: number of locked pages
* @dmabuf: dmabuf used to for exporting to user space * @dmabuf: dmabuf used to for exporting to user space
* @flags: defined by TEE_SHM_* in tee_drv.h * @flags: defined by TEE_SHM_* in tee_drv.h
* @id: unique id of a shared memory object on this device * @id: unique id of a shared memory object on this device, shared
* with user space
* @sec_world_id:
* secure world assigned id of this shared memory object, not
* used by all drivers
* *
* This pool is only supposed to be accessed directly from the TEE * This pool is only supposed to be accessed directly from the TEE
* subsystem and from drivers that implements their own shm pool manager. * subsystem and from drivers that implements their own shm pool manager.
...@@ -213,6 +217,7 @@ struct tee_shm { ...@@ -213,6 +217,7 @@ struct tee_shm {
struct dma_buf *dmabuf; struct dma_buf *dmabuf;
u32 flags; u32 flags;
int id; int id;
u64 sec_world_id;
}; };
/** /**
......
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