/** * 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; } /*------------------------------------------------------------------------- */ /** *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; 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_BUFFER_USER); 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->client_buffer + 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->client_buffer + 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)) { 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--; return -1; } /* Perform the update in public <=> THE shortcut */ if (!tf_crypto_update(cus, &cus_params)) { /* Decrement CUS context use count */ cus->use_count--; 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--; } else { 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); u32 tf_crypto_turn_off_clocks(void) { unsigned long flags; u32 ret = 0xf; spin_lock_irqsave(&clk_lock, flags); ret = tf_try_disabling_secure_hwa_clocks(0xf) & 0xff; spin_unlock_irqrestore(&clk_lock, flags); if (ret == 0xff) panic("Error calling API_HAL_HWATURNOFF_INDEX"); return ret; } void tf_crypto_disable_clock(uint32_t clock_paddr) { dprintk(KERN_INFO "tf_crypto_disable_clock: " \ "clock_paddr=0x%08X\n", clock_paddr); tf_clock_timer_stop(); } /*------------------------------------------------------------------------- */ 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); /* Ensure none concurrent access when changing clock registers */ spin_lock_irqsave(&clk_lock, flags); tf_clock_timer_start(); clock_reg = (u32 *)IO_ADDRESS(clock_paddr); val = __raw_readl(clock_reg); if ((val & 0x30000) == 0) goto end; val |= 0x2; __raw_writel(val, clock_reg); /* Wait for clock to be fully enabled */ while ((__raw_readl(clock_reg) & 0x30000) != 0) ; end: spin_unlock_irqrestore(&clk_lock, flags); } u32 tf_crypto_read_clock_value(uint32_t clock_paddr) { u32 *clock_reg; u32 val; unsigned long flags; dprintk(KERN_INFO "tf_crypto_read_clock_value: " \ "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); spin_unlock_irqrestore(&clk_lock, flags); return val; } /*------------------------------------------------------------------------- */ /* 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; /*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; 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; }