2019-05-29 17:17:56 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2015-04-14 15:33:20 +03:00
/*
* Copyright ( c ) 2015 - 2016 , Linaro Limited
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
# include <linux/delay.h>
# include <linux/device.h>
2020-08-14 14:12:21 +03:00
# include <linux/i2c.h>
2015-04-14 15:33:20 +03:00
# include <linux/slab.h>
# include <linux/tee_drv.h>
# include "optee_private.h"
# include "optee_smc.h"
2021-01-20 13:14:12 +03:00
# include "optee_rpc_cmd.h"
2015-04-14 15:33:20 +03:00
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 ;
2018-06-18 17:24:56 +03:00
ktime_get_real_ts64 ( & ts ) ;
2015-04-14 15:33:20 +03:00
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 ;
}
2020-08-31 19:11:02 +03:00
# if IS_REACHABLE(CONFIG_I2C)
2020-08-14 14:12:21 +03:00
static void handle_rpc_func_cmd_i2c_transfer ( struct tee_context * ctx ,
struct optee_msg_arg * arg )
{
struct i2c_client client = { 0 } ;
struct tee_param * params ;
size_t i ;
int ret = - EOPNOTSUPP ;
u8 attr [ ] = {
TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT ,
TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT ,
TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT ,
TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT ,
} ;
if ( arg - > num_params ! = ARRAY_SIZE ( attr ) ) {
arg - > ret = TEEC_ERROR_BAD_PARAMETERS ;
return ;
}
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 ) )
goto bad ;
for ( i = 0 ; i < arg - > num_params ; i + + ) {
if ( params [ i ] . attr ! = attr [ i ] )
goto bad ;
}
client . adapter = i2c_get_adapter ( params [ 0 ] . u . value . b ) ;
if ( ! client . adapter )
goto bad ;
2021-01-20 13:14:12 +03:00
if ( params [ 1 ] . u . value . a & OPTEE_RPC_I2C_FLAGS_TEN_BIT ) {
2020-08-14 14:12:21 +03:00
if ( ! i2c_check_functionality ( client . adapter ,
I2C_FUNC_10BIT_ADDR ) ) {
i2c_put_adapter ( client . adapter ) ;
goto bad ;
}
client . flags = I2C_CLIENT_TEN ;
}
client . addr = params [ 0 ] . u . value . c ;
snprintf ( client . name , I2C_NAME_SIZE , " i2c%d " , client . adapter - > nr ) ;
switch ( params [ 0 ] . u . value . a ) {
2021-01-20 13:14:12 +03:00
case OPTEE_RPC_I2C_TRANSFER_RD :
2020-08-14 14:12:21 +03:00
ret = i2c_master_recv ( & client , params [ 2 ] . u . memref . shm - > kaddr ,
params [ 2 ] . u . memref . size ) ;
break ;
2021-01-20 13:14:12 +03:00
case OPTEE_RPC_I2C_TRANSFER_WR :
2020-08-14 14:12:21 +03:00
ret = i2c_master_send ( & client , params [ 2 ] . u . memref . shm - > kaddr ,
params [ 2 ] . u . memref . size ) ;
break ;
default :
i2c_put_adapter ( client . adapter ) ;
goto bad ;
}
if ( ret < 0 ) {
arg - > ret = TEEC_ERROR_COMMUNICATION ;
} else {
params [ 3 ] . u . value . a = ret ;
if ( optee_to_msg_param ( arg - > params , arg - > num_params , params ) )
arg - > ret = TEEC_ERROR_BAD_PARAMETERS ;
else
arg - > ret = TEEC_SUCCESS ;
}
i2c_put_adapter ( client . adapter ) ;
kfree ( params ) ;
return ;
bad :
kfree ( params ) ;
arg - > ret = TEEC_ERROR_BAD_PARAMETERS ;
}
# else
static void handle_rpc_func_cmd_i2c_transfer ( struct tee_context * ctx ,
struct optee_msg_arg * arg )
{
arg - > ret = TEEC_ERROR_NOT_SUPPORTED ;
}
# endif
2015-04-14 15:33:20 +03:00
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 ) {
2021-01-20 13:14:12 +03:00
case OPTEE_RPC_WAIT_QUEUE_SLEEP :
2015-04-14 15:33:20 +03:00
wq_sleep ( & optee - > wait_queue , arg - > params [ 0 ] . u . value . b ) ;
break ;
2021-01-20 13:14:12 +03:00
case OPTEE_RPC_WAIT_QUEUE_WAKEUP :
2015-04-14 15:33:20 +03:00
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 ;
2017-05-05 19:20:32 +03:00
/* Go to interruptible sleep */
msleep_interruptible ( msec_to_wait ) ;
2015-04-14 15:33:20 +03:00
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 ;
2021-01-20 13:14:12 +03:00
param . u . value . a = OPTEE_RPC_SHM_TYPE_APPL ;
2015-04-14 15:33:20 +03:00
param . u . value . b = sz ;
param . u . value . c = 0 ;
2021-01-20 13:14:12 +03:00
ret = optee_supp_thrd_req ( ctx , OPTEE_RPC_CMD_SHM_ALLOC , 1 , & param ) ;
2015-04-14 15:33:20 +03:00
if ( ret )
return ERR_PTR ( - ENOMEM ) ;
2016-12-23 15:13:39 +03:00
mutex_lock ( & optee - > supp . mutex ) ;
2015-04-14 15:33:20 +03:00
/* Increases count as secure world doesn't have a reference */
shm = tee_shm_get_from_id ( optee - > supp . ctx , param . u . value . c ) ;
2016-12-23 15:13:39 +03:00
mutex_unlock ( & optee - > supp . mutex ) ;
2015-04-14 15:33:20 +03:00
return shm ;
}
static void handle_rpc_func_cmd_shm_alloc ( struct tee_context * ctx ,
2017-11-29 15:48:33 +03:00
struct optee_msg_arg * arg ,
struct optee_call_ctx * call_ctx )
2015-04-14 15:33:20 +03:00
{
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 ) {
2021-01-20 13:14:12 +03:00
case OPTEE_RPC_SHM_TYPE_APPL :
2015-04-14 15:33:20 +03:00
shm = cmd_alloc_suppl ( ctx , sz ) ;
break ;
2021-01-20 13:14:12 +03:00
case OPTEE_RPC_SHM_TYPE_KERNEL :
2015-04-14 15:33:20 +03:00
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 ;
}
2017-11-29 15:48:33 +03:00
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 4 k 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 ;
}
2015-04-14 15:33:20 +03:00
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 ;
2021-01-20 13:14:12 +03:00
param . u . value . a = OPTEE_RPC_SHM_TYPE_APPL ;
2015-04-14 15:33:20 +03:00
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 ) ;
2021-01-20 13:14:12 +03:00
optee_supp_thrd_req ( ctx , OPTEE_RPC_CMD_SHM_FREE , 1 , & param ) ;
2015-04-14 15:33:20 +03:00
}
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 ) {
2021-01-20 13:14:12 +03:00
case OPTEE_RPC_SHM_TYPE_APPL :
2015-04-14 15:33:20 +03:00
cmd_free_suppl ( ctx , shm ) ;
break ;
2021-01-20 13:14:12 +03:00
case OPTEE_RPC_SHM_TYPE_KERNEL :
2015-04-14 15:33:20 +03:00
tee_shm_free ( shm ) ;
break ;
default :
arg - > ret = TEEC_ERROR_BAD_PARAMETERS ;
}
arg - > ret = TEEC_SUCCESS ;
}
2017-11-29 15:48:33 +03:00
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 ) ;
}
2015-04-14 15:33:20 +03:00
static void handle_rpc_func_cmd ( struct tee_context * ctx , struct optee * optee ,
2017-11-29 15:48:33 +03:00
struct tee_shm * shm ,
struct optee_call_ctx * call_ctx )
2015-04-14 15:33:20 +03:00
{
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 ) {
2021-01-20 13:14:12 +03:00
case OPTEE_RPC_CMD_GET_TIME :
2015-04-14 15:33:20 +03:00
handle_rpc_func_cmd_get_time ( arg ) ;
break ;
2021-01-20 13:14:12 +03:00
case OPTEE_RPC_CMD_WAIT_QUEUE :
2015-04-14 15:33:20 +03:00
handle_rpc_func_cmd_wq ( optee , arg ) ;
break ;
2021-01-20 13:14:12 +03:00
case OPTEE_RPC_CMD_SUSPEND :
2015-04-14 15:33:20 +03:00
handle_rpc_func_cmd_wait ( arg ) ;
break ;
2021-01-20 13:14:12 +03:00
case OPTEE_RPC_CMD_SHM_ALLOC :
2017-11-29 15:48:33 +03:00
free_pages_list ( call_ctx ) ;
handle_rpc_func_cmd_shm_alloc ( ctx , arg , call_ctx ) ;
2015-04-14 15:33:20 +03:00
break ;
2021-01-20 13:14:12 +03:00
case OPTEE_RPC_CMD_SHM_FREE :
2015-04-14 15:33:20 +03:00
handle_rpc_func_cmd_shm_free ( ctx , arg ) ;
break ;
2021-01-20 13:14:12 +03:00
case OPTEE_RPC_CMD_I2C_TRANSFER :
2020-08-14 14:12:21 +03:00
handle_rpc_func_cmd_i2c_transfer ( ctx , arg ) ;
break ;
2015-04-14 15:33:20 +03:00
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
2017-11-29 15:48:33 +03:00
* @ call_ctx : call context . Preserved during one OP - TEE invocation
2015-04-14 15:33:20 +03:00
*
* Result of RPC is written back into @ param .
*/
2017-11-29 15:48:33 +03:00
void optee_handle_rpc ( struct tee_context * ctx , struct optee_rpc_param * param ,
struct optee_call_ctx * call_ctx )
2015-04-14 15:33:20 +03:00
{
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 ;
2017-02-16 11:43:44 +03:00
case OPTEE_SMC_RPC_FUNC_FOREIGN_INTR :
2015-04-14 15:33:20 +03:00
/*
2017-02-16 11:43:44 +03:00
* 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
2015-04-14 15:33:20 +03:00
* vector .
*/
break ;
case OPTEE_SMC_RPC_FUNC_CMD :
shm = reg_pair_to_ptr ( param - > a1 , param - > a2 ) ;
2017-11-29 15:48:33 +03:00
handle_rpc_func_cmd ( ctx , optee , shm , call_ctx ) ;
2015-04-14 15:33:20 +03:00
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 ;
}