blob: 7edca0f90d1117840ce6076c87bfa070abcf4071 [file] [log] [blame] [edit]
/**
* Copyright (c) 2011 Trusted Logic S.A.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include "tf_defs.h"
#include "tf_util.h"
#include "tf_zebra.h"
#include "tf_crypto.h"
#include "tf_dma.h"
#define IO_ADDRESS OMAP2_L4_IO_ADDRESS
#define S_SUCCESS 0x00000000
#define S_ERROR_GENERIC 0xFFFF0000
#define S_ERROR_ACCESS_DENIED 0xFFFF0001
#define S_ERROR_BAD_FORMAT 0xFFFF0005
#define S_ERROR_BAD_PARAMETERS 0xFFFF0006
#define S_ERROR_OUT_OF_MEMORY 0xFFFF000C
#define S_ERROR_SHORT_BUFFER 0xFFFF0010
#define S_ERROR_UNREACHABLE 0xFFFF3013
#define S_ERROR_SERVICE 0xFFFF1000
#define CKR_OK 0x00000000
#define PUBLIC_CRYPTO_TIMEOUT_CONST 0x000FFFFF
#define RPC_AES1_CODE PUBLIC_CRYPTO_HWA_AES1
#define RPC_DES_CODE PUBLIC_CRYPTO_HWA_DES
#define RPC_SHA_CODE PUBLIC_CRYPTO_HWA_SHA
#define RPC_CRYPTO_COMMAND_MASK 0x000003c0
#define RPC_INSTALL_SHORTCUT_LOCK_ACCELERATOR 0x200
#define RPC_INSTALL_SHORTCUT_LOCK_ACCELERATOR_UNLOCK 0x000
#define RPC_INSTALL_SHORTCUT_LOCK_ACCELERATOR_LOCK 0x001
#define RPC_LOCK_ACCELERATORS_SUSPEND_SHORTCUT 0x240
#define RPC_LOCK_ACCELERATORS_SUSPEND_SHORTCUT_LOCK_AES1 RPC_AES1_CODE
#define RPC_LOCK_ACCELERATORS_SUSPEND_SHORTCUT_LOCK_DES RPC_DES_CODE
#define RPC_LOCK_ACCELERATORS_SUSPEND_SHORTCUT_LOCK_SHA RPC_SHA_CODE
#define RPC_LOCK_ACCELERATORS_SUSPEND_SHORTCUT_SUSPEND 0x010
#define RPC_LOCK_ACCELERATORS_SUSPEND_SHORTCUT_UNINSTALL 0x020
#define RPC_RESUME_SHORTCUT_UNLOCK_ACCELERATORS 0x280
#define RPC_RESUME_SHORTCUT_UNLOCK_ACCELERATORS_UNLOCK_AES1 RPC_AES1_CODE
#define RPC_RESUME_SHORTCUT_UNLOCK_ACCELERATORS_UNLOCK_DES RPC_DES_CODE
#define RPC_RESUME_SHORTCUT_UNLOCK_ACCELERATORS_UNLOCK_SHA RPC_SHA_CODE
#define RPC_RESUME_SHORTCUT_UNLOCK_ACCELERATORS_RESUME 0x010
#define RPC_CLEAR_GLOBAL_KEY_CONTEXT 0x2c0
#define RPC_CLEAR_GLOBAL_KEY_CONTEXT_CLEARED_AES 0x001
#define RPC_CLEAR_GLOBAL_KEY_CONTEXT_CLEARED_DES 0x002
#define ENABLE_CLOCK true
#define DISABLE_CLOCK false
/*---------------------------------------------------------------------------*/
/*RPC IN/OUT structures for CUS implementation */
/*---------------------------------------------------------------------------*/
struct rpc_install_shortcut_lock_accelerator_out {
u32 shortcut_id;
u32 error;
};
struct rpc_install_shortcut_lock_accelerator_in {
u32 device_context_id;
u32 client_session;
u32 command_id;
u32 key_context;
/**
*The identifier of the HWA accelerator that this shortcut uses!
*Possible values are:
*- 1 (RPC_AES1_CODE)
*- 4 (RPC_DES_CODE)
*- 8 (RPC_SHA_CODE)
**/
u32 hwa_id;
/**
*This field defines the algorithm, direction, mode, key size.
*It contains some of the bits of the corresponding "CTRL" register
*of the accelerator.
*
*More precisely:
*For AES1 accelerator, hwa_ctrl contains the following bits:
*- CTR (bit 6):
* when 1, selects CTR mode.
* when 0, selects CBC or ECB mode (according to CBC bit)
*- CBC (bit 5)
* when 1, selects CBC mode (but only if CTR=0)
* when 0, selects EBC mode (but only if CTR=0)
*- DIRECTION (bit 2)
* 0: decryption
* 1: encryption
*
*For the DES2 accelerator, hwa_ctrl contains the following bits:
*- CBC (bit 4): 1 for CBC, 0 for ECB
*- DIRECTION (bit 2): 0 for decryption, 1 for encryption
*
*For the SHA accelerator, hwa_ctrl contains the following bits:
*- ALGO (bit 2:1):
* 0x0: MD5
* 0x1: SHA1
* 0x2: SHA-224
* 0x3: SHA-256
**/
u32 hwa_ctrl;
union tf_crypto_operation_state operation_state;
};
struct rpc_lock_hwa_suspend_shortcut_out {
union tf_crypto_operation_state operation_state;
};
struct rpc_lock_hwa_suspend_shortcut_in {
u32 shortcut_id;
};
struct rpc_resume_shortcut_unlock_hwa_in {
u32 shortcut_id;
u32 aes1_key_context;
u32 reserved;
u32 des_key_context;
union tf_crypto_operation_state operation_state;
};
/*------------------------------------------------------------------------- */
/*
* tf_get_device_context(struct cus_context *cus)
* search in the all the device context (connection_list) if the CUS context
* specified by cus exist.
*
* If it is found, return the device context where the CUS context is.
* If is is not found, return NULL.
*/
static struct tf_connection *tf_get_device_context(
struct cus_context *cus)
{
struct tf_connection *connection = NULL;
struct cus_context *cusFromList = NULL;
struct tf_device *dev = tf_get_device();
spin_lock(&(dev->connection_list_lock));
list_for_each_entry(connection, &(dev->connection_list),
list) {
spin_lock(&(connection->shortcut_list_lock));
list_for_each_entry(cusFromList,
&(connection->shortcut_list), list) {
if ((u32)cusFromList == (u32)cus) {
spin_unlock(&(connection->
shortcut_list_lock));
spin_unlock(&(dev->
connection_list_lock));
return connection;
}
}
spin_unlock(&(connection->
shortcut_list_lock));
}
spin_unlock(&(dev->connection_list_lock));
/*cus does not exist */
return NULL;
}
/*------------------------------------------------------------------------- */
/*
* Get the shared memory from the memory block handle coming from secure.
* Return NULL if it does not exist.
*/
static struct tf_shmem_desc *tf_get_shmem_from_block_handle(
struct tf_connection *connection, u32 block)
{
struct tf_shmem_desc *shmem_desc = NULL;
mutex_lock(&(connection->shmem_mutex));
list_for_each_entry(shmem_desc,
&(connection->used_shmem_list), list) {
if ((u32) shmem_desc->block_identifier ==
(u32) block) {
mutex_unlock(&(connection->shmem_mutex));
return shmem_desc;
}
}
/* block does not exist */
mutex_unlock(&(connection->shmem_mutex));
return NULL;
}
/*------------------------------------------------------------------------- */
/*
* HWA public lock or unlock one HWA according algo specified by hwa_id
*/
void tf_crypto_lock_hwa(u32 hwa_id, bool do_lock)
{
struct semaphore *s = NULL;
struct tf_device *dev = tf_get_device();
dprintk(KERN_INFO "[pid=%d] %s: hwa_id=0x%04X do_lock=%d\n",
current->pid, __func__, hwa_id, do_lock);
switch (hwa_id) {
case RPC_AES1_CODE:
s = &dev->aes1_sema;
break;
case RPC_DES_CODE:
s = &dev->des_sema;
break;
default:
case RPC_SHA_CODE:
s = &dev->sha_sema;
break;
}
if (do_lock == LOCK_HWA) {
dprintk(KERN_INFO "tf_crypto_lock_hwa: "
"Wait for HWAID=0x%04X\n", hwa_id);
while (down_trylock(s))
cpu_relax();
dprintk(KERN_INFO "tf_crypto_lock_hwa: "
"Locked on HWAID=0x%04X\n", hwa_id);
} else {
up(s);
dprintk(KERN_INFO "tf_crypto_lock_hwa: "
"Released for HWAID=0x%04X\n", hwa_id);
}
}
/*------------------------------------------------------------------------- */
/*
* HWAs public lock or unlock HWA's specified in the HWA H/A/D fields of RPC
* command rpc_command
*/
static void tf_crypto_lock_hwas(u32 rpc_command, bool do_lock)
{
dprintk(KERN_INFO
"tf_crypto_lock_hwas: rpc_command=0x%08x do_lock=%d\n",
rpc_command, do_lock);
/* perform the locks */
if (rpc_command & RPC_AES1_CODE)
tf_crypto_lock_hwa(RPC_AES1_CODE, do_lock);
if (rpc_command & RPC_DES_CODE)
tf_crypto_lock_hwa(RPC_DES_CODE, do_lock);
if (rpc_command & RPC_SHA_CODE)
tf_crypto_lock_hwa(RPC_SHA_CODE, do_lock);
}
/*------------------------------------------------------------------------- */
/**
*Initialize the public crypto DMA channels, global HWA semaphores and handles
*/
u32 tf_crypto_init(void)
{
struct tf_device *dev = tf_get_device();
u32 error = PUBLIC_CRYPTO_OPERATION_SUCCESS;
/* Initialize HWAs */
tf_aes_init();
tf_des_init();
tf_digest_init();
/*initialize the HWA semaphores */
sema_init(&dev->aes1_sema, 1);
sema_init(&dev->des_sema, 1);
sema_init(&dev->sha_sema, 1);
/*initialize the current key handle loaded in the AESn/DES HWA */
dev->aes1_key_context = 0;
dev->des_key_context = 0;
dev->sham1_is_public = false;
/*initialize the DMA semaphores */
mutex_init(&dev->sm.dma_mutex);
/*allocate DMA buffer */
dev->dma_buffer_length = PAGE_SIZE * 16;
dev->dma_buffer = dma_alloc_coherent(NULL,
dev->dma_buffer_length,
&(dev->dma_buffer_phys),
GFP_KERNEL);
if (dev->dma_buffer == NULL) {
printk(KERN_ERR
"tf_crypto_init: Out of memory for DMA buffer\n");
error = S_ERROR_OUT_OF_MEMORY;
}
return error;
}
/*------------------------------------------------------------------------- */
/*
*Initialize the device context CUS fields (shortcut semaphore and public CUS
*list)
*/
void tf_crypto_init_cus(struct tf_connection *connection)
{
/*initialize the CUS list in the given device context */
spin_lock_init(&(connection->shortcut_list_lock));
INIT_LIST_HEAD(&(connection->shortcut_list));
}
/*------------------------------------------------------------------------- */
/**
*Terminate the public crypto (including DMA)
*/
void tf_crypto_terminate(void)
{
struct tf_device *dev = tf_get_device();
if (dev->dma_buffer != NULL) {
dma_free_coherent(NULL, dev->dma_buffer_length,
dev->dma_buffer,
dev->dma_buffer_phys);
dev->dma_buffer = NULL;
}
tf_digest_exit();
tf_des_exit();
tf_aes_exit();
}
/*------------------------------------------------------------------------- */
/*
*Perform a crypto update operation.
*THIS FUNCTION IS CALLED FROM THE IOCTL
*/
static bool tf_crypto_update(
struct cus_context *cus,
struct cus_params *params)
{
bool status = true;
dprintk(KERN_INFO
"tf_crypto_update(%x): "\
"HWAID=0x%x, In=%p, Out=%p, Len=%u\n",
(uint32_t) cus, cus->hwa_id,
params->input_data,
params->output_data, params->input_data_length);
/* Enable the clock and Process Data */
switch (cus->hwa_id) {
case RPC_AES1_CODE:
tf_crypto_enable_clock(PUBLIC_CRYPTO_AES1_CLOCK_REG);
cus->operation_state.aes.key_is_public = 0;
cus->operation_state.aes.CTRL = cus->hwa_ctrl;
status = tf_aes_update(
&cus->operation_state.aes,
params->input_data,
params->output_data,
params->input_data_length / AES_BLOCK_SIZE);
tf_crypto_disable_clock(PUBLIC_CRYPTO_AES1_CLOCK_REG);
break;
case RPC_DES_CODE:
tf_crypto_enable_clock(PUBLIC_CRYPTO_DES3DES_CLOCK_REG);
status = tf_des_update(
cus->hwa_ctrl,
&cus->operation_state.des,
params->input_data,
params->output_data,
params->input_data_length / DES_BLOCK_SIZE);
tf_crypto_disable_clock(PUBLIC_CRYPTO_DES3DES_CLOCK_REG);
break;
case RPC_SHA_CODE:
tf_crypto_enable_clock(PUBLIC_CRYPTO_SHA2MD5_CLOCK_REG);
cus->operation_state.sha.CTRL = cus->hwa_ctrl;
status = tf_digest_update(
&cus->operation_state.sha,
params->input_data,
params->input_data_length);
tf_crypto_disable_clock(PUBLIC_CRYPTO_SHA2MD5_CLOCK_REG);
break;
default:
BUG_ON(1);
break;
}
dprintk(KERN_INFO "tf_crypto_update: Done\n");
return status;
}
/*------------------------------------------------------------------------- */
/*
*Check if the command must be intercepted by a CUS or not.
*THIS FUNCTION IS CALLED FROM THE USER THREAD (ioctl).
*
*inputs: struct tf_connection *connection : current device context
* tf_command_invoke_client_command *command : the command
* bool incrementuse_count : specify if the use_count must be incremented
*output:
* struct cus_context **cus_ctx : the public CUS
* if it is shortcuted
*return: true or false
*
*/
static bool tf_crypto_is_shortcuted_command(
struct tf_connection *connection,
struct tf_command_invoke_client_command *command,
struct cus_context **cus_ctx,
bool incrementuse_count)
{
struct tf_device *dev = tf_get_device();
struct cus_context *cus = NULL;
*cus_ctx = NULL;
dprintk(KERN_INFO "tf_crypto_is_shortcuted_command: "\
"connection=0x%08x, command=0x%08x, "\
"CltSession=0x%08x, CmdID=0x%08x\n",
(uint32_t) connection, (uint32_t) command,
(uint32_t) command->client_session,
command->client_command_identifier);
/*take shortcut_list_lock for the device context
*in which the message is sent <=> make sure that nobody is
*going to change data while processing */
spin_lock(&(connection->shortcut_list_lock));
/*lookup in the list of shortcuts attached to the device context for a
*shortcut context that contains the same client_session as the command
*and such that command_id is equal to client_command_identifier of the
*INVOKE_CLIENT_COMMAND message. If no such shortcut exists, take the
*standard path */
list_for_each_entry(
cus, &(connection->shortcut_list), list) {
dprintk(KERN_INFO
"tf_crypto_is_shortcuted_command: "\
"command_id = 0x%08x client_session = 0x%08x\n",
cus->command_id, cus->client_session);
if ((cus->client_session == command->client_session)
&&
(cus->command_id == command->
client_command_identifier)) {
dprintk(KERN_INFO
"tf_crypto_is_shortcuted_command: "\
"shortcut is identified\n");
/*find a CUS : check if is suspended or not */
if (cus->suspended) {
/*
* suspended of the shortcut context is set to
* true, it means that the secure world has
* suspended the shortcut to perform an update
* on its own. In this case, take the standard
* path. This should happen very rarely because
* the client and the service should generally
* communicate to avoid such a collision
*/
dprintk(KERN_INFO "shortcut exists but "\
"suspended\n");
goto command_not_shortcutable;
} else {
dprintk(KERN_INFO "shortcut exists\n");
/*For AES and DES/3DES operations,
*provisionally determine if the accelerator
*is loaded with the appropriate key before
*deciding to enter the accelerator critical
*section. In most cases, if some other thread
*or the secure world is currently using the
*accelerator, the key won't change.
*So, if the key doesn't match now, it is
*likely not to match later on, so we'd better
*not try to enter the critical section in this
*case: */
if (cus->hwa_id == RPC_AES1_CODE &&
cus->
key_context != dev->
aes1_key_context) {
/*For AES operations, atomically read
*g_hAES1SSecureKeyContext and check if
*it is equal to key_context. If not,
*take the standard path <=> do not
*shortcut */
dprintk(KERN_INFO
"shortcut exists but AES key "\
"not correct\nkey_context="\
"0x%08x vs 0x%08x\n",
cus->key_context,
dev->
aes1_key_context);
goto command_not_shortcutable;
} else if (cus->hwa_id == RPC_DES_CODE
&& cus->key_context !=
dev->
des_key_context) {
/*
* For DES/3DES atomically read
* des_key_context and check if
* it is equal to key_context. If not,
* take the standard path <=> do not
* shortcut
*/
dprintk(KERN_INFO
"shortcut exists but DES key "
"not correct "
"des_key_context = 0x%08x"
" key_context0x%08x\n",
(u32)dev->
des_key_context,
(u32)cus->key_context);
goto command_not_shortcutable;
} else if (cus->hwa_id == RPC_SHA_CODE
&& !dev->sham1_is_public) {
/*
* For digest operations, atomically
* read sham1_is_public and check if it
* is true. If not, no shortcut.
*/
dprintk(KERN_INFO
"shortcut exists but SHAM1 "
"is not accessible in public");
goto command_not_shortcutable;
}
}
dprintk(KERN_INFO "shortcut exists and enable\n");
/*Shortcut has been found and context fits with
*thread => YES! the command can be shortcuted */
/*
*set the pointer on the corresponding session
*(eq CUS context)
*/
*cus_ctx = cus;
/*
*increment use_count if required
*/
if (incrementuse_count)
cus->use_count++;
/*
*release shortcut_list_lock
*/
spin_unlock(&(connection->
shortcut_list_lock));
return true;
}
}
command_not_shortcutable:
/*
*release shortcut_list_lock
*/
spin_unlock(&(connection->shortcut_list_lock));
*cus_ctx = NULL;
return false;
}
/*------------------------------------------------------------------------- */
/*
* Pre-process the client command (crypto update operation), i.e., parse the
* command message (decode buffers, etc.) THIS FUNCTION IS CALLED FROM THE USER
* THREAD (ioctl).
*
* For incorrect messages, an error is returned and the message will be sent to
* secure
*/
static bool tf_crypto_parse_command_message(struct tf_connection *connection,
struct cus_context *cus,
struct tf_command_invoke_client_command *command,
struct cus_params *params)
{
u32 param_type;
u32 input_data_length;
u32 output_data_length;
u8 *input_data;
u8 *output_data;
struct tf_shmem_desc *input_shmem = NULL;
struct tf_shmem_desc *output_shmem = NULL;
dprintk(KERN_INFO
"tf_crypto_parse_command_message(%p) : Session=0x%x\n",
cus, cus->client_session);
if (command->params[0].temp_memref.size == 0)
return false;
param_type = TF_GET_PARAM_TYPE(command->param_types, 0);
switch (param_type) {
case TF_PARAM_TYPE_MEMREF_TEMP_INPUT:
if (command->params[0].temp_memref.descriptor == 0)
return false;
input_data = (u8 *) command->params[0].temp_memref.
descriptor;
input_data_length = command->params[0].temp_memref.size;
break;
case TF_PARAM_TYPE_MEMREF_INPUT:
input_shmem = tf_get_shmem_from_block_handle(connection,
command->params[0].memref.block);
if (input_shmem == NULL)
return false;
atomic_inc(&input_shmem->ref_count);
input_data = input_shmem->pBuffer +
command->params[0].memref.offset;
input_data_length = command->params[0].memref.size;
break;
default:
return false;
}
if (cus->hwa_id != RPC_SHA_CODE) {
if (command->params[1].temp_memref.size == 0)
goto err0;
/* We need an output buffer as well */
param_type = TF_GET_PARAM_TYPE(command->param_types, 1);
switch (param_type) {
case TF_PARAM_TYPE_MEMREF_TEMP_OUTPUT:
output_data =
(u8 *) command->params[1].temp_memref.
descriptor;
output_data_length =
command->params[1].temp_memref.size;
break;
case TF_PARAM_TYPE_MEMREF_OUTPUT:
if (command->params[1].temp_memref.descriptor == 0)
return false;
output_shmem = tf_get_shmem_from_block_handle(
connection, command->params[1].memref.block);
if (output_shmem == NULL)
goto err0;
atomic_inc(&output_shmem->ref_count);
output_data = output_shmem->pBuffer +
command->params[1].memref.offset;
output_data_length = command->params[1].memref.size;
break;
default:
dprintk(KERN_ERR "tf_crypto_parse_command_message: "
"Encrypt/decrypt operations require an output "
"buffer\n");
goto err0;
}
if (output_data_length < input_data_length) {
dprintk(KERN_ERR "tf_crypto_parse_command_message: "
"Short buffer: output_data_length = %d < "
"input_data_length = %d\n",
output_data_length, input_data_length);
goto err1;
}
} else {
output_data_length = 0;
output_data = NULL;
}
/*
* Check if input length is compatible with the algorithm of the
* shortcut
*/
switch (cus->hwa_id) {
case RPC_AES1_CODE:
/* Must be multiple of the AES block size */
if ((input_data_length % AES_BLOCK_SIZE) != 0) {
dprintk(KERN_ERR
"tf_crypto_parse_command_message(%p): "\
"Input Data Length invalid [%d] for AES\n",
cus, input_data_length);
goto err1;
}
break;
case RPC_DES_CODE:
/* Must be multiple of the DES block size */
if ((input_data_length % DES_BLOCK_SIZE) != 0) {
dprintk(KERN_ERR
"tf_crypto_parse_command_message(%p): "\
"Input Data Length invalid [%d] for DES\n",
cus, input_data_length);
goto err1;
}
break;
default:
/* SHA operation: no constraint on data length */
break;
}
params->input_data = input_data;
params->input_data_length = input_data_length;
params->input_shmem = input_shmem;
params->output_data = output_data;
params->output_data_length = output_data_length;
params->output_shmem = output_shmem;
return true;
err1:
if (output_shmem)
atomic_dec(&output_shmem->ref_count);
err0:
if (input_shmem)
atomic_dec(&input_shmem->ref_count);
return false;
}
/*------------------------------------------------------------------------- */
/*
*Post-process the client command (crypto update operation),
*i.e. copy the result into the user output buffer and release the resources.
*THIS FUNCTION IS CALLED FROM THE USER THREAD (ioctl).
*/
static void tf_crypto_write_answer(
struct cus_context *cus,
struct cus_params *params,
struct tf_answer_invoke_client_command *answer)
{
u32 error = S_SUCCESS;
dprintk(KERN_INFO
"tf_crypto_write_answer(%p) : Session=0x%x\n",
cus, cus->client_session);
/* Generate the answer */
answer->message_size =
(sizeof(struct tf_answer_invoke_client_command) -
sizeof(struct tf_answer_header)) / 4;
answer->message_type = TF_MESSAGE_TYPE_INVOKE_CLIENT_COMMAND;
answer->error_origin = TF_ORIGIN_TRUSTED_APP;
answer->operation_id = 0;
answer->error_code = error;
answer->answers[1].size.size = params->output_data_length;
}
/*------------------------------------------------------------------------- */
int tf_crypto_try_shortcuted_update(struct tf_connection *connection,
struct tf_command_invoke_client_command *command,
struct tf_answer_invoke_client_command *answer)
{
struct cus_context *cus = NULL;
if (tf_crypto_is_shortcuted_command(connection,
(struct tf_command_invoke_client_command *) command,
&cus, false)) {
u32 hwa_id = cus->hwa_id;
/* Lock HWA */
tf_crypto_lock_hwa(hwa_id, LOCK_HWA);
if (tf_crypto_is_shortcuted_command(connection,
command,
&cus, true)) {
struct cus_params cus_params;
memset(&cus_params, 0, sizeof(cus_params));
if (!tf_crypto_parse_command_message(
connection,
cus,
command,
&cus_params)) {
/* Decrement CUS context use count */
cus->use_count--;
/* Release HWA lock */
tf_crypto_lock_hwa(cus->hwa_id,
UNLOCK_HWA);
return -1;
}
/* Perform the update in public <=> THE shortcut */
if (!tf_crypto_update(cus, &cus_params)) {
/* Decrement CUS context use count */
cus->use_count--;
/* Release HWA lock */
tf_crypto_lock_hwa(cus->hwa_id,
UNLOCK_HWA);
return -1;
}
/* Write answer message */
tf_crypto_write_answer(cus,
&cus_params, answer);
/* Decrement registered shmems use count if needed */
if (cus_params.input_shmem)
atomic_dec(&cus_params.input_shmem->ref_count);
if (cus_params.output_shmem)
atomic_dec(&cus_params.output_shmem->ref_count);
/* Decrement CUS context use count */
cus->use_count--;
tf_crypto_lock_hwa(cus->hwa_id,
UNLOCK_HWA);
} else {
tf_crypto_lock_hwa(hwa_id, UNLOCK_HWA);
return -1;
}
} else {
return -1;
}
return 0;
}
/*------------------------------------------------------------------------- */
void tf_crypto_wait_for_ready_bit_infinitely(u32 *reg, u32 bit)
{
while (!(INREG32(reg) & bit))
;
}
/*------------------------------------------------------------------------- */
u32 tf_crypto_wait_for_ready_bit(u32 *reg, u32 bit)
{
u32 timeoutCounter = PUBLIC_CRYPTO_TIMEOUT_CONST;
while ((!(INREG32(reg) & bit)) && ((--timeoutCounter) != 0))
;
if (timeoutCounter == 0)
return PUBLIC_CRYPTO_ERR_TIMEOUT;
return PUBLIC_CRYPTO_OPERATION_SUCCESS;
}
/*------------------------------------------------------------------------- */
static DEFINE_SPINLOCK(clk_lock);
void tf_crypto_disable_clock(uint32_t clock_paddr)
{
u32 *clock_reg;
u32 val;
unsigned long flags;
dprintk(KERN_INFO "tf_crypto_disable_clock: " \
"clock_paddr=0x%08X\n",
clock_paddr);
/* Ensure none concurrent access when changing clock registers */
spin_lock_irqsave(&clk_lock, flags);
clock_reg = (u32 *)IO_ADDRESS(clock_paddr);
val = __raw_readl(clock_reg);
val &= ~(0x3);
__raw_writel(val, clock_reg);
/* Wait for clock to be fully disabled */
while ((__raw_readl(clock_reg) & 0x30000) == 0)
;
spin_unlock_irqrestore(&clk_lock, flags);
tf_l4sec_clkdm_allow_idle(true);
}
/*------------------------------------------------------------------------- */
void tf_crypto_enable_clock(uint32_t clock_paddr)
{
u32 *clock_reg;
u32 val;
unsigned long flags;
dprintk(KERN_INFO "tf_crypto_enable_clock: " \
"clock_paddr=0x%08X\n",
clock_paddr);
tf_l4sec_clkdm_wakeup(true);
/* Ensure none concurrent access when changing clock registers */
spin_lock_irqsave(&clk_lock, flags);
clock_reg = (u32 *)IO_ADDRESS(clock_paddr);
val = __raw_readl(clock_reg);
val |= 0x2;
__raw_writel(val, clock_reg);
/* Wait for clock to be fully enabled */
while ((__raw_readl(clock_reg) & 0x30000) != 0)
;
spin_unlock_irqrestore(&clk_lock, flags);
}
/*------------------------------------------------------------------------- */
/* CUS RPCs */
/*------------------------------------------------------------------------- */
/*
* This RPC is used by the secure world to install a new shortcut. Optionally,
* for AES or DES/3DES operations, it can also lock the accelerator so that the
* secure world can install a new key in it.
*/
static int tf_crypto_install_shortcut_lock_hwa(
u32 rpc_command, void *rpc_shared_buffer)
{
struct cus_context *cus = NULL;
struct tf_connection *connection = NULL;
/* Reference the input/ouput data */
struct rpc_install_shortcut_lock_accelerator_out *install_cus_out =
rpc_shared_buffer;
struct rpc_install_shortcut_lock_accelerator_in *install_cus_in =
rpc_shared_buffer;
dprintk(KERN_INFO "tf_crypto_install_shortcut_lock_hwa: "
"rpc_command=0x%08x; hwa_id=0x%08x\n",
rpc_command, install_cus_in->hwa_id);
connection = (struct tf_connection *)
install_cus_in->device_context_id;
if (connection == NULL) {
dprintk(KERN_INFO
"tf_crypto_install_shortcut_lock_hwa: "
"DeviceContext 0x%08x does not exist, "
"cannot create Shortcut\n",
install_cus_in->device_context_id);
install_cus_out->error = -1;
return 0;
}
/*
* Allocate a shortcut context. If the allocation fails,
* return S_ERROR_OUT_OF_MEMORY error code
*/
cus = (struct cus_context *)
internal_kmalloc(sizeof(*cus), GFP_KERNEL);
if (cus == NULL) {
dprintk(KERN_ERR
"tf_crypto_install_shortcut_lock_hwa: "\
"Out of memory for public session\n");
install_cus_out->error = S_ERROR_OUT_OF_MEMORY;
return 0;
}
memset(cus, 0, sizeof(*cus));
/*setup the shortcut */
cus->magic_number = CUS_CONTEXT_MAGIC;
cus->client_session = install_cus_in->client_session;
cus->command_id = install_cus_in->command_id;
cus->hwa_id = install_cus_in->hwa_id;
cus->hwa_ctrl = install_cus_in->hwa_ctrl;
cus->key_context = install_cus_in->key_context;
cus->use_count = 0;
cus->suspended = false;
memcpy(&cus->operation_state,
&install_cus_in->operation_state,
sizeof(union tf_crypto_operation_state));
/*lock the shortcut_list_lock for this device context */
spin_lock(&connection->shortcut_list_lock);
/*Insert the shortcut in the list of shortcuts in the device context */
list_add(&(cus->list), &(connection->shortcut_list));
/*release shortcut_list_lock */
spin_unlock(&connection->shortcut_list_lock);
/*fill the output structure */
install_cus_out->shortcut_id = (u32) cus;
install_cus_out->error = S_SUCCESS;
/*If the L bit is true, then:
* Enter the accelerator critical section. If an update is currently in
* progress on the accelerator (using g_hXXXKeyContext key), this will
* wait until the update has completed. This is call when secure wants
* to install a key in HWA, once it is done secure world will release
* the lock. For SHA (activate shortcut is always called without LOCK
* fag):do nothing
*/
if ((rpc_command & RPC_INSTALL_SHORTCUT_LOCK_ACCELERATOR_LOCK) != 0) {
/*Lock the HWA */
tf_crypto_lock_hwa(cus->hwa_id, LOCK_HWA);
}
dprintk(KERN_INFO
"tf_crypto_install_shortcut_lock_hwa: Done\n");
return S_SUCCESS;
}
/*------------------------------------------------------------------------- */
/*
* This RPC is used to perform one or several of the following operations
* - Lock one or several accelerators for the exclusive use by the secure world,
* either because it is going to be switched to secure or because a new key is
* going to be loaded in the accelerator
* - Suspend a shortcut, i.e., make it temporarily unavailable to the public
* world. This is used when a secure update is going to be performed on the
* operation. The answer to the RPC then contains the operation state
* necessary for the secure world to do the update.
* - Uninstall the shortcut
*/
static int tf_crypto_lock_hwas_suspend_shortcut(
u32 rpc_command, void *rpc_shared_buffer)
{
u32 target_shortcut;
struct cus_context *cus = NULL;
struct tf_connection *connection = NULL;
/*reference the input/ouput data */
struct rpc_lock_hwa_suspend_shortcut_out *suspend_cus_out =
rpc_shared_buffer;
struct rpc_lock_hwa_suspend_shortcut_in *suspend_cus_in =
rpc_shared_buffer;
dprintk(KERN_INFO
"tf_crypto_lock_hwas_suspend_shortcut: "\
"suspend_cus_in=0x%08x; shortcut_id=0x%08x\n",
suspend_cus_in->shortcut_id, (u32)suspend_cus_in);
target_shortcut = suspend_cus_in->shortcut_id;
/*lock HWAs */
tf_crypto_lock_hwas(rpc_command, LOCK_HWA);
/*if suspend_cus_in->shortcut_id != 0 and if rpc_command.S != 0,
then, suspend shortcut */
if ((target_shortcut != 0) && ((rpc_command &
RPC_LOCK_ACCELERATORS_SUSPEND_SHORTCUT_SUSPEND) != 0)) {
/*reference the CUSContext */
cus = (struct cus_context *)
suspend_cus_in->shortcut_id;
/*preventive check1: return if shortcut does not exist */
connection = tf_get_device_context(cus);
if (connection == NULL) {
dprintk(KERN_INFO
"tf_crypto_lock_hwas_suspend_shortcut: "\
"shortcut_id=0x%08x does not exist, cannot suspend "\
"Shortcut\n",
suspend_cus_in->shortcut_id);
return -1;
}
loop_on_suspend:
/*lock shortcut_list_lock associated with the
*device context */
spin_lock(&connection->shortcut_list_lock);
/*Suspend shortcut */
cus->suspended = true;
if (cus->use_count != 0) {
/*release shortcut_list_lock */
spin_unlock(&connection->
shortcut_list_lock);
schedule();
goto loop_on_suspend;
}
/*Copy the operation state data stored in CUS Context into the
*answer to the RPC output assuming that HWA register has been
*saved at update time */
memcpy(&suspend_cus_out->operation_state,
&cus->operation_state,
sizeof(union tf_crypto_operation_state));
/*Uninstall shortcut if requiered */
if ((rpc_command &
RPC_LOCK_ACCELERATORS_SUSPEND_SHORTCUT_UNINSTALL) != 0) {
dprintk(KERN_INFO
"tf_crypto_lock_hwas_suspend_shortcut:"\
"Uninstall 0x%08x\n",
target_shortcut);
list_del(&(cus->list));
/*list_del only remove the item in the list, the
*memory must be free afterward */
/*release the lock before calling internal_kfree */
spin_unlock(&connection->
shortcut_list_lock);
if (cus != NULL)
internal_kfree(cus);
return 0;
}
/*release shortcut_list_lock */
spin_unlock(&connection->shortcut_list_lock);
}
return 0;
}
/*------------------------------------------------------------------------- */
/*
* This RPC is used to perform one or several of the following operations:
* - Resume a shortcut previously suspended
* - Inform the public driver of the new keys installed in the DES and AES
* accelerators
* - Unlock some of the accelerators
*/
static int tf_crypto_resume_shortcut_unlock_hwas(
u32 rpc_command, void *rpc_shared_buffer)
{
struct tf_device *dev = tf_get_device();
struct tf_connection *connection = NULL;
struct cus_context *cus = NULL;
/*reference the input data */
struct rpc_resume_shortcut_unlock_hwa_in *resume_cus_in =
rpc_shared_buffer;
dprintk(KERN_INFO
"tf_crypto_resume_shortcut_unlock_hwas\n"
"rpc_command=0x%08x\nshortcut_id=0x%08x\n",
rpc_command, resume_cus_in->shortcut_id);
/*if shortcut_id not 0 resume the shortcut and unlock HWA
else only unlock HWA */
if (resume_cus_in->shortcut_id != 0) {
/*reference the CUSContext */
cus = (struct cus_context *)
resume_cus_in->shortcut_id;
/*preventive check1: return if shortcut does not exist
*else, points to the public crypto monitor (inside the device
*context) */
connection = tf_get_device_context(cus);
if (connection == NULL) {
dprintk(KERN_INFO
"tf_crypto_resume_shortcut_unlock_hwas(...):"\
"shortcut_id 0x%08x does not exist, cannot suspend "\
"Shortcut\n",
resume_cus_in->shortcut_id);
return -1;
}
/*if S set and shortcut not yet suspended */
if ((cus->suspended) &&
((rpc_command &
RPC_RESUME_SHORTCUT_UNLOCK_ACCELERATORS_RESUME) != 0)){
/*Write operation_stateData in the shortcut context */
memcpy(&cus->operation_state,
&resume_cus_in->operation_state,
sizeof(union tf_crypto_operation_state));
/*resume the shortcut */
cus->suspended = false;
}
}
/*
* If A is set: Atomically set aes1_key_context to
* aes1_key_context
*/
if ((rpc_command &
RPC_RESUME_SHORTCUT_UNLOCK_ACCELERATORS_UNLOCK_AES1) != 0) {
dev->aes1_key_context =
resume_cus_in->aes1_key_context;
}
/*
* If D is set:
* Atomically set des_key_context to des_key_context
*/
if ((rpc_command &
RPC_RESUME_SHORTCUT_UNLOCK_ACCELERATORS_UNLOCK_DES) != 0) {
dev->des_key_context =
resume_cus_in->des_key_context;
}
/* H is never set by the PA: Atomically set sham1_is_public to true */
dev->sham1_is_public = true;
/* Unlock HWAs according rpc_command */
tf_crypto_lock_hwas(rpc_command, UNLOCK_HWA);
return 0;
}
/*------------------------------------------------------------------------- */
/*
* This RPC is used to notify the public driver that the key in the AES, DES
* accelerators has been cleared. This happens only when the key is no longer
* referenced by any shortcuts. So, it is guaranteed that no-one has entered the
* accelerators critical section and there is no need to enter it to implement
* this RPC.
*/
static int tf_crypto_clear_global_key_context(
u32 rpc_command, void *rpc_shared_buffer)
{
struct tf_device *dev = tf_get_device();
/*
* If A is set: Atomically set aes1_key_context to 0
*/
if ((rpc_command &
RPC_RESUME_SHORTCUT_UNLOCK_ACCELERATORS_UNLOCK_AES1) != 0) {
dev->aes1_key_context = 0;
}
/*
*If D is set: Atomically set des_key_context to 0
*/
if ((rpc_command &
RPC_RESUME_SHORTCUT_UNLOCK_ACCELERATORS_UNLOCK_DES) != 0) {
dev->des_key_context = 0;
}
return 0;
}
/*------------------------------------------------------------------------- */
/*
* Execute a public crypto related RPC
*/
int tf_crypto_execute_rpc(u32 rpc_command, void *rpc_shared_buffer)
{
switch (rpc_command & RPC_CRYPTO_COMMAND_MASK) {
case RPC_INSTALL_SHORTCUT_LOCK_ACCELERATOR:
dprintk(KERN_INFO "RPC_INSTALL_SHORTCUT_LOCK_ACCELERATOR\n");
return tf_crypto_install_shortcut_lock_hwa(
rpc_command, rpc_shared_buffer);
case RPC_LOCK_ACCELERATORS_SUSPEND_SHORTCUT:
dprintk(KERN_INFO "RPC_LOCK_ACCELERATORS_SUSPEND_SHORTCUT\n");
return tf_crypto_lock_hwas_suspend_shortcut(
rpc_command, rpc_shared_buffer);
case RPC_RESUME_SHORTCUT_UNLOCK_ACCELERATORS:
dprintk(KERN_INFO "RPC_RESUME_SHORTCUT_UNLOCK_ACCELERATORS\n");
return tf_crypto_resume_shortcut_unlock_hwas(
rpc_command, rpc_shared_buffer);
case RPC_CLEAR_GLOBAL_KEY_CONTEXT:
dprintk(KERN_INFO "RPC_CLEAR_GLOBAL_KEY_CONTEXT\n");
return tf_crypto_clear_global_key_context(
rpc_command, rpc_shared_buffer);
}
return -1;
}