2019-05-29 07:17:56 -07:00
// SPDX-License-Identifier: GPL-2.0-only
2015-04-14 14:33:20 +02:00
/*
2021-03-25 15:08:50 +01:00
* Copyright ( c ) 2015 - 2021 , Linaro Limited
2015-04-14 14:33:20 +02:00
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
# include <linux/delay.h>
2020-08-14 13:12:21 +02:00
# include <linux/i2c.h>
2015-04-14 14:33:20 +02:00
# include <linux/slab.h>
# include <linux/tee_drv.h>
# include "optee_private.h"
2021-01-20 11:14:12 +01:00
# include "optee_rpc_cmd.h"
2015-04-14 14:33:20 +02:00
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 16:24:56 +02:00
ktime_get_real_ts64 ( & ts ) ;
2015-04-14 14:33:20 +02: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 18:11:02 +02:00
# if IS_REACHABLE(CONFIG_I2C)
2020-08-14 13:12:21 +02:00
static void handle_rpc_func_cmd_i2c_transfer ( struct tee_context * ctx ,
struct optee_msg_arg * arg )
{
2021-03-25 15:08:50 +01:00
struct optee * optee = tee_get_drvdata ( ctx - > teedev ) ;
2020-08-14 13:12:21 +02:00
struct tee_param * params ;
2021-01-25 12:37:52 +01:00
struct i2c_adapter * adapter ;
struct i2c_msg msg = { } ;
2020-08-14 13:12:21 +02:00
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 ;
}
2021-03-25 15:08:50 +01:00
if ( optee - > ops - > from_msg_param ( optee , params , arg - > num_params ,
arg - > params ) )
2020-08-14 13:12:21 +02:00
goto bad ;
for ( i = 0 ; i < arg - > num_params ; i + + ) {
if ( params [ i ] . attr ! = attr [ i ] )
goto bad ;
}
2021-01-25 12:37:52 +01:00
adapter = i2c_get_adapter ( params [ 0 ] . u . value . b ) ;
if ( ! adapter )
2020-08-14 13:12:21 +02:00
goto bad ;
2021-01-20 11:14:12 +01:00
if ( params [ 1 ] . u . value . a & OPTEE_RPC_I2C_FLAGS_TEN_BIT ) {
2021-01-25 12:37:52 +01:00
if ( ! i2c_check_functionality ( adapter ,
2020-08-14 13:12:21 +02:00
I2C_FUNC_10BIT_ADDR ) ) {
2021-01-25 12:37:52 +01:00
i2c_put_adapter ( adapter ) ;
2020-08-14 13:12:21 +02:00
goto bad ;
}
2021-01-25 12:37:52 +01:00
msg . flags = I2C_M_TEN ;
2020-08-14 13:12:21 +02:00
}
2021-01-25 12:37:52 +01:00
msg . addr = params [ 0 ] . u . value . c ;
msg . buf = params [ 2 ] . u . memref . shm - > kaddr ;
msg . len = params [ 2 ] . u . memref . size ;
2020-08-14 13:12:21 +02:00
switch ( params [ 0 ] . u . value . a ) {
2021-01-20 11:14:12 +01:00
case OPTEE_RPC_I2C_TRANSFER_RD :
2021-01-25 12:37:52 +01:00
msg . flags | = I2C_M_RD ;
2020-08-14 13:12:21 +02:00
break ;
2021-01-20 11:14:12 +01:00
case OPTEE_RPC_I2C_TRANSFER_WR :
2020-08-14 13:12:21 +02:00
break ;
default :
2021-01-25 12:37:52 +01:00
i2c_put_adapter ( adapter ) ;
2020-08-14 13:12:21 +02:00
goto bad ;
}
2021-01-25 12:37:52 +01:00
ret = i2c_transfer ( adapter , & msg , 1 ) ;
2020-08-14 13:12:21 +02:00
if ( ret < 0 ) {
arg - > ret = TEEC_ERROR_COMMUNICATION ;
} else {
2021-01-25 12:37:52 +01:00
params [ 3 ] . u . value . a = msg . len ;
2021-03-25 15:08:50 +01:00
if ( optee - > ops - > to_msg_param ( optee , arg - > params ,
arg - > num_params , params ) )
2020-08-14 13:12:21 +02:00
arg - > ret = TEEC_ERROR_BAD_PARAMETERS ;
else
arg - > ret = TEEC_SUCCESS ;
}
2021-01-25 12:37:52 +01:00
i2c_put_adapter ( adapter ) ;
2020-08-14 13:12:21 +02:00
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 14:33:20 +02:00
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-06-15 22:23:53 +02:00
case OPTEE_RPC_NOTIFICATION_WAIT :
if ( optee_notif_wait ( optee , arg - > params [ 0 ] . u . value . b ) )
goto bad ;
2015-04-14 14:33:20 +02:00
break ;
2021-06-15 22:23:53 +02:00
case OPTEE_RPC_NOTIFICATION_SEND :
if ( optee_notif_send ( optee , arg - > params [ 0 ] . u . value . b ) )
goto bad ;
2015-04-14 14:33:20 +02:00
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-06 00:20:32 +08:00
/* Go to interruptible sleep */
msleep_interruptible ( msec_to_wait ) ;
2015-04-14 14:33:20 +02:00
arg - > ret = TEEC_SUCCESS ;
return ;
bad :
arg - > ret = TEEC_ERROR_BAD_PARAMETERS ;
}
2021-03-25 15:08:50 +01:00
static void handle_rpc_supp_cmd ( struct tee_context * ctx , struct optee * optee ,
2015-04-14 14:33:20 +02:00
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 ;
}
2021-03-25 15:08:50 +01:00
if ( optee - > ops - > from_msg_param ( optee , params , arg - > num_params ,
arg - > params ) ) {
2015-04-14 14:33:20 +02:00
arg - > ret = TEEC_ERROR_BAD_PARAMETERS ;
goto out ;
}
arg - > ret = optee_supp_thrd_req ( ctx , arg - > cmd , arg - > num_params , params ) ;
2021-03-25 15:08:50 +01:00
if ( optee - > ops - > to_msg_param ( optee , arg - > params , arg - > num_params ,
params ) )
2015-04-14 14:33:20 +02:00
arg - > ret = TEEC_ERROR_BAD_PARAMETERS ;
out :
kfree ( params ) ;
}
2021-07-21 16:30:28 +02:00
struct tee_shm * optee_rpc_cmd_alloc_suppl ( struct tee_context * ctx , size_t sz )
2015-04-14 14:33:20 +02:00
{
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 11:14:12 +01:00
param . u . value . a = OPTEE_RPC_SHM_TYPE_APPL ;
2015-04-14 14:33:20 +02:00
param . u . value . b = sz ;
param . u . value . c = 0 ;
2021-01-20 11:14:12 +01:00
ret = optee_supp_thrd_req ( ctx , OPTEE_RPC_CMD_SHM_ALLOC , 1 , & param ) ;
2015-04-14 14:33:20 +02:00
if ( ret )
return ERR_PTR ( - ENOMEM ) ;
2016-12-23 13:13:39 +01:00
mutex_lock ( & optee - > supp . mutex ) ;
2015-04-14 14:33:20 +02: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 13:13:39 +01:00
mutex_unlock ( & optee - > supp . mutex ) ;
2015-04-14 14:33:20 +02:00
return shm ;
}
2021-07-21 16:30:28 +02:00
void optee_rpc_cmd_free_suppl ( struct tee_context * ctx , struct tee_shm * shm )
2015-04-14 14:33:20 +02:00
{
struct tee_param param ;
param . attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT ;
2021-01-20 11:14:12 +01:00
param . u . value . a = OPTEE_RPC_SHM_TYPE_APPL ;
2015-04-14 14:33:20 +02: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 11:14:12 +01:00
optee_supp_thrd_req ( ctx , OPTEE_RPC_CMD_SHM_FREE , 1 , & param ) ;
2015-04-14 14:33:20 +02:00
}
2021-07-21 16:30:28 +02:00
void optee_rpc_cmd ( struct tee_context * ctx , struct optee * optee ,
struct optee_msg_arg * arg )
2015-04-14 14:33:20 +02:00
{
switch ( arg - > cmd ) {
2021-01-20 11:14:12 +01:00
case OPTEE_RPC_CMD_GET_TIME :
2015-04-14 14:33:20 +02:00
handle_rpc_func_cmd_get_time ( arg ) ;
break ;
2021-06-15 22:23:53 +02:00
case OPTEE_RPC_CMD_NOTIFICATION :
2015-04-14 14:33:20 +02:00
handle_rpc_func_cmd_wq ( optee , arg ) ;
break ;
2021-01-20 11:14:12 +01:00
case OPTEE_RPC_CMD_SUSPEND :
2015-04-14 14:33:20 +02:00
handle_rpc_func_cmd_wait ( arg ) ;
break ;
2021-01-20 11:14:12 +01:00
case OPTEE_RPC_CMD_I2C_TRANSFER :
2020-08-14 13:12:21 +02:00
handle_rpc_func_cmd_i2c_transfer ( ctx , arg ) ;
break ;
2015-04-14 14:33:20 +02:00
default :
2021-03-25 15:08:50 +01:00
handle_rpc_supp_cmd ( ctx , optee , arg ) ;
2015-04-14 14:33:20 +02:00
}
}