forked from Minki/linux
4c3f97276e
Introduce basic low level Arm TrustZone CryptoCell HW support. This first patch doesn't actually register any Crypto API transformations, these will follow up in the next patch. This first revision supports the CC 712 REE component. Signed-off-by: Gilad Ben-Yossef <gilad@benyossef.com> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
280 lines
8.0 KiB
C
280 lines
8.0 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/* Copyright (C) 2012-2018 ARM Limited or its affiliates. */
|
|
|
|
#include <crypto/ctr.h>
|
|
#include "cc_driver.h"
|
|
#include "cc_ivgen.h"
|
|
#include "cc_request_mgr.h"
|
|
#include "cc_sram_mgr.h"
|
|
#include "cc_buffer_mgr.h"
|
|
|
|
/* The max. size of pool *MUST* be <= SRAM total size */
|
|
#define CC_IVPOOL_SIZE 1024
|
|
/* The first 32B fraction of pool are dedicated to the
|
|
* next encryption "key" & "IV" for pool regeneration
|
|
*/
|
|
#define CC_IVPOOL_META_SIZE (CC_AES_IV_SIZE + AES_KEYSIZE_128)
|
|
#define CC_IVPOOL_GEN_SEQ_LEN 4
|
|
|
|
/**
|
|
* struct cc_ivgen_ctx -IV pool generation context
|
|
* @pool: the start address of the iv-pool resides in internal RAM
|
|
* @ctr_key_dma: address of pool's encryption key material in internal RAM
|
|
* @ctr_iv_dma: address of pool's counter iv in internal RAM
|
|
* @next_iv_ofs: the offset to the next available IV in pool
|
|
* @pool_meta: virt. address of the initial enc. key/IV
|
|
* @pool_meta_dma: phys. address of the initial enc. key/IV
|
|
*/
|
|
struct cc_ivgen_ctx {
|
|
cc_sram_addr_t pool;
|
|
cc_sram_addr_t ctr_key;
|
|
cc_sram_addr_t ctr_iv;
|
|
u32 next_iv_ofs;
|
|
u8 *pool_meta;
|
|
dma_addr_t pool_meta_dma;
|
|
};
|
|
|
|
/*!
|
|
* Generates CC_IVPOOL_SIZE of random bytes by
|
|
* encrypting 0's using AES128-CTR.
|
|
*
|
|
* \param ivgen iv-pool context
|
|
* \param iv_seq IN/OUT array to the descriptors sequence
|
|
* \param iv_seq_len IN/OUT pointer to the sequence length
|
|
*/
|
|
static int cc_gen_iv_pool(struct cc_ivgen_ctx *ivgen_ctx,
|
|
struct cc_hw_desc iv_seq[], unsigned int *iv_seq_len)
|
|
{
|
|
unsigned int idx = *iv_seq_len;
|
|
|
|
if ((*iv_seq_len + CC_IVPOOL_GEN_SEQ_LEN) > CC_IVPOOL_SEQ_LEN) {
|
|
/* The sequence will be longer than allowed */
|
|
return -EINVAL;
|
|
}
|
|
/* Setup key */
|
|
hw_desc_init(&iv_seq[idx]);
|
|
set_din_sram(&iv_seq[idx], ivgen_ctx->ctr_key, AES_KEYSIZE_128);
|
|
set_setup_mode(&iv_seq[idx], SETUP_LOAD_KEY0);
|
|
set_cipher_config0(&iv_seq[idx], DESC_DIRECTION_ENCRYPT_ENCRYPT);
|
|
set_flow_mode(&iv_seq[idx], S_DIN_to_AES);
|
|
set_key_size_aes(&iv_seq[idx], CC_AES_128_BIT_KEY_SIZE);
|
|
set_cipher_mode(&iv_seq[idx], DRV_CIPHER_CTR);
|
|
idx++;
|
|
|
|
/* Setup cipher state */
|
|
hw_desc_init(&iv_seq[idx]);
|
|
set_din_sram(&iv_seq[idx], ivgen_ctx->ctr_iv, CC_AES_IV_SIZE);
|
|
set_cipher_config0(&iv_seq[idx], DESC_DIRECTION_ENCRYPT_ENCRYPT);
|
|
set_flow_mode(&iv_seq[idx], S_DIN_to_AES);
|
|
set_setup_mode(&iv_seq[idx], SETUP_LOAD_STATE1);
|
|
set_key_size_aes(&iv_seq[idx], CC_AES_128_BIT_KEY_SIZE);
|
|
set_cipher_mode(&iv_seq[idx], DRV_CIPHER_CTR);
|
|
idx++;
|
|
|
|
/* Perform dummy encrypt to skip first block */
|
|
hw_desc_init(&iv_seq[idx]);
|
|
set_din_const(&iv_seq[idx], 0, CC_AES_IV_SIZE);
|
|
set_dout_sram(&iv_seq[idx], ivgen_ctx->pool, CC_AES_IV_SIZE);
|
|
set_flow_mode(&iv_seq[idx], DIN_AES_DOUT);
|
|
idx++;
|
|
|
|
/* Generate IV pool */
|
|
hw_desc_init(&iv_seq[idx]);
|
|
set_din_const(&iv_seq[idx], 0, CC_IVPOOL_SIZE);
|
|
set_dout_sram(&iv_seq[idx], ivgen_ctx->pool, CC_IVPOOL_SIZE);
|
|
set_flow_mode(&iv_seq[idx], DIN_AES_DOUT);
|
|
idx++;
|
|
|
|
*iv_seq_len = idx; /* Update sequence length */
|
|
|
|
/* queue ordering assures pool readiness */
|
|
ivgen_ctx->next_iv_ofs = CC_IVPOOL_META_SIZE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
* Generates the initial pool in SRAM.
|
|
* This function should be invoked when resuming driver.
|
|
*
|
|
* \param drvdata
|
|
*
|
|
* \return int Zero for success, negative value otherwise.
|
|
*/
|
|
int cc_init_iv_sram(struct cc_drvdata *drvdata)
|
|
{
|
|
struct cc_ivgen_ctx *ivgen_ctx = drvdata->ivgen_handle;
|
|
struct cc_hw_desc iv_seq[CC_IVPOOL_SEQ_LEN];
|
|
unsigned int iv_seq_len = 0;
|
|
int rc;
|
|
|
|
/* Generate initial enc. key/iv */
|
|
get_random_bytes(ivgen_ctx->pool_meta, CC_IVPOOL_META_SIZE);
|
|
|
|
/* The first 32B reserved for the enc. Key/IV */
|
|
ivgen_ctx->ctr_key = ivgen_ctx->pool;
|
|
ivgen_ctx->ctr_iv = ivgen_ctx->pool + AES_KEYSIZE_128;
|
|
|
|
/* Copy initial enc. key and IV to SRAM at a single descriptor */
|
|
hw_desc_init(&iv_seq[iv_seq_len]);
|
|
set_din_type(&iv_seq[iv_seq_len], DMA_DLLI, ivgen_ctx->pool_meta_dma,
|
|
CC_IVPOOL_META_SIZE, NS_BIT);
|
|
set_dout_sram(&iv_seq[iv_seq_len], ivgen_ctx->pool,
|
|
CC_IVPOOL_META_SIZE);
|
|
set_flow_mode(&iv_seq[iv_seq_len], BYPASS);
|
|
iv_seq_len++;
|
|
|
|
/* Generate initial pool */
|
|
rc = cc_gen_iv_pool(ivgen_ctx, iv_seq, &iv_seq_len);
|
|
if (rc)
|
|
return rc;
|
|
|
|
/* Fire-and-forget */
|
|
return send_request_init(drvdata, iv_seq, iv_seq_len);
|
|
}
|
|
|
|
/*!
|
|
* Free iv-pool and ivgen context.
|
|
*
|
|
* \param drvdata
|
|
*/
|
|
void cc_ivgen_fini(struct cc_drvdata *drvdata)
|
|
{
|
|
struct cc_ivgen_ctx *ivgen_ctx = drvdata->ivgen_handle;
|
|
struct device *device = &drvdata->plat_dev->dev;
|
|
|
|
if (!ivgen_ctx)
|
|
return;
|
|
|
|
if (ivgen_ctx->pool_meta) {
|
|
memset(ivgen_ctx->pool_meta, 0, CC_IVPOOL_META_SIZE);
|
|
dma_free_coherent(device, CC_IVPOOL_META_SIZE,
|
|
ivgen_ctx->pool_meta,
|
|
ivgen_ctx->pool_meta_dma);
|
|
}
|
|
|
|
ivgen_ctx->pool = NULL_SRAM_ADDR;
|
|
|
|
/* release "this" context */
|
|
kfree(ivgen_ctx);
|
|
}
|
|
|
|
/*!
|
|
* Allocates iv-pool and maps resources.
|
|
* This function generates the first IV pool.
|
|
*
|
|
* \param drvdata Driver's private context
|
|
*
|
|
* \return int Zero for success, negative value otherwise.
|
|
*/
|
|
int cc_ivgen_init(struct cc_drvdata *drvdata)
|
|
{
|
|
struct cc_ivgen_ctx *ivgen_ctx;
|
|
struct device *device = &drvdata->plat_dev->dev;
|
|
int rc;
|
|
|
|
/* Allocate "this" context */
|
|
ivgen_ctx = kzalloc(sizeof(*ivgen_ctx), GFP_KERNEL);
|
|
if (!ivgen_ctx)
|
|
return -ENOMEM;
|
|
|
|
/* Allocate pool's header for initial enc. key/IV */
|
|
ivgen_ctx->pool_meta = dma_alloc_coherent(device, CC_IVPOOL_META_SIZE,
|
|
&ivgen_ctx->pool_meta_dma,
|
|
GFP_KERNEL);
|
|
if (!ivgen_ctx->pool_meta) {
|
|
dev_err(device, "Not enough memory to allocate DMA of pool_meta (%u B)\n",
|
|
CC_IVPOOL_META_SIZE);
|
|
rc = -ENOMEM;
|
|
goto out;
|
|
}
|
|
/* Allocate IV pool in SRAM */
|
|
ivgen_ctx->pool = cc_sram_alloc(drvdata, CC_IVPOOL_SIZE);
|
|
if (ivgen_ctx->pool == NULL_SRAM_ADDR) {
|
|
dev_err(device, "SRAM pool exhausted\n");
|
|
rc = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
drvdata->ivgen_handle = ivgen_ctx;
|
|
|
|
return cc_init_iv_sram(drvdata);
|
|
|
|
out:
|
|
cc_ivgen_fini(drvdata);
|
|
return rc;
|
|
}
|
|
|
|
/*!
|
|
* Acquires 16 Bytes IV from the iv-pool
|
|
*
|
|
* \param drvdata Driver private context
|
|
* \param iv_out_dma Array of physical IV out addresses
|
|
* \param iv_out_dma_len Length of iv_out_dma array (additional elements
|
|
* of iv_out_dma array are ignore)
|
|
* \param iv_out_size May be 8 or 16 bytes long
|
|
* \param iv_seq IN/OUT array to the descriptors sequence
|
|
* \param iv_seq_len IN/OUT pointer to the sequence length
|
|
*
|
|
* \return int Zero for success, negative value otherwise.
|
|
*/
|
|
int cc_get_iv(struct cc_drvdata *drvdata, dma_addr_t iv_out_dma[],
|
|
unsigned int iv_out_dma_len, unsigned int iv_out_size,
|
|
struct cc_hw_desc iv_seq[], unsigned int *iv_seq_len)
|
|
{
|
|
struct cc_ivgen_ctx *ivgen_ctx = drvdata->ivgen_handle;
|
|
unsigned int idx = *iv_seq_len;
|
|
struct device *dev = drvdata_to_dev(drvdata);
|
|
unsigned int t;
|
|
|
|
if (iv_out_size != CC_AES_IV_SIZE &&
|
|
iv_out_size != CTR_RFC3686_IV_SIZE) {
|
|
return -EINVAL;
|
|
}
|
|
if ((iv_out_dma_len + 1) > CC_IVPOOL_SEQ_LEN) {
|
|
/* The sequence will be longer than allowed */
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* check that number of generated IV is limited to max dma address
|
|
* iv buffer size
|
|
*/
|
|
if (iv_out_dma_len > CC_MAX_IVGEN_DMA_ADDRESSES) {
|
|
/* The sequence will be longer than allowed */
|
|
return -EINVAL;
|
|
}
|
|
|
|
for (t = 0; t < iv_out_dma_len; t++) {
|
|
/* Acquire IV from pool */
|
|
hw_desc_init(&iv_seq[idx]);
|
|
set_din_sram(&iv_seq[idx], (ivgen_ctx->pool +
|
|
ivgen_ctx->next_iv_ofs),
|
|
iv_out_size);
|
|
set_dout_dlli(&iv_seq[idx], iv_out_dma[t], iv_out_size,
|
|
NS_BIT, 0);
|
|
set_flow_mode(&iv_seq[idx], BYPASS);
|
|
idx++;
|
|
}
|
|
|
|
/* Bypass operation is proceeded by crypto sequence, hence must
|
|
* assure bypass-write-transaction by a memory barrier
|
|
*/
|
|
hw_desc_init(&iv_seq[idx]);
|
|
set_din_no_dma(&iv_seq[idx], 0, 0xfffff0);
|
|
set_dout_no_dma(&iv_seq[idx], 0, 0, 1);
|
|
idx++;
|
|
|
|
*iv_seq_len = idx; /* update seq length */
|
|
|
|
/* Update iv index */
|
|
ivgen_ctx->next_iv_ofs += iv_out_size;
|
|
|
|
if ((CC_IVPOOL_SIZE - ivgen_ctx->next_iv_ofs) < CC_AES_IV_SIZE) {
|
|
dev_dbg(dev, "Pool exhausted, regenerating iv-pool\n");
|
|
/* pool is drained -regenerate it! */
|
|
return cc_gen_iv_pool(ivgen_ctx, iv_seq, iv_seq_len);
|
|
}
|
|
|
|
return 0;
|
|
}
|