crypto: ccp - Enable 3DES function on v5 CCPs

Wire up support for Triple DES in ECB mode.

Signed-off-by: Gary R Hook <gary.hook@amd.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
This commit is contained in:
Gary R Hook
2017-03-15 13:20:52 -05:00
committed by Herbert Xu
parent ccebcf3f22
commit 990672d485
9 changed files with 608 additions and 3 deletions

View File

@@ -16,6 +16,7 @@
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <crypto/scatterwalk.h>
#include <crypto/des.h>
#include <linux/ccp.h>
#include "ccp-dev.h"
@@ -939,6 +940,200 @@ e_key:
return ret;
}
static int ccp_run_des3_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd)
{
struct ccp_des3_engine *des3 = &cmd->u.des3;
struct ccp_dm_workarea key, ctx;
struct ccp_data src, dst;
struct ccp_op op;
unsigned int dm_offset;
unsigned int len_singlekey;
bool in_place = false;
int ret;
/* Error checks */
if (!cmd_q->ccp->vdata->perform->des3)
return -EINVAL;
if (des3->key_len != DES3_EDE_KEY_SIZE)
return -EINVAL;
if (((des3->mode == CCP_DES3_MODE_ECB) ||
(des3->mode == CCP_DES3_MODE_CBC)) &&
(des3->src_len & (DES3_EDE_BLOCK_SIZE - 1)))
return -EINVAL;
if (!des3->key || !des3->src || !des3->dst)
return -EINVAL;
if (des3->mode != CCP_DES3_MODE_ECB) {
if (des3->iv_len != DES3_EDE_BLOCK_SIZE)
return -EINVAL;
if (!des3->iv)
return -EINVAL;
}
ret = -EIO;
/* Zero out all the fields of the command desc */
memset(&op, 0, sizeof(op));
/* Set up the Function field */
op.cmd_q = cmd_q;
op.jobid = CCP_NEW_JOBID(cmd_q->ccp);
op.sb_key = cmd_q->sb_key;
op.init = (des3->mode == CCP_DES3_MODE_ECB) ? 0 : 1;
op.u.des3.type = des3->type;
op.u.des3.mode = des3->mode;
op.u.des3.action = des3->action;
/*
* All supported key sizes fit in a single (32-byte) KSB entry and
* (like AES) must be in little endian format. Use the 256-bit byte
* swap passthru option to convert from big endian to little endian.
*/
ret = ccp_init_dm_workarea(&key, cmd_q,
CCP_DES3_KEY_SB_COUNT * CCP_SB_BYTES,
DMA_TO_DEVICE);
if (ret)
return ret;
/*
* The contents of the key triplet are in the reverse order of what
* is required by the engine. Copy the 3 pieces individually to put
* them where they belong.
*/
dm_offset = CCP_SB_BYTES - des3->key_len; /* Basic offset */
len_singlekey = des3->key_len / 3;
ccp_set_dm_area(&key, dm_offset + 2 * len_singlekey,
des3->key, 0, len_singlekey);
ccp_set_dm_area(&key, dm_offset + len_singlekey,
des3->key, len_singlekey, len_singlekey);
ccp_set_dm_area(&key, dm_offset,
des3->key, 2 * len_singlekey, len_singlekey);
/* Copy the key to the SB */
ret = ccp_copy_to_sb(cmd_q, &key, op.jobid, op.sb_key,
CCP_PASSTHRU_BYTESWAP_256BIT);
if (ret) {
cmd->engine_error = cmd_q->cmd_error;
goto e_key;
}
/*
* The DES3 context fits in a single (32-byte) KSB entry and
* must be in little endian format. Use the 256-bit byte swap
* passthru option to convert from big endian to little endian.
*/
if (des3->mode != CCP_DES3_MODE_ECB) {
u32 load_mode;
op.sb_ctx = cmd_q->sb_ctx;
ret = ccp_init_dm_workarea(&ctx, cmd_q,
CCP_DES3_CTX_SB_COUNT * CCP_SB_BYTES,
DMA_BIDIRECTIONAL);
if (ret)
goto e_key;
/* Load the context into the LSB */
dm_offset = CCP_SB_BYTES - des3->iv_len;
ccp_set_dm_area(&ctx, dm_offset, des3->iv, 0, des3->iv_len);
if (cmd_q->ccp->vdata->version == CCP_VERSION(3, 0))
load_mode = CCP_PASSTHRU_BYTESWAP_NOOP;
else
load_mode = CCP_PASSTHRU_BYTESWAP_256BIT;
ret = ccp_copy_to_sb(cmd_q, &ctx, op.jobid, op.sb_ctx,
load_mode);
if (ret) {
cmd->engine_error = cmd_q->cmd_error;
goto e_ctx;
}
}
/*
* Prepare the input and output data workareas. For in-place
* operations we need to set the dma direction to BIDIRECTIONAL
* and copy the src workarea to the dst workarea.
*/
if (sg_virt(des3->src) == sg_virt(des3->dst))
in_place = true;
ret = ccp_init_data(&src, cmd_q, des3->src, des3->src_len,
DES3_EDE_BLOCK_SIZE,
in_place ? DMA_BIDIRECTIONAL : DMA_TO_DEVICE);
if (ret)
goto e_ctx;
if (in_place)
dst = src;
else {
ret = ccp_init_data(&dst, cmd_q, des3->dst, des3->src_len,
DES3_EDE_BLOCK_SIZE, DMA_FROM_DEVICE);
if (ret)
goto e_src;
}
/* Send data to the CCP DES3 engine */
while (src.sg_wa.bytes_left) {
ccp_prepare_data(&src, &dst, &op, DES3_EDE_BLOCK_SIZE, true);
if (!src.sg_wa.bytes_left) {
op.eom = 1;
/* Since we don't retrieve the context in ECB mode
* we have to wait for the operation to complete
* on the last piece of data
*/
op.soc = 0;
}
ret = cmd_q->ccp->vdata->perform->des3(&op);
if (ret) {
cmd->engine_error = cmd_q->cmd_error;
goto e_dst;
}
ccp_process_data(&src, &dst, &op);
}
if (des3->mode != CCP_DES3_MODE_ECB) {
/* Retrieve the context and make BE */
ret = ccp_copy_from_sb(cmd_q, &ctx, op.jobid, op.sb_ctx,
CCP_PASSTHRU_BYTESWAP_256BIT);
if (ret) {
cmd->engine_error = cmd_q->cmd_error;
goto e_dst;
}
/* ...but we only need the last DES3_EDE_BLOCK_SIZE bytes */
if (cmd_q->ccp->vdata->version == CCP_VERSION(3, 0))
dm_offset = CCP_SB_BYTES - des3->iv_len;
else
dm_offset = 0;
ccp_get_dm_area(&ctx, dm_offset, des3->iv, 0,
DES3_EDE_BLOCK_SIZE);
}
e_dst:
if (!in_place)
ccp_free_data(&dst, cmd_q);
e_src:
ccp_free_data(&src, cmd_q);
e_ctx:
if (des3->mode != CCP_DES3_MODE_ECB)
ccp_dm_free(&ctx);
e_key:
ccp_dm_free(&key);
return ret;
}
static int ccp_run_sha_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd)
{
struct ccp_sha_engine *sha = &cmd->u.sha;
@@ -1903,6 +2098,9 @@ int ccp_run_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd)
case CCP_ENGINE_XTS_AES_128:
ret = ccp_run_xts_aes_cmd(cmd_q, cmd);
break;
case CCP_ENGINE_DES3:
ret = ccp_run_des3_cmd(cmd_q, cmd);
break;
case CCP_ENGINE_SHA:
ret = ccp_run_sha_cmd(cmd_q, cmd);
break;