blob: 716a60f789364557da79eb175ebea958ead6dfd1 [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_crypto.h"
#include "tf_dma.h"
#include <linux/io.h>
#include <mach/io.h>
/*
* DES Hardware Accelerator: Base address
*/
#define DES_REGS_HW_ADDR 0x480A5000
/*
* CTRL register Masks
*/
#define DES_CTRL_OUTPUT_READY_BIT (1<<0)
#define DES_CTRL_INPUT_READY_BIT (1<<1)
#define DES_CTRL_GET_DIRECTION(x) (x&4)
#define DES_CTRL_DIRECTION_DECRYPT 0
#define DES_CTRL_DIRECTION_ENCRYPT (1<<2)
#define DES_CTRL_GET_TDES(x) (x&8)
#define DES_CTRL_TDES_DES 0
#define DES_CTRL_TDES_TRIPLE_DES (1<<3)
#define DES_CTRL_GET_MODE(x) (x&0x10)
#define DES_CTRL_MODE_ECB 0
#define DES_CTRL_MODE_CBC (1<<4)
/*
* SYSCONFIG register masks
*/
#define DES_SYSCONFIG_DMA_REQ_IN_EN_BIT (1<<5)
#define DES_SYSCONFIG_DMA_REQ_OUT_EN_BIT (1<<6)
/*------------------------------------------------------------------------*/
/* DES/DES3 Context */
/*------------------------------------------------------------------------*/
/**
* This structure contains the registers of the DES HW accelerator.
*/
struct des3_des_reg {
u32 DES_KEY3_L; /* DES Key 3 Low Register */
u32 DES_KEY3_H; /* DES Key 3 High Register */
u32 DES_KEY2_L; /* DES Key 2 Low Register */
u32 DES_KEY2_H; /* DES Key 2 High Register */
u32 DES_KEY1_L; /* DES Key 1 Low Register */
u32 DES_KEY1_H; /* DES Key 1 High Register */
u32 DES_IV_L; /* DES Initialization Vector Low Reg */
u32 DES_IV_H; /* DES Initialization Vector High Reg */
u32 DES_CTRL; /* DES Control Register */
u32 DES_LENGTH; /* DES Length Register */
u32 DES_DATA_L; /* DES Data Input/Output Low Register */
u32 DES_DATA_H; /* DES Data Input/Output High Register */
u32 DES_REV; /* DES Revision Register */
u32 DES_SYSCONFIG; /* DES Mask and Reset Register */
u32 DES_SYSSTATUS; /* DES System Status Register */
};
static struct des3_des_reg *des_reg;
/*------------------------------------------------------------------------
*Forward declarations
*------------------------------------------------------------------------ */
static bool tf_des_update_dma(u8 *src, u8 *dest, u32 nb_blocks);
/*-------------------------------------------------------------------------
*Save HWA registers into the specified operation state structure
*-------------------------------------------------------------------------*/
static void tf_des_save_registers(u32 DES_CTRL,
struct tf_crypto_des_operation_state *des_state)
{
dprintk(KERN_INFO
"tf_des_save_registers in des_state=%p CTRL=0x%08x\n",
des_state, DES_CTRL);
/*Save the IV if we are in CBC mode */
if (DES_CTRL_GET_MODE(DES_CTRL) == DES_CTRL_MODE_CBC) {
des_state->DES_IV_L = INREG32(&des_reg->DES_IV_L);
des_state->DES_IV_H = INREG32(&des_reg->DES_IV_H);
}
}
/*-------------------------------------------------------------------------
*Restore the HWA registers from the operation state structure
*-------------------------------------------------------------------------*/
static void tf_des_restore_registers(u32 DES_CTRL,
struct tf_crypto_des_operation_state *des_state)
{
dprintk(KERN_INFO "tf_des_restore_registers from "
"des_state=%p CTRL=0x%08x\n",
des_state, DES_CTRL);
/*Write the IV ctx->reg */
if (DES_CTRL_GET_MODE(DES_CTRL) == DES_CTRL_MODE_CBC) {
OUTREG32(&des_reg->DES_IV_L, des_state->DES_IV_L);
OUTREG32(&des_reg->DES_IV_H, des_state->DES_IV_H);
}
/*Set the DIRECTION and CBC bits in the CTRL register.
*Keep the TDES from the accelerator */
OUTREG32(&des_reg->DES_CTRL,
(INREG32(&des_reg->DES_CTRL) & (1 << 3)) |
(DES_CTRL & ((1 << 2) | (1 << 4))));
/*Set the SYSCONFIG register to 0 */
OUTREG32(&des_reg->DES_SYSCONFIG, 0);
}
/*------------------------------------------------------------------------- */
void tf_des_init(void)
{
des_reg = omap_ioremap(DES_REGS_HW_ADDR, SZ_1M, MT_DEVICE);
if (des_reg == NULL)
panic("Unable to remap DES/3DES module");
}
void tf_des_exit(void)
{
omap_iounmap(des_reg);
}
bool tf_des_update(u32 DES_CTRL,
struct tf_crypto_des_operation_state *des_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;
/*
*Choice of the processing type
*/
if (nb_blocks * DES_BLOCK_SIZE >= DMA_TRIGGER_IRQ_DES)
dma_use = PUBLIC_CRYPTO_DMA_USE_IRQ;
dprintk(KERN_INFO "tf_des_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 (nb_blocks == 0) {
dprintk(KERN_INFO "tf_des_update: Nothing to process\n");
return true;
}
if (DES_CTRL_GET_DIRECTION(INREG32(&des_reg->DES_CTRL)) !=
DES_CTRL_GET_DIRECTION(DES_CTRL)) {
dprintk(KERN_WARNING "HWA configured for another direction\n");
return false;
}
/*Restore the registers of the accelerator from the operation state */
tf_des_restore_registers(DES_CTRL, des_state);
OUTREG32(&des_reg->DES_LENGTH, nb_blocks * DES_BLOCK_SIZE);
if (dma_use == PUBLIC_CRYPTO_DMA_USE_IRQ) {
/*perform the update with DMA */
if (!tf_des_update_dma(src, dest, nb_blocks))
return false;
} else {
u8 buf[DMA_TRIGGER_IRQ_DES];
process_src = process_dest = buf;
if (copy_from_user(buf, src, nb_blocks * DES_BLOCK_SIZE))
return false;
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 *)&des_reg->DES_CTRL,
DES_CTRL_INPUT_READY_BIT) !=
PUBLIC_CRYPTO_OPERATION_SUCCESS) {
panic("Wait too long for DES HW "
"accelerator Input data to be ready\n");
}
/*We copy the 8 bytes of data src->reg */
temp = (u32) BYTES_TO_LONG(process_src);
OUTREG32(&des_reg->DES_DATA_L, temp);
process_src += 4;
temp = (u32) BYTES_TO_LONG(process_src);
OUTREG32(&des_reg->DES_DATA_H, temp);
process_src += 4;
/*We wait for the output ready */
tf_crypto_wait_for_ready_bit_infinitely(
(u32 *)&des_reg->DES_CTRL,
DES_CTRL_OUTPUT_READY_BIT);
/*We copy the 8 bytes of data reg->dest */
temp = INREG32(&des_reg->DES_DATA_L);
LONG_TO_BYTE(process_dest, temp);
process_dest += 4;
temp = INREG32(&des_reg->DES_DATA_H);
LONG_TO_BYTE(process_dest, temp);
process_dest += 4;
}
if (copy_to_user(dest, buf, nb_blocks * DES_BLOCK_SIZE))
return false;
}
/*Save the accelerator registers into the operation state */
tf_des_save_registers(DES_CTRL, des_state);
dprintk(KERN_INFO "tf_des_update: Done\n");
return true;
}
/*------------------------------------------------------------------------- */
/*
*Static function, perform DES 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)
*output: dest : pointer of the output data (can be eq to src)
*/
static bool tf_des_update_dma(u8 *src, u8 *dest, u32 nb_blocks)
{
/*
*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 * DES_BLOCK_SIZE;
u32 length_loop = 0;
u32 nb_blocksLoop = 0;
struct tf_device *dev = tf_get_device();
dprintk(KERN_INFO
"tf_des_update_dma: In=0x%08x, Out=0x%08x, Len=%u\n",
(unsigned int)src, (unsigned int)dest,
(unsigned int)length);
/*lock the DMA */
mutex_lock(&dev->sm.dma_mutex);
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(&des_reg->DES_SYSCONFIG,
INREG32(&des_reg->DES_SYSCONFIG)
| DES_SYSCONFIG_DMA_REQ_OUT_EN_BIT
| DES_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 / DES_BLOCK_SIZE;
/*
* Copy the data from the user input buffer into a preallocated
* buffer which has correct properties from efficient DMA
* transfers.
*/
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;
}
/* DMA1: Mem -> DES */
tf_dma_set_channel_common_params(&ch0_parameters,
nb_blocksLoop,
DMA_CEN_Elts_per_Frame_DES,
DES_REGS_HW_ADDR + 0x28,
dev->dma_buffer_phys,
OMAP44XX_DMA_DES_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
"tf_des_update_dma: omap_set_dma_params(ch0)\n");
omap_set_dma_params(dma_ch0, &ch0_parameters);
/* DMA2: DES -> Mem */
tf_dma_set_channel_common_params(&ch1_parameters,
nb_blocksLoop,
DMA_CEN_Elts_per_Frame_DES,
dev->dma_buffer_phys,
DES_REGS_HW_ADDR + 0x28,
OMAP44XX_DMA_DES_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 "tf_des_update_dma: "
"omap_set_dma_params(ch1)\n");
omap_set_dma_params(dma_ch1, &ch1_parameters);
wmb();
dprintk(KERN_INFO
"tf_des_update_dma: Start DMA channel %d\n",
(unsigned int)dma_ch0);
tf_dma_start(dma_ch0, OMAP_DMA_BLOCK_IRQ);
dprintk(KERN_INFO
"tf_des_update_dma: Start DMA channel %d\n",
(unsigned int)dma_ch1);
tf_dma_start(dma_ch1, OMAP_DMA_BLOCK_IRQ);
tf_dma_wait(2);
/* Unset DMA synchronisation requests */
OUTREG32(&des_reg->DES_SYSCONFIG,
INREG32(&des_reg->DES_SYSCONFIG)
& (~DES_SYSCONFIG_DMA_REQ_OUT_EN_BIT)
& (~DES_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 (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;
}
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 "tf_des_update_dma: Success\n");
return true;
}