/*
* Support for Marvell's crypto engine which can be found on some Orion5X
* boards.
*
* Author: Sebastian Andrzej Siewior < sebastian at breakpoint dot cc >
* License: GPLv2
*
*/
#include <crypto/aes.h>
#include <crypto/algapi.h>
#include <linux/crypto.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kthread.h>
#include <linux/platform_device.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <crypto/internal/hash.h>
#include <crypto/sha.h>
#include "mv_cesa.h"
#define MV_CESA "MV-CESA:"
#define MAX_HW_HASH_SIZE 0xFFFF
/*
* STM:
* /---------------------------------------\
* | | request complete
* \./ |
* IDLE -> new request -> BUSY -> done -> DEQUEUE
* /°\ |
* | | more scatter entries
* \________________/
*/
enum engine_status {
ENGINE_IDLE,
ENGINE_BUSY,
ENGINE_W_DEQUEUE,
};
/**
* struct req_progress - used for every crypt request
* @src_sg_it: sg iterator for src
* @dst_sg_it: sg iterator for dst
* @sg_src_left: bytes left in src to process (scatter list)
* @src_start: offset to add to src start position (scatter list)
* @crypt_len: length of current hw crypt/hash process
* @hw_nbytes: total bytes to process in hw for this request
* @copy_back: whether to copy data back (crypt) or not (hash)
* @sg_dst_left: bytes left dst to process in this scatter list
* @dst_start: offset to add to dst start position (scatter list)
* @hw_processed_bytes: number of bytes processed by hw (request).
*
* sg helper are used to iterate over the scatterlist. Since the size of the
* SRAM may be less than the scatter size, this struct struct is used to keep
* track of progress within current scatterlist.
*/
struct req_progress {
struct sg_mapping_iter src_sg_it;
struct sg_mapping_iter dst_sg_it;
void (*complete) (void);
void (*process) (int is_first);
/* src mostly */
int sg_src_left;
int src_start;
int crypt_len;
int hw_nbytes;
/* dst mostly */
int copy_back;
int sg_dst_left;
int dst_start;
int hw_processed_bytes;
};
struct crypto_priv {
void __iomem *reg;
void __iomem *sram;
int irq;
struct task_struct *queue_th;
/* the lock protects queue and eng_st */
spinlock_t lock;
struct crypto_queue queue;
enum engine_status eng_st;
struct crypto_async_request *cur_req;
struct req_progress p;
int max_req_size;
int sram_size;
int has_sha1;
int has_hmac_sha1;
};
static struct crypto_priv *cpg;
struct mv_ctx {
u8 aes_enc_key[AES_KEY_LEN];
u32 aes_dec_key[8];
int key_len;
u32 need_calc_aes_dkey;
};
enum crypto_op {
COP_AES_ECB,
COP_AES_CBC,
};
struct mv_req_ctx {
enum crypto_op op;
int decrypt;
};
enum hash_op {
COP_SHA1,
COP_HMAC_SHA1
};
struct mv_tfm_hash_ctx {
struct crypto_shash *fallback;
struct crypto_shash *base_hash;
u32 ivs[2 * SHA1_DIGEST_SIZE / 4];
int count_add;
enum hash_op op;
};
struct mv_req_hash_ctx {
u64 count;
u32 state[SHA1_DIGEST_SIZE / 4];
u8 buffer[SHA1_BLOCK_SIZE];
int first_hash; /* marks that we don't have previous state */
int last_chunk; /* marks that this is the 'final' request */
int extra_bytes; /* unprocessed bytes in buffer */
enum hash_op op;
int count_add;
};
static void compute_aes_dec_key(struct mv_ctx *ctx)
{
struct crypto_aes_ctx gen_aes_key;
int key_pos;
if (!ctx->need_calc_aes_dkey)
return;
crypto_aes_expand_key(&gen_aes_key, ctx->aes_enc_key, ctx->key_len);
key_pos = ctx->key_len + 24;
memcpy(ctx->aes_dec_key, &gen_aes_key.key_enc[key_pos], 4 * 4);
switch (ctx->key_len) {
case AES_KEYSIZE_256:
key_pos -= 2;
/* fall */
case AES_KEYSIZE_192:
key_pos -= 2;
memcpy(&ctx->aes_dec_key[4], &gen_aes_key.key_enc[key_pos],
4 * 4);