Skip to content

Instantly share code, notes, and snippets.

@zeroXten
Created May 7, 2015 08:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zeroXten/a0bbc92e85fdab87d3ae to your computer and use it in GitHub Desktop.
Save zeroXten/a0bbc92e85fdab87d3ae to your computer and use it in GitHub Desktop.
arch/arm/mach-oxnas/cipher.c
/* linux/arch/arm/mach-oxnas/cipher.c
*
* 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.
*/
#include <linux/config.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/device.h>
#include <linux/string.h>
#include <linux/sysdev.h>
#include <linux/highmem.h>
#include <asm/semaphore.h>
#include <asm/arch/cipher.h>
#include <asm/io.h>
#include <asm/arch/hardware.h>
#include <linux/dma-mapping.h>
#include <asm/arch/dma.h>
#include <asm-arm/page.h>
#if 0
#define DPRINTK(fmt, args...) printk(KERN_ERR "%s: " fmt, __FUNCTION__, ## args)
#define VPRINTK(fmt, args...) printk(KERN_ERR "%s: " fmt, __FUNCTION__, ## args)
#else
#define DPRINTK(fmt, args...)
#define VPRINTK(fmt, args...)
#endif
//#define CIPHER_USE_SG_DMA
/*****************************************************************************/
typedef struct ox800_aeslrw_driver ox800_aeslrw_driver_t;
struct ox800_aeslrw_driver {
struct device dev;
struct semaphore core;
int result;
u8 cipher_key[OX800DPE_KEYSIZE];
u8 tweak_key[OX800DPE_KEYSIZE];
};
static ox800_aeslrw_driver_t ox800_aeslrw_driver;
/*****************************************************************************/
/**
* Sets the keys only if they have changed.
* @param cipher_key 16 byte array that is the cipher key
* @param tweak_key 16 byte array that is the I-Value tweak key
*/
static void ox800_aeslrw_setkeys(u8* cipher_key, u8* tweak_key)
{
VPRINTK(KERN_INFO"\n");
/*
* changing the keys can take a long time as the core will
* compute internal values based on the keys
*/
if (memcmp(&(ox800_aeslrw_driver.cipher_key[0]), cipher_key, OX800DPE_KEYSIZE) ||
memcmp(&(ox800_aeslrw_driver.tweak_key[0]), tweak_key, OX800DPE_KEYSIZE) )
{
u32* key;
unsigned int i;
DPRINTK(KERN_INFO"cipher key =");
for (i = 0; i < OX800DPE_KEYSIZE; ++i)
DPRINTK("%02x", cipher_key[i]);
DPRINTK("\n");
DPRINTK(KERN_INFO"tweak key =");
for (i = 0; i < OX800DPE_KEYSIZE; ++i)
DPRINTK("%02x", tweak_key[i]);
DPRINTK("\n");
/* update stored values */
memcpy(&(ox800_aeslrw_driver.cipher_key[0]), cipher_key, OX800DPE_KEYSIZE);
memcpy(&(ox800_aeslrw_driver.tweak_key[0]), tweak_key, OX800DPE_KEYSIZE);
/* update hardware values */
key = (u32* )cipher_key;
writel(key[0], OX800DPE_KEY00 );
writel(key[1], OX800DPE_KEY01 );
writel(key[2], OX800DPE_KEY02 );
writel(key[3], OX800DPE_KEY03 );
key = (u32* )tweak_key;
writel(key[0], OX800DPE_KEY10 );
writel(key[1], OX800DPE_KEY11 );
writel(key[2], OX800DPE_KEY12 );
writel(key[3], OX800DPE_KEY13 );
}
}
/**
* Generic LRW-AES en/decryption
* @param encrypt non-zero to encrypt, zero to decrypt
* @param in Source of data
* @param out Location to place en/decrypted data
* @param nents Number of entries in scatter list, in and out must have the same
* number of entries
* @param iv 8 byte array containing the I-Value
* @return error code or 0 for success
*/
static int ox800_aeslrw_gencrypt( u8 encrypt,
struct scatterlist* in,
struct scatterlist* out,
unsigned int nents,
u8 iv[])
{
oxnas_dma_channel_t* dma_in;
oxnas_dma_channel_t* dma_out;
struct scatterlist* out_;
char same_buffer;
int status = 0;
/* get dma resources (non blocking) */
dma_in = oxnas_dma_request(0);
dma_out = oxnas_dma_request(0);
VPRINTK("dma in %d out %d \n",
dma_in->channel_number_,
dma_out->channel_number_);
if ((dma_in) && (dma_out)) {
u32 reg;
// shouldn't be busy or full
reg = readl( OX800DPE_STATUS );
if (! (reg & OX800DPE_STAT_IDLE) )
printk("not idle after abort toggle");
if (reg & OX800DPE_STAT_TX_NOTEMPTY)
printk("tx fifo not empty after abort toggle");
if (! (reg & OX800DPE_STAT_RX_SPACE) )
printk("rx not empty after abort toggle");
/* check to see if the destination buffer is the same as the source */
same_buffer = (in->page == out->page);
/* map transfers */
if (same_buffer) {
dma_map_sg(NULL, in, nents, DMA_BIDIRECTIONAL);
out_ = in;
} else {
/* map transfers */
dma_map_sg(NULL, in, nents, DMA_TO_DEVICE);
dma_map_sg(NULL, out, nents, DMA_FROM_DEVICE);
out_ = out;
}
#ifdef CIPHER_USE_SG_DMA
/* setup DMA transfers */
oxnas_dma_device_set_sg(
dma_in,
OXNAS_DMA_TO_DEVICE,
in,
nents,
&oxnas_dpe_rx_dma_settings,
OXNAS_DMA_MODE_INC);
oxnas_dma_device_set_sg(
dma_out,
OXNAS_DMA_FROM_DEVICE,
out_,
nents,
&oxnas_dpe_tx_dma_settings,
OXNAS_DMA_MODE_INC);
#else
oxnas_dma_device_set(
dma_in,
OXNAS_DMA_TO_DEVICE,
(unsigned char* )sg_dma_address(in),
sg_dma_len(in),
&oxnas_dpe_rx_dma_settings,
OXNAS_DMA_MODE_INC,
1 /*paused */ );
oxnas_dma_device_set(
dma_out,
OXNAS_DMA_FROM_DEVICE,
(unsigned char* )sg_dma_address(out_),
sg_dma_len(out_),
&oxnas_dpe_tx_dma_settings,
OXNAS_DMA_MODE_INC,
1 /*paused */ );
#endif
/* set dma callbacks */
oxnas_dma_set_callback(
dma_in,
OXNAS_DMA_CALLBACK_ARG_NUL,
OXNAS_DMA_CALLBACK_ARG_NUL);
oxnas_dma_set_callback(
dma_out,
OXNAS_DMA_CALLBACK_ARG_NUL,
OXNAS_DMA_CALLBACK_ARG_NUL);
/* set for AES LRW encryption or decryption */
writel( (encrypt ? OX800DPE_CTL_DIRECTION_ENC : 0 ) |
OX800DPE_CTL_MODE_LRW_AES,
OX800DPE_CONTROL);
wmb();
/* write in I-value */
writel(*((u32* )&(iv[0])), OX800DPE_DATA_LRW0 );
writel(*((u32* )&(iv[4])), OX800DPE_DATA_LRW1 );
wmb();
/* wait until done */
while( !(OX800DPE_STAT_IDLE & readl( OX800DPE_STATUS )) );
/* start dma */
oxnas_dma_start(dma_out);
oxnas_dma_start(dma_in);
/* wait (once for each channel) */
while ( oxnas_dma_is_active( dma_out ) ||
oxnas_dma_is_active( dma_in ) )
{
schedule();
}
/* free any allocated dma channels */
oxnas_dma_free( dma_in );
oxnas_dma_free( dma_out );
/* unmap transfers */
if (same_buffer) {
dma_unmap_sg(NULL, in, nents, DMA_BIDIRECTIONAL);
} else {
dma_unmap_sg(NULL, in, nents, DMA_TO_DEVICE);
dma_unmap_sg(NULL, out, nents, DMA_FROM_DEVICE);
}
status = ox800_aeslrw_driver.result;
} else {
/* free any allocated dma channels */
if (dma_in)
oxnas_dma_free( dma_in );
if (dma_out)
oxnas_dma_free( dma_out );
status = -EBUSY;
}
/* return an indication of success */
return status;
}
/**
* Performs LRW-AES encryption.
* @param in Source of data
* @param out Location to place encrypted data
* @param nents Number of entries in scatter list, in and out must have the same
* number of entries
* @param iv I-Value
* @param cipher_key 16 byte array that is the cipher key
* @param tweak_key 16 byte array that is the I-Value tweak key
* @return error code or 0 for success
*/
int ox800_aeslrw_encrypt( struct scatterlist* in,
struct scatterlist* out,
unsigned int nents,
u8* iv,
u8* cipher_key,
u8* tweak_key)
{
int localresult;
VPRINTK(KERN_INFO"in %p, out %p, nents %d, iv %08x%08x, ckey %p, tkey %p\n",
in, out, nents, *((u32* )(&iv[4])), *((u32* )(&iv[0])), cipher_key, tweak_key );
/* get cipher core */
while( down_interruptible(&ox800_aeslrw_driver.core) ) ;
VPRINTK(KERN_INFO"got core\n");
ox800_aeslrw_setkeys(cipher_key, tweak_key);
localresult = ox800_aeslrw_gencrypt( 1, in, out, nents, iv);
up(&ox800_aeslrw_driver.core);
VPRINTK(KERN_INFO"released\n");
return localresult;
}
/**
* Performs LRW-AES decryption.
* @param in Source of data
* @param out Location to place decrypted data
* @param nents Number of entries in scatter list, in and out must have the same
* number of entries
* @param iv I-Value
* @param cipher_key 16 byte array that is the cipher key
* @param tweak_key 16 byte array that is the I-Value tweak key
* @return error code or 0 for success
*/
int ox800_aeslrw_decrypt( struct scatterlist* in,
struct scatterlist* out,
unsigned int nents,
u8* iv,
u8* cipher_key,
u8* tweak_key)
{
int localresult;
VPRINTK(KERN_INFO"in %p, out %p, nents %d, iv %08x%08x, ckey %p, tkey%p\n",
in, out, nents, *((u32* )(&iv[4])), *((u32* )(&iv[0])), cipher_key, tweak_key );
/* get cipher core */
while( down_interruptible(&ox800_aeslrw_driver.core) ) ;
VPRINTK(KERN_INFO"got core\n");
ox800_aeslrw_setkeys(cipher_key, tweak_key);
localresult = ox800_aeslrw_gencrypt( 0, in, out, nents, iv);
up(&ox800_aeslrw_driver.core);
VPRINTK(KERN_INFO"released core \n");
return localresult;
}
/**
* module initialisation
* @return success is 0
*/
static int __init ox800_aeslrw_init( void )
{
VPRINTK(KERN_INFO"\n");
/* Enable the clock to the DPE block */
writel(1UL << SYS_CTRL_CKEN_DPE_BIT, SYS_CTRL_CKEN_SET_CTRL);
/* Bring out of reset */
writel(1UL << SYS_CTRL_RSTEN_DPE_BIT, SYS_CTRL_RSTEN_CLR_CTRL);
/* initialise in unlocked state */
init_MUTEX(&ox800_aeslrw_driver.core);
return 0;
}
/**
* module cleanup
*/
static void __exit ox800_aeslrw_exit( void )
{
}
/**
* macros to register intiialisation and exit functions with kernal
*/
module_init(ox800_aeslrw_init);
module_exit(ox800_aeslrw_exit);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment