/** * 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_crypto.h" #include "tf_dma.h" #include "tf_zebra.h" #include #include #include #include #include #include #include #include /* *AES Hardware Accelerator: Base address */ #define AES1_REGS_HW_ADDR 0x4B501000 /* *CTRL register Masks */ #define AES_CTRL_OUTPUT_READY_BIT (1<<0) #define AES_CTRL_INPUT_READY_BIT (1<<1) #define AES_CTRL_GET_DIRECTION(x) (x&4) #define AES_CTRL_DIRECTION_DECRYPT 0 #define AES_CTRL_DIRECTION_ENCRYPT (1<<2) #define AES_CTRL_GET_KEY_SIZE(x) (x & 0x18) #define AES_CTRL_KEY_SIZE_128 0x08 #define AES_CTRL_KEY_SIZE_192 0x10 #define AES_CTRL_KEY_SIZE_256 0x18 #define AES_CTRL_GET_MODE(x) ((x & 0x60) >> 5) #define AES_CTRL_IS_MODE_CBC(x) (AES_CTRL_GET_MODE(x) == 1) #define AES_CTRL_IS_MODE_ECB(x) (AES_CTRL_GET_MODE(x) == 0) #define AES_CTRL_IS_MODE_CTR(x) ((AES_CTRL_GET_MODE(x) == 2) || \ (AES_CTRL_GET_MODE(x) == 3)) #define AES_CTRL_MODE_CBC_BIT 0x20 #define AES_CTRL_MODE_ECB_BIT 0 #define AES_CTRL_MODE_CTR_BIT 0x40 #define AES_CTRL_GET_CTR_WIDTH(x) (x&0x180) #define AES_CTRL_CTR_WIDTH_32 0 #define AES_CTRL_CTR_WIDTH_64 0x80 #define AES_CTRL_CTR_WIDTH_96 0x100 #define AES_CTRL_CTR_WIDTH_128 0x180 /* * SYSCONFIG register masks */ #define AES_SYSCONFIG_DMA_REQ_IN_EN_BIT (1 << 5) #define AES_SYSCONFIG_DMA_REQ_OUT_EN_BIT (1 << 6) /*----------------------------------------------------------------------*/ /* AES Context */ /*----------------------------------------------------------------------*/ /** *This structure contains the registers of the AES HW accelerator. */ struct aes_reg { u32 AES_KEY2_6; /* 0x00 */ u32 AES_KEY2_7; /* 0xO4 */ u32 AES_KEY2_4; /* 0x08 */ u32 AES_KEY2_5; /* 0x0C */ u32 AES_KEY2_2; /* 0x10 */ u32 AES_KEY2_3; /* 0x14 */ u32 AES_KEY2_0; /* 0x18 */ u32 AES_KEY2_1; /* 0x1C */ u32 AES_KEY1_6; /* 0x20 */ u32 AES_KEY1_7; /* 0x24 */ u32 AES_KEY1_4; /* 0x28 */ u32 AES_KEY1_5; /* 0x2C */ u32 AES_KEY1_2; /* 0x30 */ u32 AES_KEY1_3; /* 0x34 */ u32 AES_KEY1_0; /* 0x38 */ u32 AES_KEY1_1; /* 0x3C */ u32 AES_IV_IN_0; /* 0x40 */ u32 AES_IV_IN_1; /* 0x44 */ u32 AES_IV_IN_2; /* 0x48 */ u32 AES_IV_IN_3; /* 0x4C */ u32 AES_CTRL; /* 0x50 */ u32 AES_C_LENGTH_0; /* 0x54 */ u32 AES_C_LENGTH_1; /* 0x58 */ u32 AES_AUTH_LENGTH; /* 0x5C */ u32 AES_DATA_IN_0; /* 0x60 */ u32 AES_DATA_IN_1; /* 0x64 */ u32 AES_DATA_IN_2; /* 0x68 */ u32 AES_DATA_IN_3; /* 0x6C */ u32 AES_TAG_OUT_0; /* 0x70 */ u32 AES_TAG_OUT_1; /* 0x74 */ u32 AES_TAG_OUT_2; /* 0x78 */ u32 AES_TAG_OUT_3; /* 0x7C */ u32 AES_REVISION; /* 0x80 */ u32 AES_SYSCONFIG; /* 0x84 */ u32 AES_SYSSTATUS; /* 0x88 */ }; static struct aes_reg *paes_reg; #ifdef CONFIG_SMC_KERNEL_CRYPTO #define FLAGS_FAST BIT(7) #define FLAGS_BUSY 8 struct aes_hwa_ctx { unsigned long flags; spinlock_t lock; struct crypto_queue queue; struct tasklet_struct task; struct ablkcipher_request *req; size_t total; struct scatterlist *in_sg; size_t in_offset; struct scatterlist *out_sg; size_t out_offset; size_t buflen; void *buf_in; size_t dma_size; int dma_in; int dma_lch_in; dma_addr_t dma_addr_in; void *buf_out; int dma_out; int dma_lch_out; dma_addr_t dma_addr_out; struct tf_crypto_aes_operation_state *ctx; }; static struct aes_hwa_ctx *aes_ctx; #endif /*--------------------------------------------------------------------------- *Forward declarations *------------------------------------------------------------------------- */ static bool tf_aes_update_dma(u8 *src, u8 *dest, u32 nb_blocks, bool is_kernel); /*---------------------------------------------------------------------------- *Save HWA registers into the specified operation state structure *--------------------------------------------------------------------------*/ static void tf_aes_save_registers( struct tf_crypto_aes_operation_state *aes_state) { dprintk(KERN_INFO "tf_aes_save_registers: " "aes_state(%p) <- paes_reg(%p): CTRL=0x%08x\n", aes_state, paes_reg, aes_state->CTRL); /*Save the IV if we are in CBC or CTR mode (not required for ECB) */ if (!AES_CTRL_IS_MODE_ECB(aes_state->CTRL)) { aes_state->AES_IV_0 = INREG32(&paes_reg->AES_IV_IN_0); aes_state->AES_IV_1 = INREG32(&paes_reg->AES_IV_IN_1); aes_state->AES_IV_2 = INREG32(&paes_reg->AES_IV_IN_2); aes_state->AES_IV_3 = INREG32(&paes_reg->AES_IV_IN_3); } } /*---------------------------------------------------------------------------- *Restore the HWA registers from the operation state structure *---------------------------------------------------------------------------*/ static void tf_aes_restore_registers( struct tf_crypto_aes_operation_state *aes_state) { struct tf_device *dev = tf_get_device(); dprintk(KERN_INFO "tf_aes_restore_registers: " "paes_reg(%p) <- aes_state(%p): CTRL=0x%08x\n", paes_reg, aes_state, aes_state->CTRL); if (aes_state->key_is_public) { OUTREG32(&paes_reg->AES_KEY1_0, aes_state->KEY1_0); OUTREG32(&paes_reg->AES_KEY1_1, aes_state->KEY1_1); OUTREG32(&paes_reg->AES_KEY1_2, aes_state->KEY1_2); OUTREG32(&paes_reg->AES_KEY1_3, aes_state->KEY1_3); OUTREG32(&paes_reg->AES_KEY1_4, aes_state->KEY1_4); OUTREG32(&paes_reg->AES_KEY1_5, aes_state->KEY1_5); OUTREG32(&paes_reg->AES_KEY1_6, aes_state->KEY1_6); OUTREG32(&paes_reg->AES_KEY1_7, aes_state->KEY1_7); /* * Make sure a potential secure key that has been overwritten by * the previous code is reinstalled before performing other * public crypto operations. */ dev->aes1_key_context = 0; } else { aes_state->CTRL |= INREG32(&paes_reg->AES_CTRL); } /* * Restore the IV first if we are in CBC or CTR mode * (not required for ECB) */ if (!AES_CTRL_IS_MODE_ECB(aes_state->CTRL)) { OUTREG32(&paes_reg->AES_IV_IN_0, aes_state->AES_IV_0); OUTREG32(&paes_reg->AES_IV_IN_1, aes_state->AES_IV_1); OUTREG32(&paes_reg->AES_IV_IN_2, aes_state->AES_IV_2); OUTREG32(&paes_reg->AES_IV_IN_3, aes_state->AES_IV_3); } /* Then set the CTRL register: * overwrite the CTRL only when needed, because unconditionally doing * it leads to break the HWA process (observed by experimentation) */ aes_state->CTRL = (aes_state->CTRL & (3 << 3)) /* key size */ | (aes_state->CTRL & ((1 << 2) | (1 << 5) | (1 << 6))) | (0x3 << 7) /* Always set CTR_WIDTH to 128-bit */; if ((aes_state->CTRL & 0x1FC) != (INREG32(&paes_reg->AES_CTRL) & 0x1FC)) OUTREG32(&paes_reg->AES_CTRL, aes_state->CTRL & 0x1FC); /* Set the SYSCONFIG register to 0 */ OUTREG32(&paes_reg->AES_SYSCONFIG, 0); } /*-------------------------------------------------------------------------- */ void tf_aes_init(void) { paes_reg = omap_ioremap(AES1_REGS_HW_ADDR, SZ_1M, MT_DEVICE); if (paes_reg == NULL) panic("Unable to remap AES1 module"); } void tf_aes_exit(void) { omap_iounmap(paes_reg); } bool tf_aes_update(struct tf_crypto_aes_operation_state *aes_state, u8 *src, u8 *dest, u32 nb_blocks) { u32 nbr_of_blocks; u32 temp; u8 *process_src; u8 *process_dest; u32 dma_use = PUBLIC_CRYPTO_DMA_USE_NONE; bool is_kernel = false; /* *Choice of the processing type */ if (nb_blocks * AES_BLOCK_SIZE >= DMA_TRIGGER_IRQ_AES) dma_use = PUBLIC_CRYPTO_DMA_USE_IRQ; dprintk(KERN_INFO "tf_aes_update: " "src=0x%08x, dest=0x%08x, nb_blocks=0x%08x, dma_use=0x%08x\n", (unsigned int)src, (unsigned int)dest, (unsigned int)nb_blocks, (unsigned int)dma_use); if (aes_state->key_is_public) is_kernel = true; if (nb_blocks == 0) { dprintk(KERN_INFO "tf_aes_update: Nothing to process\n"); return true; } if ((AES_CTRL_GET_DIRECTION(INREG32(&paes_reg->AES_CTRL)) != AES_CTRL_GET_DIRECTION(aes_state->CTRL)) && !aes_state->key_is_public) { dprintk(KERN_WARNING "HWA configured for another direction\n"); return false; } /*Restore the registers of the accelerator from the operation state */ tf_aes_restore_registers(aes_state); if (dma_use == PUBLIC_CRYPTO_DMA_USE_IRQ) { /* Perform the update with DMA */ if (!tf_aes_update_dma(src, dest, nb_blocks, is_kernel)) return false; } else { u8 buf[DMA_TRIGGER_IRQ_AES]; /* * Synchronous Linux crypto API buffers are mapped in kernel * space */ if (is_kernel) { process_src = src; process_dest = dest; } else { if (copy_from_user(buf, src, nb_blocks * AES_BLOCK_SIZE)) return false; process_src = process_dest = buf; } for (nbr_of_blocks = 0; nbr_of_blocks < nb_blocks; nbr_of_blocks++) { /*We wait for the input ready */ /*Crash the system as this should never occur */ if (tf_crypto_wait_for_ready_bit( (u32 *)&paes_reg->AES_CTRL, AES_CTRL_INPUT_READY_BIT) != PUBLIC_CRYPTO_OPERATION_SUCCESS) panic("Wait too long for AES hardware " "accelerator Input data to be ready\n"); /* We copy the 16 bytes of data src->reg */ temp = (u32) BYTES_TO_LONG(process_src); OUTREG32(&paes_reg->AES_DATA_IN_0, temp); process_src += 4; temp = (u32) BYTES_TO_LONG(process_src); OUTREG32(&paes_reg->AES_DATA_IN_1, temp); process_src += 4; temp = (u32) BYTES_TO_LONG(process_src); OUTREG32(&paes_reg->AES_DATA_IN_2, temp); process_src += 4; temp = (u32) BYTES_TO_LONG(process_src); OUTREG32(&paes_reg->AES_DATA_IN_3, temp); process_src += 4; /* We wait for the output ready */ tf_crypto_wait_for_ready_bit_infinitely( (u32 *)&paes_reg->AES_CTRL, AES_CTRL_OUTPUT_READY_BIT); /* We copy the 16 bytes of data reg->dest */ temp = INREG32(&paes_reg->AES_DATA_IN_0); LONG_TO_BYTE(process_dest, temp); process_dest += 4; temp = INREG32(&paes_reg->AES_DATA_IN_1); LONG_TO_BYTE(process_dest, temp); process_dest += 4; temp = INREG32(&paes_reg->AES_DATA_IN_2); LONG_TO_BYTE(process_dest, temp); process_dest += 4; temp = INREG32(&paes_reg->AES_DATA_IN_3); LONG_TO_BYTE(process_dest, temp); process_dest += 4; } if (!is_kernel) if (copy_to_user(dest, buf, nb_blocks * AES_BLOCK_SIZE)) return false; } /* Save the accelerator registers into the operation state */ tf_aes_save_registers(aes_state); dprintk(KERN_INFO "tf_aes_update: Done\n"); return true; } /*-------------------------------------------------------------------------- */ /* *Static function, perform AES encryption/decryption using the DMA for data *transfer. * *inputs: src : pointer of the input data to process * nb_blocks : number of block to process * dma_use : PUBLIC_CRYPTO_DMA_USE_IRQ (use irq to monitor end of DMA) * | PUBLIC_CRYPTO_DMA_USE_POLLING (poll the end of DMA) *output: dest : pointer of the output data (can be eq to src) */ static bool tf_aes_update_dma(u8 *src, u8 *dest, u32 nb_blocks, bool is_kernel) { /* *Note: The DMA only sees physical addresses ! */ int dma_ch0; int dma_ch1; struct omap_dma_channel_params ch0_parameters; struct omap_dma_channel_params ch1_parameters; u32 length = nb_blocks * AES_BLOCK_SIZE; u32 length_loop = 0; u32 nb_blocksLoop = 0; struct tf_device *dev = tf_get_device(); dprintk(KERN_INFO "%s: In=0x%08x, Out=0x%08x, Len=%u\n", __func__, (unsigned int)src, (unsigned int)dest, (unsigned int)length); /*lock the DMA */ while (!mutex_trylock(&dev->sm.dma_mutex)) cpu_relax(); if (tf_dma_request(&dma_ch0) != PUBLIC_CRYPTO_OPERATION_SUCCESS) { mutex_unlock(&dev->sm.dma_mutex); return false; } if (tf_dma_request(&dma_ch1) != PUBLIC_CRYPTO_OPERATION_SUCCESS) { omap_free_dma(dma_ch0); mutex_unlock(&dev->sm.dma_mutex); return false; } while (length > 0) { /* * At this time, we are sure that the DMAchannels *are available and not used by other public crypto operation */ /*DMA used for Input and Output */ OUTREG32(&paes_reg->AES_SYSCONFIG, INREG32(&paes_reg->AES_SYSCONFIG) | AES_SYSCONFIG_DMA_REQ_OUT_EN_BIT | AES_SYSCONFIG_DMA_REQ_IN_EN_BIT); /*check length */ if (length <= dev->dma_buffer_length) length_loop = length; else length_loop = dev->dma_buffer_length; /*The length is always a multiple of the block size */ nb_blocksLoop = length_loop / AES_BLOCK_SIZE; /* * Copy the data from the user input buffer into a preallocated * buffer which has correct properties from efficient DMA * transfers. */ if (!is_kernel) { if (copy_from_user( dev->dma_buffer, src, length_loop)) { omap_free_dma(dma_ch0); omap_free_dma(dma_ch1); mutex_unlock(&dev->sm.dma_mutex); return false; } } else { memcpy(dev->dma_buffer, src, length_loop); } /*DMA1: Mem -> AES */ tf_dma_set_channel_common_params(&ch0_parameters, nb_blocksLoop, DMA_CEN_Elts_per_Frame_AES, AES1_REGS_HW_ADDR + 0x60, (u32)dev->dma_buffer_phys, OMAP44XX_DMA_AES1_P_DATA_IN_REQ); ch0_parameters.src_amode = OMAP_DMA_AMODE_POST_INC; ch0_parameters.dst_amode = OMAP_DMA_AMODE_CONSTANT; ch0_parameters.src_or_dst_synch = OMAP_DMA_DST_SYNC; dprintk(KERN_INFO "%s: omap_set_dma_params(ch0)\n", __func__); omap_set_dma_params(dma_ch0, &ch0_parameters); omap_set_dma_src_burst_mode(dma_ch0, OMAP_DMA_DATA_BURST_8); omap_set_dma_dest_burst_mode(dma_ch0, OMAP_DMA_DATA_BURST_8); omap_set_dma_src_data_pack(dma_ch0, 1); /*DMA2: AES -> Mem */ tf_dma_set_channel_common_params(&ch1_parameters, nb_blocksLoop, DMA_CEN_Elts_per_Frame_AES, (u32)dev->dma_buffer_phys, AES1_REGS_HW_ADDR + 0x60, OMAP44XX_DMA_AES1_P_DATA_OUT_REQ); ch1_parameters.src_amode = OMAP_DMA_AMODE_CONSTANT; ch1_parameters.dst_amode = OMAP_DMA_AMODE_POST_INC; ch1_parameters.src_or_dst_synch = OMAP_DMA_SRC_SYNC; dprintk(KERN_INFO "%s: omap_set_dma_params(ch1)\n", __func__); omap_set_dma_params(dma_ch1, &ch1_parameters); omap_set_dma_src_burst_mode(dma_ch1, OMAP_DMA_DATA_BURST_8); omap_set_dma_dest_burst_mode(dma_ch1, OMAP_DMA_DATA_BURST_8); omap_set_dma_dest_data_pack(dma_ch1, 1); wmb(); dprintk(KERN_INFO "%s: Start DMA channel %d\n", __func__, (unsigned int)dma_ch1); tf_dma_start(dma_ch1, OMAP_DMA_BLOCK_IRQ); dprintk(KERN_INFO "%s: Start DMA channel %d\n", __func__, (unsigned int)dma_ch0); tf_dma_start(dma_ch0, OMAP_DMA_BLOCK_IRQ); dprintk(KERN_INFO "%s: Waiting for IRQ\n", __func__); tf_dma_wait(2); /*Unset DMA synchronisation requests */ OUTREG32(&paes_reg->AES_SYSCONFIG, INREG32(&paes_reg->AES_SYSCONFIG) & (~AES_SYSCONFIG_DMA_REQ_OUT_EN_BIT) & (~AES_SYSCONFIG_DMA_REQ_IN_EN_BIT)); omap_clear_dma(dma_ch0); omap_clear_dma(dma_ch1); /* *The dma transfer is complete */ /*The DMA output is in the preallocated aligned buffer *and needs to be copied to the output buffer.*/ if (!is_kernel) { if (copy_to_user( dest, dev->dma_buffer, length_loop)) { omap_free_dma(dma_ch0); omap_free_dma(dma_ch1); mutex_unlock(&dev->sm.dma_mutex); return false; } } else { memcpy(dest, dev->dma_buffer, length_loop); } src += length_loop; dest += length_loop; length -= length_loop; } /*For safety reasons, let's clean the working buffer */ memset(dev->dma_buffer, 0, length_loop); /*release the DMA */ omap_free_dma(dma_ch0); omap_free_dma(dma_ch1); mutex_unlock(&dev->sm.dma_mutex); dprintk(KERN_INFO "%s: Success\n", __func__); return true; } #ifdef CONFIG_SMC_KERNEL_CRYPTO /* * AES HWA registration into kernel crypto framework */ static void sg_copy_buf(void *buf, struct scatterlist *sg, unsigned int start, unsigned int nbytes, int out) { struct scatter_walk walk; if (!nbytes) return; scatterwalk_start(&walk, sg); scatterwalk_advance(&walk, start); scatterwalk_copychunks(buf, &walk, nbytes, out); scatterwalk_done(&walk, out, 0); } static int sg_copy(struct scatterlist **sg, size_t *offset, void *buf, size_t buflen, size_t total, int out) { unsigned int count, off = 0; while (buflen && total) { count = min((*sg)->length - *offset, total); count = min(count, buflen); if (!count) return off; sg_copy_buf(buf + off, *sg, *offset, count, out); off += count; buflen -= count; *offset += count; total -= count; if (*offset == (*sg)->length) { *sg = sg_next(*sg); if (*sg) *offset = 0; else total = 0; } } return off; } static int aes_dma_start(struct aes_hwa_ctx *ctx) { int err, fast = 0, in, out; size_t count; dma_addr_t addr_in, addr_out; struct omap_dma_channel_params dma_params; struct tf_crypto_aes_operation_state *state = crypto_ablkcipher_ctx(crypto_ablkcipher_reqtfm(ctx->req)); if (sg_is_last(ctx->in_sg) && sg_is_last(ctx->out_sg)) { in = IS_ALIGNED((u32)ctx->in_sg->offset, sizeof(u32)); out = IS_ALIGNED((u32)ctx->out_sg->offset, sizeof(u32)); fast = in && out; } if (fast) { count = min(ctx->total, sg_dma_len(ctx->in_sg)); count = min(count, sg_dma_len(ctx->out_sg)); if (count != ctx->total) return -EINVAL; err = dma_map_sg(NULL, ctx->in_sg, 1, DMA_TO_DEVICE); if (!err) return -EINVAL; err = dma_map_sg(NULL, ctx->out_sg, 1, DMA_FROM_DEVICE); if (!err) { dma_unmap_sg(NULL, ctx->in_sg, 1, DMA_TO_DEVICE); return -EINVAL; } addr_in = sg_dma_address(ctx->in_sg); addr_out = sg_dma_address(ctx->out_sg); ctx->flags |= FLAGS_FAST; } else { count = sg_copy(&ctx->in_sg, &ctx->in_offset, ctx->buf_in, ctx->buflen, ctx->total, 0); addr_in = ctx->dma_addr_in; addr_out = ctx->dma_addr_out; ctx->flags &= ~FLAGS_FAST; } ctx->total -= count; tf_crypto_lock_hwa(PUBLIC_CRYPTO_HWA_AES1, LOCK_HWA); /* Configure HWA */ tf_crypto_enable_clock(PUBLIC_CRYPTO_AES1_CLOCK_REG); tf_aes_restore_registers(state); OUTREG32(&paes_reg->AES_SYSCONFIG, INREG32(&paes_reg->AES_SYSCONFIG) | AES_SYSCONFIG_DMA_REQ_OUT_EN_BIT | AES_SYSCONFIG_DMA_REQ_IN_EN_BIT); ctx->dma_size = count; if (!fast) dma_sync_single_for_device(NULL, addr_in, count, DMA_TO_DEVICE); dma_params.data_type = OMAP_DMA_DATA_TYPE_S32; dma_params.frame_count = count / AES_BLOCK_SIZE; dma_params.elem_count = DMA_CEN_Elts_per_Frame_AES; dma_params.src_ei = 0; dma_params.src_fi = 0; dma_params.dst_ei = 0; dma_params.dst_fi = 0; dma_params.sync_mode = OMAP_DMA_SYNC_FRAME; /* IN */ dma_params.trigger = ctx->dma_in; dma_params.src_or_dst_synch = OMAP_DMA_DST_SYNC; dma_params.dst_start = AES1_REGS_HW_ADDR + 0x60; dma_params.dst_amode = OMAP_DMA_AMODE_CONSTANT; dma_params.src_start = addr_in; dma_params.src_amode = OMAP_DMA_AMODE_POST_INC; omap_set_dma_params(ctx->dma_lch_in, &dma_params); omap_set_dma_dest_burst_mode(ctx->dma_lch_in, OMAP_DMA_DATA_BURST_8); omap_set_dma_src_burst_mode(ctx->dma_lch_in, OMAP_DMA_DATA_BURST_8); omap_set_dma_src_data_pack(ctx->dma_lch_in, 1); /* OUT */ dma_params.trigger = ctx->dma_out; dma_params.src_or_dst_synch = OMAP_DMA_SRC_SYNC; dma_params.src_start = AES1_REGS_HW_ADDR + 0x60; dma_params.src_amode = OMAP_DMA_AMODE_CONSTANT; dma_params.dst_start = addr_out; dma_params.dst_amode = OMAP_DMA_AMODE_POST_INC; omap_set_dma_params(ctx->dma_lch_out, &dma_params); omap_set_dma_dest_burst_mode(ctx->dma_lch_out, OMAP_DMA_DATA_BURST_8); omap_set_dma_src_burst_mode(ctx->dma_lch_out, OMAP_DMA_DATA_BURST_8); omap_set_dma_dest_data_pack(ctx->dma_lch_out, 1); /* Is this really needed? */ omap_disable_dma_irq(ctx->dma_lch_in, OMAP_DMA_DROP_IRQ); omap_enable_dma_irq(ctx->dma_lch_in, OMAP_DMA_BLOCK_IRQ); omap_disable_dma_irq(ctx->dma_lch_out, OMAP_DMA_DROP_IRQ); omap_enable_dma_irq(ctx->dma_lch_out, OMAP_DMA_BLOCK_IRQ); wmb(); omap_start_dma(ctx->dma_lch_in); omap_start_dma(ctx->dma_lch_out); return 0; } static int aes_dma_stop(struct aes_hwa_ctx *ctx) { struct tf_crypto_aes_operation_state *state = crypto_ablkcipher_ctx(crypto_ablkcipher_reqtfm(ctx->req)); int err = 0; size_t count; dprintk(KERN_INFO "aes_dma_stop(%p)\n", ctx); tf_aes_save_registers(state); if (!AES_CTRL_IS_MODE_ECB(state->CTRL)) { u32 *ptr = (u32 *) ctx->req->info; ptr[0] = state->AES_IV_0; ptr[1] = state->AES_IV_1; ptr[2] = state->AES_IV_2; ptr[3] = state->AES_IV_3; } OUTREG32(&paes_reg->AES_SYSCONFIG, 0); tf_crypto_disable_clock(PUBLIC_CRYPTO_AES1_CLOCK_REG); tf_crypto_lock_hwa(PUBLIC_CRYPTO_HWA_AES1, UNLOCK_HWA); omap_stop_dma(ctx->dma_lch_in); omap_stop_dma(ctx->dma_lch_out); if (ctx->flags & FLAGS_FAST) { dma_unmap_sg(NULL, ctx->out_sg, 1, DMA_FROM_DEVICE); dma_unmap_sg(NULL, ctx->in_sg, 1, DMA_TO_DEVICE); } else { dma_sync_single_for_device(NULL, ctx->dma_addr_out, ctx->dma_size, DMA_FROM_DEVICE); /* Copy data */ count = sg_copy(&ctx->out_sg, &ctx->out_offset, ctx->buf_out, ctx->buflen, ctx->dma_size, 1); if (count != ctx->dma_size) err = -EINVAL; } if (err || !ctx->total) ctx->req->base.complete(&ctx->req->base, err); return err; } static void aes_dma_callback(int lch, u16 ch_status, void *data) { struct aes_hwa_ctx *ctx = data; if (lch == ctx->dma_lch_out) tasklet_schedule(&ctx->task); } static int aes_dma_init(struct aes_hwa_ctx *ctx) { int err = -ENOMEM; ctx->dma_lch_out = -1; ctx->dma_lch_in = -1; ctx->buflen = PAGE_SIZE; ctx->buflen &= ~(AES_BLOCK_SIZE - 1); dprintk(KERN_INFO "aes_dma_init(%p)\n", ctx); /* Allocate and map cache buffers */ ctx->buf_in = dma_alloc_coherent(NULL, ctx->buflen, &ctx->dma_addr_in, GFP_KERNEL); if (!ctx->buf_in) { dprintk(KERN_ERR "SMC: Unable to alloc AES in cache buffer\n"); return -ENOMEM; } ctx->buf_out = dma_alloc_coherent(NULL, ctx->buflen, &ctx->dma_addr_out, GFP_KERNEL); if (!ctx->buf_out) { dprintk(KERN_ERR "SMC: Unable to alloc AES out cache buffer\n"); dma_free_coherent(NULL, ctx->buflen, ctx->buf_in, ctx->dma_addr_in); return -ENOMEM; } /* Request DMA channels */ err = omap_request_dma(0, "smc-aes-rx", aes_dma_callback, ctx, &ctx->dma_lch_in); if (err) { dprintk(KERN_ERR "SMC: Unable to request AES RX DMA channel\n"); goto err_dma_in; } err = omap_request_dma(0, "smc-aes-rx", aes_dma_callback, ctx, &ctx->dma_lch_out); if (err) { dprintk(KERN_ERR "SMC: Unable to request AES TX DMA channel\n"); goto err_dma_out; } dprintk(KERN_INFO "aes_dma_init(%p) configured DMA channels" "(RX = %d, TX = %d)\n", ctx, ctx->dma_lch_in, ctx->dma_lch_out); return 0; err_dma_out: omap_free_dma(ctx->dma_lch_in); err_dma_in: dma_free_coherent(NULL, ctx->buflen, ctx->buf_in, ctx->dma_addr_in); dma_free_coherent(NULL, ctx->buflen, ctx->buf_out, ctx->dma_addr_out); return err; } static void aes_dma_cleanup(struct aes_hwa_ctx *ctx) { omap_free_dma(ctx->dma_lch_out); omap_free_dma(ctx->dma_lch_in); dma_free_coherent(NULL, ctx->buflen, ctx->buf_in, ctx->dma_addr_in); dma_free_coherent(NULL, ctx->buflen, ctx->buf_out, ctx->dma_addr_out); } static int aes_handle_req(struct aes_hwa_ctx *ctx) { struct tf_crypto_aes_operation_state *state; struct crypto_async_request *async_req, *backlog; struct ablkcipher_request *req; unsigned long flags; if (ctx->total) goto start; spin_lock_irqsave(&ctx->lock, flags); backlog = crypto_get_backlog(&ctx->queue); async_req = crypto_dequeue_request(&ctx->queue); if (!async_req) clear_bit(FLAGS_BUSY, &ctx->flags); spin_unlock_irqrestore(&ctx->lock, flags); if (!async_req) return 0; if (backlog) backlog->complete(backlog, -EINPROGRESS); req = ablkcipher_request_cast(async_req); ctx->req = req; ctx->total = req->nbytes; ctx->in_offset = 0; ctx->in_sg = req->src; ctx->out_offset = 0; ctx->out_sg = req->dst; state = crypto_ablkcipher_ctx(crypto_ablkcipher_reqtfm(req)); if (!AES_CTRL_IS_MODE_ECB(state->CTRL)) { u32 *ptr = (u32 *) req->info; state->AES_IV_0 = ptr[0]; state->AES_IV_1 = ptr[1]; state->AES_IV_2 = ptr[2]; state->AES_IV_3 = ptr[3]; } start: return aes_dma_start(ctx); } static void aes_tasklet(unsigned long data) { struct aes_hwa_ctx *ctx = (struct aes_hwa_ctx *) data; aes_dma_stop(ctx); aes_handle_req(ctx); } /* Generic */ static int aes_setkey(struct tf_crypto_aes_operation_state *state, const u8 *key, unsigned int keylen) { u32 *ptr = (u32 *)key; switch (keylen) { case 16: state->CTRL |= AES_CTRL_KEY_SIZE_128; break; case 24: state->CTRL |= AES_CTRL_KEY_SIZE_192; break; case 32: state->CTRL |= AES_CTRL_KEY_SIZE_256; break; default: return -EINVAL; } state->KEY1_0 = ptr[0]; state->KEY1_1 = ptr[1]; state->KEY1_2 = ptr[2]; state->KEY1_3 = ptr[3]; if (keylen >= 24) { state->KEY1_4 = ptr[4]; state->KEY1_5 = ptr[5]; } if (keylen == 32) { state->KEY1_6 = ptr[6]; state->KEY1_7 = ptr[7]; } state->key_is_public = 1; return 0; } static int aes_operate(struct ablkcipher_request *req) { unsigned long flags; int err; /* Make sure AES HWA is accessible */ tf_delayed_secure_resume(); spin_lock_irqsave(&aes_ctx->lock, flags); err = ablkcipher_enqueue_request(&aes_ctx->queue, req); spin_unlock_irqrestore(&aes_ctx->lock, flags); if (!test_and_set_bit(FLAGS_BUSY, &aes_ctx->flags)) aes_handle_req(aes_ctx); return err; } static int aes_encrypt(struct ablkcipher_request *req) { struct tf_crypto_aes_operation_state *state = crypto_ablkcipher_ctx(crypto_ablkcipher_reqtfm(req)); state->CTRL |= AES_CTRL_DIRECTION_ENCRYPT; return aes_operate(req); } static int aes_decrypt(struct ablkcipher_request *req) { struct tf_crypto_aes_operation_state *state = crypto_ablkcipher_ctx(crypto_ablkcipher_reqtfm(req)); state->CTRL &= ~(AES_CTRL_DIRECTION_ENCRYPT); state->CTRL |= AES_CTRL_DIRECTION_DECRYPT; return aes_operate(req); } static int aes_sync_operate(struct blkcipher_desc *desc, struct scatterlist *dst, struct scatterlist *src, unsigned int nbytes) { struct crypto_tfm *tfm = crypto_blkcipher_tfm(desc->tfm); struct tf_crypto_aes_operation_state *state = crypto_tfm_ctx(tfm); struct blkcipher_walk walk; int err; if (nbytes % AES_BLOCK_SIZE) return -EINVAL; /* Make sure AES HWA is accessible */ tf_delayed_secure_resume(); tf_crypto_lock_hwa(PUBLIC_CRYPTO_HWA_AES1, LOCK_HWA); tf_crypto_enable_clock(PUBLIC_CRYPTO_AES1_CLOCK_REG); blkcipher_walk_init(&walk, dst, src, nbytes); err = blkcipher_walk_virt(desc, &walk); if (!AES_CTRL_IS_MODE_ECB(state->CTRL)) { u32 *ptr = (u32 *) walk.iv; state->AES_IV_0 = ptr[0]; state->AES_IV_1 = ptr[1]; state->AES_IV_2 = ptr[2]; state->AES_IV_3 = ptr[3]; } while ((nbytes = walk.nbytes)) { if (!tf_aes_update(state, walk.src.virt.addr, walk.dst.virt.addr, nbytes / AES_BLOCK_SIZE)) { err = -EINVAL; break; } /* tf_aes_update processes all the data */ nbytes = 0; err = blkcipher_walk_done(desc, &walk, nbytes); } if (!AES_CTRL_IS_MODE_ECB(state->CTRL)) { u32 *ptr = (u32 *) walk.iv; ptr[0] = state->AES_IV_0; ptr[1] = state->AES_IV_1; ptr[2] = state->AES_IV_2; ptr[3] = state->AES_IV_3; } tf_crypto_disable_clock(PUBLIC_CRYPTO_AES1_CLOCK_REG); tf_crypto_lock_hwa(PUBLIC_CRYPTO_HWA_AES1, UNLOCK_HWA); return err; } static int aes_sync_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst, struct scatterlist *src, unsigned int nbytes) { struct crypto_tfm *tfm = crypto_blkcipher_tfm(desc->tfm); struct tf_crypto_aes_operation_state *state = crypto_tfm_ctx(tfm); state->CTRL |= AES_CTRL_DIRECTION_ENCRYPT; dprintk(KERN_INFO "aes_sync_encrypt nbytes=0x%x\n", nbytes); return aes_sync_operate(desc, dst, src, nbytes); } static int aes_sync_decrypt(struct blkcipher_desc *desc, struct scatterlist *dst, struct scatterlist *src, unsigned int nbytes) { struct crypto_tfm *tfm = crypto_blkcipher_tfm(desc->tfm); struct tf_crypto_aes_operation_state *state = crypto_tfm_ctx(tfm); state->CTRL &= ~(AES_CTRL_DIRECTION_ENCRYPT); state->CTRL |= AES_CTRL_DIRECTION_DECRYPT; dprintk(KERN_INFO "aes_sync_decrypt\n"); return aes_sync_operate(desc, dst, src, nbytes); } /* AES ECB */ static int aes_ecb_sync_setkey(struct crypto_tfm *tfm, const u8 *key, unsigned int keylen) { struct tf_crypto_aes_operation_state *state = crypto_tfm_ctx(tfm); state->CTRL = AES_CTRL_MODE_ECB_BIT; dprintk(KERN_INFO "aes_ecb_sync_setkey\n"); return aes_setkey(state, key, keylen); } static int aes_ecb_setkey(struct crypto_ablkcipher *tfm, const u8 *key, unsigned int keylen) { struct tf_crypto_aes_operation_state *state = crypto_ablkcipher_ctx(tfm); state->CTRL = AES_CTRL_MODE_ECB_BIT; return aes_setkey(state, key, keylen); } /* AES CBC */ static int aes_cbc_sync_setkey(struct crypto_tfm *tfm, const u8 *key, unsigned int keylen) { struct tf_crypto_aes_operation_state *state = crypto_tfm_ctx(tfm); state->CTRL = AES_CTRL_MODE_CBC_BIT; dprintk(KERN_INFO "aes_cbc_sync_setkey\n"); return aes_setkey(state, key, keylen); } static int aes_cbc_setkey(struct crypto_ablkcipher *tfm, const u8 *key, unsigned int keylen) { struct tf_crypto_aes_operation_state *state = crypto_ablkcipher_ctx(tfm); state->CTRL = AES_CTRL_MODE_CBC_BIT; return aes_setkey(state, key, keylen); } /* AES CTR */ static int aes_ctr_sync_setkey(struct crypto_tfm *tfm, const u8 *key, unsigned int keylen) { struct tf_crypto_aes_operation_state *state = crypto_tfm_ctx(tfm); state->CTRL = AES_CTRL_MODE_CTR_BIT; dprintk(KERN_INFO "aes_cbc_sync_setkey\n"); return aes_setkey(state, key, keylen); } static int aes_ctr_setkey(struct crypto_ablkcipher *tfm, const u8 *key, unsigned int keylen) { struct tf_crypto_aes_operation_state *state = crypto_ablkcipher_ctx(tfm); /* Always defaults to 128-bit counter */ state->CTRL = AES_CTRL_MODE_CTR_BIT | AES_CTRL_CTR_WIDTH_128; return aes_setkey(state, key, keylen); } static struct crypto_alg smc_aes_ecb_sync_alg = { .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, .cra_priority = 999, .cra_name = "ecb(aes)", .cra_driver_name = "aes-ecb-smc", .cra_type = &crypto_blkcipher_type, .cra_module = THIS_MODULE, .cra_blocksize = AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tf_crypto_aes_operation_state), .cra_alignmask = 3, .cra_list = LIST_HEAD_INIT(smc_aes_ecb_sync_alg.cra_list), .cra_u = { .blkcipher = { .min_keysize = AES_MIN_KEY_SIZE, .max_keysize = AES_MAX_KEY_SIZE, .setkey = aes_ecb_sync_setkey, .encrypt = aes_sync_encrypt, .decrypt = aes_sync_decrypt, } }, }; static struct crypto_alg smc_aes_cbc_sync_alg = { .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, .cra_priority = 999, .cra_name = "cbc(aes)", .cra_driver_name = "aes-cbc-smc", .cra_type = &crypto_blkcipher_type, .cra_module = THIS_MODULE, .cra_blocksize = AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tf_crypto_aes_operation_state), .cra_alignmask = 3, .cra_list = LIST_HEAD_INIT(smc_aes_cbc_sync_alg.cra_list), .cra_u = { .blkcipher = { .min_keysize = AES_MIN_KEY_SIZE, .max_keysize = AES_MAX_KEY_SIZE, .ivsize = PUBLIC_CRYPTO_IV_MAX_SIZE, .setkey = aes_cbc_sync_setkey, .encrypt = aes_sync_encrypt, .decrypt = aes_sync_decrypt, } }, }; static struct crypto_alg smc_aes_ctr_sync_alg = { .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, .cra_priority = 999, .cra_name = "ctr(aes)", .cra_driver_name = "aes-ctr-smc", .cra_type = &crypto_blkcipher_type, .cra_module = THIS_MODULE, .cra_blocksize = AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tf_crypto_aes_operation_state), .cra_alignmask = 3, .cra_list = LIST_HEAD_INIT(smc_aes_ctr_sync_alg.cra_list), .cra_u = { .blkcipher = { .min_keysize = AES_MIN_KEY_SIZE, .max_keysize = AES_MAX_KEY_SIZE, .ivsize = PUBLIC_CRYPTO_IV_MAX_SIZE, .setkey = aes_ctr_sync_setkey, .encrypt = aes_sync_encrypt, .decrypt = aes_sync_decrypt, } }, }; static struct crypto_alg smc_aes_ecb_alg = { .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC, .cra_priority = 999, .cra_name = "ecb(aes)", .cra_driver_name = "aes-ecb-smc", .cra_type = &crypto_ablkcipher_type, .cra_module = THIS_MODULE, .cra_blocksize = AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tf_crypto_aes_operation_state), .cra_alignmask = 3, .cra_list = LIST_HEAD_INIT(smc_aes_ecb_alg.cra_list), .cra_u = { .ablkcipher = { .min_keysize = AES_MIN_KEY_SIZE, .max_keysize = AES_MAX_KEY_SIZE, .setkey = aes_ecb_setkey, .encrypt = aes_encrypt, .decrypt = aes_decrypt, } }, }; static struct crypto_alg smc_aes_cbc_alg = { .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC, .cra_priority = 999, .cra_name = "cbc(aes)", .cra_driver_name = "aes-cbc-smc", .cra_module = THIS_MODULE, .cra_type = &crypto_ablkcipher_type, .cra_blocksize = AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tf_crypto_aes_operation_state), .cra_alignmask = 3, .cra_list = LIST_HEAD_INIT(smc_aes_cbc_alg.cra_list), .cra_u = { .ablkcipher = { .min_keysize = AES_MIN_KEY_SIZE, .max_keysize = AES_MAX_KEY_SIZE, .ivsize = PUBLIC_CRYPTO_IV_MAX_SIZE, .setkey = aes_cbc_setkey, .encrypt = aes_encrypt, .decrypt = aes_decrypt, } }, }; static struct crypto_alg smc_aes_ctr_alg = { .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC, .cra_priority = 999, .cra_name = "ctr(aes)", .cra_driver_name = "aes-ctr-smc", .cra_module = THIS_MODULE, .cra_type = &crypto_ablkcipher_type, .cra_blocksize = AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct tf_crypto_aes_operation_state), .cra_alignmask = 3, .cra_list = LIST_HEAD_INIT(smc_aes_ctr_alg.cra_list), .cra_u = { .ablkcipher = { .min_keysize = AES_MIN_KEY_SIZE, .max_keysize = AES_MAX_KEY_SIZE, .ivsize = PUBLIC_CRYPTO_IV_MAX_SIZE, .setkey = aes_ctr_setkey, .encrypt = aes_encrypt, .decrypt = aes_decrypt, } }, }; int register_smc_public_crypto_aes(void) { int ret; aes_ctx = kzalloc(sizeof(struct aes_hwa_ctx), GFP_KERNEL); if (aes_ctx == NULL) return -ENOMEM; crypto_init_queue(&aes_ctx->queue, 1); tasklet_init(&aes_ctx->task, aes_tasklet, (unsigned long)aes_ctx); spin_lock_init(&aes_ctx->lock); aes_ctx->dma_in = OMAP44XX_DMA_AES1_P_DATA_IN_REQ; aes_ctx->dma_out = OMAP44XX_DMA_AES1_P_DATA_OUT_REQ; ret = aes_dma_init(aes_ctx); if (ret) goto err_dma; ret = crypto_register_alg(&smc_aes_ecb_sync_alg); if (ret) goto err_ecb_sync; ret = crypto_register_alg(&smc_aes_cbc_sync_alg); if (ret) goto err_cbc_sync; ret = crypto_register_alg(&smc_aes_ctr_sync_alg); if (ret) goto err_ctr_sync; ret = crypto_register_alg(&smc_aes_ecb_alg); if (ret) goto err_ecb; ret = crypto_register_alg(&smc_aes_cbc_alg); if (ret) goto err_cbc; ret = crypto_register_alg(&smc_aes_ctr_alg); if (ret) goto err_ctr; return 0; err_ctr: crypto_unregister_alg(&smc_aes_cbc_alg); err_cbc: crypto_unregister_alg(&smc_aes_ecb_alg); err_ecb: crypto_unregister_alg(&smc_aes_ctr_sync_alg); err_ctr_sync: crypto_unregister_alg(&smc_aes_cbc_sync_alg); err_cbc_sync: crypto_unregister_alg(&smc_aes_ecb_sync_alg); err_ecb_sync: aes_dma_cleanup(aes_ctx); err_dma: tasklet_kill(&aes_ctx->task); kfree(aes_ctx); return ret; } void unregister_smc_public_crypto_aes(void) { if (aes_ctx == NULL) return; crypto_unregister_alg(&smc_aes_ecb_sync_alg); crypto_unregister_alg(&smc_aes_cbc_sync_alg); crypto_unregister_alg(&smc_aes_ctr_sync_alg); crypto_unregister_alg(&smc_aes_ecb_alg); crypto_unregister_alg(&smc_aes_cbc_alg); crypto_unregister_alg(&smc_aes_ctr_alg); aes_dma_cleanup(aes_ctx); tasklet_kill(&aes_ctx->task); kfree(aes_ctx); } #endif