linux/drivers/crypto/mv_cesa.c
Tejun Heo 5a0e3ad6af include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files.  percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.

percpu.h -> slab.h dependency is about to be removed.  Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability.  As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.

  http://userweb.kernel.org/~tj/misc/slabh-sweep.py

The script does the followings.

* Scan files for gfp and slab usages and update includes such that
  only the necessary includes are there.  ie. if only gfp is used,
  gfp.h, if slab is used, slab.h.

* When the script inserts a new include, it looks at the include
  blocks and try to put the new include such that its order conforms
  to its surrounding.  It's put in the include block which contains
  core kernel includes, in the same order that the rest are ordered -
  alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
  doesn't seem to be any matching order.

* If the script can't find a place to put a new include (mostly
  because the file doesn't have fitting include block), it prints out
  an error message indicating which .h file needs to be added to the
  file.

The conversion was done in the following steps.

1. The initial automatic conversion of all .c files updated slightly
   over 4000 files, deleting around 700 includes and adding ~480 gfp.h
   and ~3000 slab.h inclusions.  The script emitted errors for ~400
   files.

2. Each error was manually checked.  Some didn't need the inclusion,
   some needed manual addition while adding it to implementation .h or
   embedding .c file was more appropriate for others.  This step added
   inclusions to around 150 files.

3. The script was run again and the output was compared to the edits
   from #2 to make sure no file was left behind.

4. Several build tests were done and a couple of problems were fixed.
   e.g. lib/decompress_*.c used malloc/free() wrappers around slab
   APIs requiring slab.h to be added manually.

5. The script was run on all .h files but without automatically
   editing them as sprinkling gfp.h and slab.h inclusions around .h
   files could easily lead to inclusion dependency hell.  Most gfp.h
   inclusion directives were ignored as stuff from gfp.h was usually
   wildly available and often used in preprocessor macros.  Each
   slab.h inclusion directive was examined and added manually as
   necessary.

6. percpu.h was updated not to include slab.h.

7. Build test were done on the following configurations and failures
   were fixed.  CONFIG_GCOV_KERNEL was turned off for all tests (as my
   distributed build env didn't work with gcov compiles) and a few
   more options had to be turned off depending on archs to make things
   build (like ipr on powerpc/64 which failed due to missing writeq).

   * x86 and x86_64 UP and SMP allmodconfig and a custom test config.
   * powerpc and powerpc64 SMP allmodconfig
   * sparc and sparc64 SMP allmodconfig
   * ia64 SMP allmodconfig
   * s390 SMP allmodconfig
   * alpha SMP allmodconfig
   * um on x86_64 SMP allmodconfig

8. percpu.h modifications were reverted so that it could be applied as
   a separate patch and serve as bisection point.

Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.

Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-30 22:02:32 +09:00

608 lines
14 KiB
C

/*
* 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 "mv_cesa.h"
/*
* 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 crypt process
* @sg_dst_left: bytes left dst to process in this scatter list
* @dst_start: offset to add to dst start position (scatter list)
* @total_req_bytes: total number of bytes processed (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;
/* src mostly */
int sg_src_left;
int src_start;
int crypt_len;
/* dst mostly */
int sg_dst_left;
int dst_start;
int total_req_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 ablkcipher_request *cur_req;
struct req_progress p;
int max_req_size;
int sram_size;
};
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;
};
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);
break;
}
ctx->need_calc_aes_dkey = 0;
}
static int mv_setkey_aes(struct crypto_ablkcipher *cipher, const u8 *key,
unsigned int len)
{
struct crypto_tfm *tfm = crypto_ablkcipher_tfm(cipher);
struct mv_ctx *ctx = crypto_tfm_ctx(tfm);
switch (len) {
case AES_KEYSIZE_128:
case AES_KEYSIZE_192:
case AES_KEYSIZE_256:
break;
default:
crypto_ablkcipher_set_flags(cipher, CRYPTO_TFM_RES_BAD_KEY_LEN);
return -EINVAL;
}
ctx->key_len = len;
ctx->need_calc_aes_dkey = 1;
memcpy(ctx->aes_enc_key, key, AES_KEY_LEN);
return 0;
}
static void setup_data_in(struct ablkcipher_request *req)
{
int ret;
void *buf;
if (!cpg->p.sg_src_left) {
ret = sg_miter_next(&cpg->p.src_sg_it);
BUG_ON(!ret);
cpg->p.sg_src_left = cpg->p.src_sg_it.length;
cpg->p.src_start = 0;
}
cpg->p.crypt_len = min(cpg->p.sg_src_left, cpg->max_req_size);
buf = cpg->p.src_sg_it.addr;
buf += cpg->p.src_start;
memcpy(cpg->sram + SRAM_DATA_IN_START, buf, cpg->p.crypt_len);
cpg->p.sg_src_left -= cpg->p.crypt_len;
cpg->p.src_start += cpg->p.crypt_len;
}
static void mv_process_current_q(int first_block)
{
struct ablkcipher_request *req = cpg->cur_req;
struct mv_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
struct mv_req_ctx *req_ctx = ablkcipher_request_ctx(req);
struct sec_accel_config op;
switch (req_ctx->op) {
case COP_AES_ECB:
op.config = CFG_OP_CRYPT_ONLY | CFG_ENCM_AES | CFG_ENC_MODE_ECB;
break;
case COP_AES_CBC:
op.config = CFG_OP_CRYPT_ONLY | CFG_ENCM_AES | CFG_ENC_MODE_CBC;
op.enc_iv = ENC_IV_POINT(SRAM_DATA_IV) |
ENC_IV_BUF_POINT(SRAM_DATA_IV_BUF);
if (first_block)
memcpy(cpg->sram + SRAM_DATA_IV, req->info, 16);
break;
}
if (req_ctx->decrypt) {
op.config |= CFG_DIR_DEC;
memcpy(cpg->sram + SRAM_DATA_KEY_P, ctx->aes_dec_key,
AES_KEY_LEN);
} else {
op.config |= CFG_DIR_ENC;
memcpy(cpg->sram + SRAM_DATA_KEY_P, ctx->aes_enc_key,
AES_KEY_LEN);
}
switch (ctx->key_len) {
case AES_KEYSIZE_128:
op.config |= CFG_AES_LEN_128;
break;
case AES_KEYSIZE_192:
op.config |= CFG_AES_LEN_192;
break;
case AES_KEYSIZE_256:
op.config |= CFG_AES_LEN_256;
break;
}
op.enc_p = ENC_P_SRC(SRAM_DATA_IN_START) |
ENC_P_DST(SRAM_DATA_OUT_START);
op.enc_key_p = SRAM_DATA_KEY_P;
setup_data_in(req);
op.enc_len = cpg->p.crypt_len;
memcpy(cpg->sram + SRAM_CONFIG, &op,
sizeof(struct sec_accel_config));
writel(SRAM_CONFIG, cpg->reg + SEC_ACCEL_DESC_P0);
/* GO */
writel(SEC_CMD_EN_SEC_ACCL0, cpg->reg + SEC_ACCEL_CMD);
/*
* XXX: add timer if the interrupt does not occur for some mystery
* reason
*/
}
static void mv_crypto_algo_completion(void)
{
struct ablkcipher_request *req = cpg->cur_req;
struct mv_req_ctx *req_ctx = ablkcipher_request_ctx(req);
if (req_ctx->op != COP_AES_CBC)
return ;
memcpy(req->info, cpg->sram + SRAM_DATA_IV_BUF, 16);
}
static void dequeue_complete_req(void)
{
struct ablkcipher_request *req = cpg->cur_req;
void *buf;
int ret;
cpg->p.total_req_bytes += cpg->p.crypt_len;
do {
int dst_copy;
if (!cpg->p.sg_dst_left) {
ret = sg_miter_next(&cpg->p.dst_sg_it);
BUG_ON(!ret);
cpg->p.sg_dst_left = cpg->p.dst_sg_it.length;
cpg->p.dst_start = 0;
}
buf = cpg->p.dst_sg_it.addr;
buf += cpg->p.dst_start;
dst_copy = min(cpg->p.crypt_len, cpg->p.sg_dst_left);
memcpy(buf, cpg->sram + SRAM_DATA_OUT_START, dst_copy);
cpg->p.sg_dst_left -= dst_copy;
cpg->p.crypt_len -= dst_copy;
cpg->p.dst_start += dst_copy;
} while (cpg->p.crypt_len > 0);
BUG_ON(cpg->eng_st != ENGINE_W_DEQUEUE);
if (cpg->p.total_req_bytes < req->nbytes) {
/* process next scatter list entry */
cpg->eng_st = ENGINE_BUSY;
mv_process_current_q(0);
} else {
sg_miter_stop(&cpg->p.src_sg_it);
sg_miter_stop(&cpg->p.dst_sg_it);
mv_crypto_algo_completion();
cpg->eng_st = ENGINE_IDLE;
req->base.complete(&req->base, 0);
}
}
static int count_sgs(struct scatterlist *sl, unsigned int total_bytes)
{
int i = 0;
do {
total_bytes -= sl[i].length;
i++;
} while (total_bytes > 0);
return i;
}
static void mv_enqueue_new_req(struct ablkcipher_request *req)
{
int num_sgs;
cpg->cur_req = req;
memset(&cpg->p, 0, sizeof(struct req_progress));
num_sgs = count_sgs(req->src, req->nbytes);
sg_miter_start(&cpg->p.src_sg_it, req->src, num_sgs, SG_MITER_FROM_SG);
num_sgs = count_sgs(req->dst, req->nbytes);
sg_miter_start(&cpg->p.dst_sg_it, req->dst, num_sgs, SG_MITER_TO_SG);
mv_process_current_q(1);
}
static int queue_manag(void *data)
{
cpg->eng_st = ENGINE_IDLE;
do {
struct ablkcipher_request *req;
struct crypto_async_request *async_req = NULL;
struct crypto_async_request *backlog;
__set_current_state(TASK_INTERRUPTIBLE);
if (cpg->eng_st == ENGINE_W_DEQUEUE)
dequeue_complete_req();
spin_lock_irq(&cpg->lock);
if (cpg->eng_st == ENGINE_IDLE) {
backlog = crypto_get_backlog(&cpg->queue);
async_req = crypto_dequeue_request(&cpg->queue);
if (async_req) {
BUG_ON(cpg->eng_st != ENGINE_IDLE);
cpg->eng_st = ENGINE_BUSY;
}
}
spin_unlock_irq(&cpg->lock);
if (backlog) {
backlog->complete(backlog, -EINPROGRESS);
backlog = NULL;
}
if (async_req) {
req = container_of(async_req,
struct ablkcipher_request, base);
mv_enqueue_new_req(req);
async_req = NULL;
}
schedule();
} while (!kthread_should_stop());
return 0;
}
static int mv_handle_req(struct ablkcipher_request *req)
{
unsigned long flags;
int ret;
spin_lock_irqsave(&cpg->lock, flags);
ret = ablkcipher_enqueue_request(&cpg->queue, req);
spin_unlock_irqrestore(&cpg->lock, flags);
wake_up_process(cpg->queue_th);
return ret;
}
static int mv_enc_aes_ecb(struct ablkcipher_request *req)
{
struct mv_req_ctx *req_ctx = ablkcipher_request_ctx(req);
req_ctx->op = COP_AES_ECB;
req_ctx->decrypt = 0;
return mv_handle_req(req);
}
static int mv_dec_aes_ecb(struct ablkcipher_request *req)
{
struct mv_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
struct mv_req_ctx *req_ctx = ablkcipher_request_ctx(req);
req_ctx->op = COP_AES_ECB;
req_ctx->decrypt = 1;
compute_aes_dec_key(ctx);
return mv_handle_req(req);
}
static int mv_enc_aes_cbc(struct ablkcipher_request *req)
{
struct mv_req_ctx *req_ctx = ablkcipher_request_ctx(req);
req_ctx->op = COP_AES_CBC;
req_ctx->decrypt = 0;
return mv_handle_req(req);
}
static int mv_dec_aes_cbc(struct ablkcipher_request *req)
{
struct mv_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
struct mv_req_ctx *req_ctx = ablkcipher_request_ctx(req);
req_ctx->op = COP_AES_CBC;
req_ctx->decrypt = 1;
compute_aes_dec_key(ctx);
return mv_handle_req(req);
}
static int mv_cra_init(struct crypto_tfm *tfm)
{
tfm->crt_ablkcipher.reqsize = sizeof(struct mv_req_ctx);
return 0;
}
irqreturn_t crypto_int(int irq, void *priv)
{
u32 val;
val = readl(cpg->reg + SEC_ACCEL_INT_STATUS);
if (!(val & SEC_INT_ACCEL0_DONE))
return IRQ_NONE;
val &= ~SEC_INT_ACCEL0_DONE;
writel(val, cpg->reg + FPGA_INT_STATUS);
writel(val, cpg->reg + SEC_ACCEL_INT_STATUS);
BUG_ON(cpg->eng_st != ENGINE_BUSY);
cpg->eng_st = ENGINE_W_DEQUEUE;
wake_up_process(cpg->queue_th);
return IRQ_HANDLED;
}
struct crypto_alg mv_aes_alg_ecb = {
.cra_name = "ecb(aes)",
.cra_driver_name = "mv-ecb-aes",
.cra_priority = 300,
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
.cra_blocksize = 16,
.cra_ctxsize = sizeof(struct mv_ctx),
.cra_alignmask = 0,
.cra_type = &crypto_ablkcipher_type,
.cra_module = THIS_MODULE,
.cra_init = mv_cra_init,
.cra_u = {
.ablkcipher = {
.min_keysize = AES_MIN_KEY_SIZE,
.max_keysize = AES_MAX_KEY_SIZE,
.setkey = mv_setkey_aes,
.encrypt = mv_enc_aes_ecb,
.decrypt = mv_dec_aes_ecb,
},
},
};
struct crypto_alg mv_aes_alg_cbc = {
.cra_name = "cbc(aes)",
.cra_driver_name = "mv-cbc-aes",
.cra_priority = 300,
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
.cra_blocksize = AES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct mv_ctx),
.cra_alignmask = 0,
.cra_type = &crypto_ablkcipher_type,
.cra_module = THIS_MODULE,
.cra_init = mv_cra_init,
.cra_u = {
.ablkcipher = {
.ivsize = AES_BLOCK_SIZE,
.min_keysize = AES_MIN_KEY_SIZE,
.max_keysize = AES_MAX_KEY_SIZE,
.setkey = mv_setkey_aes,
.encrypt = mv_enc_aes_cbc,
.decrypt = mv_dec_aes_cbc,
},
},
};
static int mv_probe(struct platform_device *pdev)
{
struct crypto_priv *cp;
struct resource *res;
int irq;
int ret;
if (cpg) {
printk(KERN_ERR "Second crypto dev?\n");
return -EEXIST;
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
if (!res)
return -ENXIO;
cp = kzalloc(sizeof(*cp), GFP_KERNEL);
if (!cp)
return -ENOMEM;
spin_lock_init(&cp->lock);
crypto_init_queue(&cp->queue, 50);
cp->reg = ioremap(res->start, res->end - res->start + 1);
if (!cp->reg) {
ret = -ENOMEM;
goto err;
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sram");
if (!res) {
ret = -ENXIO;
goto err_unmap_reg;
}
cp->sram_size = res->end - res->start + 1;
cp->max_req_size = cp->sram_size - SRAM_CFG_SPACE;
cp->sram = ioremap(res->start, cp->sram_size);
if (!cp->sram) {
ret = -ENOMEM;
goto err_unmap_reg;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0 || irq == NO_IRQ) {
ret = irq;
goto err_unmap_sram;
}
cp->irq = irq;
platform_set_drvdata(pdev, cp);
cpg = cp;
cp->queue_th = kthread_run(queue_manag, cp, "mv_crypto");
if (IS_ERR(cp->queue_th)) {
ret = PTR_ERR(cp->queue_th);
goto err_thread;
}
ret = request_irq(irq, crypto_int, IRQF_DISABLED, dev_name(&pdev->dev),
cp);
if (ret)
goto err_unmap_sram;
writel(SEC_INT_ACCEL0_DONE, cpg->reg + SEC_ACCEL_INT_MASK);
writel(SEC_CFG_STOP_DIG_ERR, cpg->reg + SEC_ACCEL_CFG);
ret = crypto_register_alg(&mv_aes_alg_ecb);
if (ret)
goto err_reg;
ret = crypto_register_alg(&mv_aes_alg_cbc);
if (ret)
goto err_unreg_ecb;
return 0;
err_unreg_ecb:
crypto_unregister_alg(&mv_aes_alg_ecb);
err_thread:
free_irq(irq, cp);
err_reg:
kthread_stop(cp->queue_th);
err_unmap_sram:
iounmap(cp->sram);
err_unmap_reg:
iounmap(cp->reg);
err:
kfree(cp);
cpg = NULL;
platform_set_drvdata(pdev, NULL);
return ret;
}
static int mv_remove(struct platform_device *pdev)
{
struct crypto_priv *cp = platform_get_drvdata(pdev);
crypto_unregister_alg(&mv_aes_alg_ecb);
crypto_unregister_alg(&mv_aes_alg_cbc);
kthread_stop(cp->queue_th);
free_irq(cp->irq, cp);
memset(cp->sram, 0, cp->sram_size);
iounmap(cp->sram);
iounmap(cp->reg);
kfree(cp);
cpg = NULL;
return 0;
}
static struct platform_driver marvell_crypto = {
.probe = mv_probe,
.remove = mv_remove,
.driver = {
.owner = THIS_MODULE,
.name = "mv_crypto",
},
};
MODULE_ALIAS("platform:mv_crypto");
static int __init mv_crypto_init(void)
{
return platform_driver_register(&marvell_crypto);
}
module_init(mv_crypto_init);
static void __exit mv_crypto_exit(void)
{
platform_driver_unregister(&marvell_crypto);
}
module_exit(mv_crypto_exit);
MODULE_AUTHOR("Sebastian Andrzej Siewior <sebastian@breakpoint.cc>");
MODULE_DESCRIPTION("Support for Marvell's cryptographic engine");
MODULE_LICENSE("GPL");