Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+57
View File
@@ -0,0 +1,57 @@
config CRYPTO_DEV_ASPEED
tristate "Support for Aspeed cryptographic engine driver"
depends on ARCH_ASPEED || COMPILE_TEST
select CRYPTO_ENGINE
help
Hash and Crypto Engine (HACE) is designed to accelerate the
throughput of hash data digest, encryption and decryption.
Select y here to have support for the cryptographic driver
available on Aspeed SoC.
config CRYPTO_DEV_ASPEED_DEBUG
bool "Enable Aspeed crypto debug messages"
depends on CRYPTO_DEV_ASPEED
help
Print Aspeed crypto debugging messages if you use this
option to ask for those messages.
Avoid enabling this option for production build to
minimize driver timing.
config CRYPTO_DEV_ASPEED_HACE_HASH
bool "Enable Aspeed Hash & Crypto Engine (HACE) hash"
depends on CRYPTO_DEV_ASPEED
select CRYPTO_SHA1
select CRYPTO_SHA256
select CRYPTO_SHA512
select CRYPTO_HMAC
help
Select here to enable Aspeed Hash & Crypto Engine (HACE)
hash driver.
Supports multiple message digest standards, including
SHA-1, SHA-224, SHA-256, SHA-384, SHA-512, and so on.
config CRYPTO_DEV_ASPEED_HACE_CRYPTO
bool "Enable Aspeed Hash & Crypto Engine (HACE) crypto"
depends on CRYPTO_DEV_ASPEED
select CRYPTO_AES
select CRYPTO_DES
select CRYPTO_ECB
select CRYPTO_CBC
select CRYPTO_CTR
help
Select here to enable Aspeed Hash & Crypto Engine (HACE)
crypto driver.
Supports AES/DES symmetric-key encryption and decryption
with ECB/CBC/CTR options.
config CRYPTO_DEV_ASPEED_ACRY
bool "Enable Aspeed ACRY RSA Engine"
depends on CRYPTO_DEV_ASPEED
select CRYPTO_ENGINE
select CRYPTO_RSA
help
Select here to enable Aspeed ECC/RSA Engine (ACRY)
RSA driver.
Supports 256 bits to 4096 bits RSA encryption/decryption
and signature/verification.
@@ -0,0 +1,11 @@
hace-hash-$(CONFIG_CRYPTO_DEV_ASPEED_HACE_HASH) := aspeed-hace-hash.o
hace-crypto-$(CONFIG_CRYPTO_DEV_ASPEED_HACE_CRYPTO) := aspeed-hace-crypto.o
obj-$(CONFIG_CRYPTO_DEV_ASPEED) += aspeed_crypto.o
aspeed_crypto-objs := aspeed-hace.o \
$(hace-hash-y) \
$(hace-crypto-y)
aspeed_acry-$(CONFIG_CRYPTO_DEV_ASPEED_ACRY) += aspeed-acry.o
obj-$(CONFIG_CRYPTO_DEV_ASPEED) += $(aspeed_acry-y)
@@ -0,0 +1,818 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2021 Aspeed Technology Inc.
*/
#include <crypto/engine.h>
#include <crypto/internal/akcipher.h>
#include <crypto/internal/rsa.h>
#include <crypto/scatterwalk.h>
#include <linux/clk.h>
#include <linux/count_zeros.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/string.h>
#ifdef CONFIG_CRYPTO_DEV_ASPEED_DEBUG
#define ACRY_DBG(d, fmt, ...) \
dev_info((d)->dev, "%s() " fmt, __func__, ##__VA_ARGS__)
#else
#define ACRY_DBG(d, fmt, ...) \
dev_dbg((d)->dev, "%s() " fmt, __func__, ##__VA_ARGS__)
#endif
/*****************************
* *
* ACRY register definitions *
* *
* ***************************/
#define ASPEED_ACRY_TRIGGER 0x000 /* ACRY Engine Control: trigger */
#define ASPEED_ACRY_DMA_CMD 0x048 /* ACRY Engine Control: Command */
#define ASPEED_ACRY_DMA_SRC_BASE 0x04C /* ACRY DRAM base address for DMA */
#define ASPEED_ACRY_DMA_LEN 0x050 /* ACRY Data Length of DMA */
#define ASPEED_ACRY_RSA_KEY_LEN 0x058 /* ACRY RSA Exp/Mod Key Length (Bits) */
#define ASPEED_ACRY_INT_MASK 0x3F8 /* ACRY Interrupt Mask */
#define ASPEED_ACRY_STATUS 0x3FC /* ACRY Interrupt Status */
/* rsa trigger */
#define ACRY_CMD_RSA_TRIGGER BIT(0)
#define ACRY_CMD_DMA_RSA_TRIGGER BIT(1)
/* rsa dma cmd */
#define ACRY_CMD_DMA_SRAM_MODE_RSA (0x3 << 4)
#define ACRY_CMD_DMEM_AHB BIT(8)
#define ACRY_CMD_DMA_SRAM_AHB_ENGINE 0
/* rsa key len */
#define RSA_E_BITS_LEN(x) ((x) << 16)
#define RSA_M_BITS_LEN(x) (x)
/* acry isr */
#define ACRY_RSA_ISR BIT(1)
#define ASPEED_ACRY_BUFF_SIZE 0x1800 /* DMA buffer size */
#define ASPEED_ACRY_SRAM_MAX_LEN 2048 /* ACRY SRAM maximum length (Bytes) */
#define ASPEED_ACRY_RSA_MAX_KEY_LEN 512 /* ACRY RSA maximum key length (Bytes) */
#define CRYPTO_FLAGS_BUSY BIT(1)
#define BYTES_PER_DWORD 4
/*****************************
* *
* AHBC register definitions *
* *
* ***************************/
#define AHBC_REGION_PROT 0x240
#define REGION_ACRYM BIT(23)
#define ast_acry_write(acry, val, offset) \
writel((val), (acry)->regs + (offset))
#define ast_acry_read(acry, offset) \
readl((acry)->regs + (offset))
struct aspeed_acry_dev;
typedef int (*aspeed_acry_fn_t)(struct aspeed_acry_dev *);
struct aspeed_acry_dev {
void __iomem *regs;
struct device *dev;
int irq;
struct clk *clk;
struct regmap *ahbc;
struct akcipher_request *req;
struct tasklet_struct done_task;
aspeed_acry_fn_t resume;
unsigned long flags;
/* ACRY output SRAM buffer */
void __iomem *acry_sram;
/* ACRY input DMA buffer */
void *buf_addr;
dma_addr_t buf_dma_addr;
struct crypto_engine *crypt_engine_rsa;
/* ACRY SRAM memory mapped */
int exp_dw_mapping[ASPEED_ACRY_RSA_MAX_KEY_LEN];
int mod_dw_mapping[ASPEED_ACRY_RSA_MAX_KEY_LEN];
int data_byte_mapping[ASPEED_ACRY_SRAM_MAX_LEN];
};
struct aspeed_acry_ctx {
struct aspeed_acry_dev *acry_dev;
struct rsa_key key;
int enc;
u8 *n;
u8 *e;
u8 *d;
size_t n_sz;
size_t e_sz;
size_t d_sz;
aspeed_acry_fn_t trigger;
struct crypto_akcipher *fallback_tfm;
};
struct aspeed_acry_alg {
struct aspeed_acry_dev *acry_dev;
struct akcipher_engine_alg akcipher;
};
enum aspeed_rsa_key_mode {
ASPEED_RSA_EXP_MODE = 0,
ASPEED_RSA_MOD_MODE,
ASPEED_RSA_DATA_MODE,
};
static inline struct akcipher_request *
akcipher_request_cast(struct crypto_async_request *req)
{
return container_of(req, struct akcipher_request, base);
}
static int aspeed_acry_do_fallback(struct akcipher_request *req)
{
struct crypto_akcipher *cipher = crypto_akcipher_reqtfm(req);
struct aspeed_acry_ctx *ctx = akcipher_tfm_ctx(cipher);
int err;
akcipher_request_set_tfm(req, ctx->fallback_tfm);
if (ctx->enc)
err = crypto_akcipher_encrypt(req);
else
err = crypto_akcipher_decrypt(req);
akcipher_request_set_tfm(req, cipher);
return err;
}
static bool aspeed_acry_need_fallback(struct akcipher_request *req)
{
struct crypto_akcipher *cipher = crypto_akcipher_reqtfm(req);
struct aspeed_acry_ctx *ctx = akcipher_tfm_ctx(cipher);
return ctx->key.n_sz > ASPEED_ACRY_RSA_MAX_KEY_LEN;
}
static int aspeed_acry_handle_queue(struct aspeed_acry_dev *acry_dev,
struct akcipher_request *req)
{
if (aspeed_acry_need_fallback(req)) {
ACRY_DBG(acry_dev, "SW fallback\n");
return aspeed_acry_do_fallback(req);
}
return crypto_transfer_akcipher_request_to_engine(acry_dev->crypt_engine_rsa, req);
}
static int aspeed_acry_do_request(struct crypto_engine *engine, void *areq)
{
struct akcipher_request *req = akcipher_request_cast(areq);
struct crypto_akcipher *cipher = crypto_akcipher_reqtfm(req);
struct aspeed_acry_ctx *ctx = akcipher_tfm_ctx(cipher);
struct aspeed_acry_dev *acry_dev = ctx->acry_dev;
acry_dev->req = req;
acry_dev->flags |= CRYPTO_FLAGS_BUSY;
return ctx->trigger(acry_dev);
}
static int aspeed_acry_complete(struct aspeed_acry_dev *acry_dev, int err)
{
struct akcipher_request *req = acry_dev->req;
acry_dev->flags &= ~CRYPTO_FLAGS_BUSY;
crypto_finalize_akcipher_request(acry_dev->crypt_engine_rsa, req, err);
return err;
}
/*
* Copy Data to DMA buffer for engine used.
*/
static void aspeed_acry_rsa_sg_copy_to_buffer(struct aspeed_acry_dev *acry_dev,
u8 *buf, struct scatterlist *src,
size_t nbytes)
{
static u8 dram_buffer[ASPEED_ACRY_SRAM_MAX_LEN];
int i = 0, j;
int data_idx;
ACRY_DBG(acry_dev, "\n");
scatterwalk_map_and_copy(dram_buffer, src, 0, nbytes, 0);
for (j = nbytes - 1; j >= 0; j--) {
data_idx = acry_dev->data_byte_mapping[i];
buf[data_idx] = dram_buffer[j];
i++;
}
for (; i < ASPEED_ACRY_SRAM_MAX_LEN; i++) {
data_idx = acry_dev->data_byte_mapping[i];
buf[data_idx] = 0;
}
}
/*
* Copy Exp/Mod to DMA buffer for engine used.
*
* Params:
* - mode 0 : Exponential
* - mode 1 : Modulus
*
* Example:
* - DRAM memory layout:
* D[0], D[4], D[8], D[12]
* - ACRY SRAM memory layout should reverse the order of source data:
* D[12], D[8], D[4], D[0]
*/
static int aspeed_acry_rsa_ctx_copy(struct aspeed_acry_dev *acry_dev, void *buf,
const void *xbuf, size_t nbytes,
enum aspeed_rsa_key_mode mode)
{
const u8 *src = xbuf;
__le32 *dw_buf = buf;
int nbits, ndw;
int i, j, idx;
u32 data = 0;
ACRY_DBG(acry_dev, "nbytes:%zu, mode:%d\n", nbytes, mode);
if (nbytes > ASPEED_ACRY_RSA_MAX_KEY_LEN)
return -ENOMEM;
/* Remove the leading zeros */
while (nbytes > 0 && src[0] == 0) {
src++;
nbytes--;
}
nbits = nbytes * 8;
if (nbytes > 0)
nbits -= count_leading_zeros(src[0]) - (BITS_PER_LONG - 8);
/* double-world alignment */
ndw = DIV_ROUND_UP(nbytes, BYTES_PER_DWORD);
if (nbytes > 0) {
i = BYTES_PER_DWORD - nbytes % BYTES_PER_DWORD;
i %= BYTES_PER_DWORD;
for (j = ndw; j > 0; j--) {
for (; i < BYTES_PER_DWORD; i++) {
data <<= 8;
data |= *src++;
}
i = 0;
if (mode == ASPEED_RSA_EXP_MODE)
idx = acry_dev->exp_dw_mapping[j - 1];
else /* mode == ASPEED_RSA_MOD_MODE */
idx = acry_dev->mod_dw_mapping[j - 1];
dw_buf[idx] = cpu_to_le32(data);
}
}
return nbits;
}
static int aspeed_acry_rsa_transfer(struct aspeed_acry_dev *acry_dev)
{
struct akcipher_request *req = acry_dev->req;
u8 __iomem *sram_buffer = acry_dev->acry_sram;
struct scatterlist *out_sg = req->dst;
static u8 dram_buffer[ASPEED_ACRY_SRAM_MAX_LEN];
int leading_zero = 1;
int result_nbytes;
int i = 0, j;
int data_idx;
/* Set Data Memory to AHB(CPU) Access Mode */
ast_acry_write(acry_dev, ACRY_CMD_DMEM_AHB, ASPEED_ACRY_DMA_CMD);
/* Disable ACRY SRAM protection */
regmap_update_bits(acry_dev->ahbc, AHBC_REGION_PROT,
REGION_ACRYM, 0);
result_nbytes = ASPEED_ACRY_SRAM_MAX_LEN;
for (j = ASPEED_ACRY_SRAM_MAX_LEN - 1; j >= 0; j--) {
data_idx = acry_dev->data_byte_mapping[j];
if (readb(sram_buffer + data_idx) == 0 && leading_zero) {
result_nbytes--;
} else {
leading_zero = 0;
dram_buffer[i] = readb(sram_buffer + data_idx);
i++;
}
}
ACRY_DBG(acry_dev, "result_nbytes:%d, req->dst_len:%d\n",
result_nbytes, req->dst_len);
if (result_nbytes <= req->dst_len) {
scatterwalk_map_and_copy(dram_buffer, out_sg, 0, result_nbytes,
1);
req->dst_len = result_nbytes;
} else {
dev_err(acry_dev->dev, "RSA engine error!\n");
}
memzero_explicit(acry_dev->buf_addr, ASPEED_ACRY_BUFF_SIZE);
return aspeed_acry_complete(acry_dev, 0);
}
static int aspeed_acry_rsa_trigger(struct aspeed_acry_dev *acry_dev)
{
struct akcipher_request *req = acry_dev->req;
struct crypto_akcipher *cipher = crypto_akcipher_reqtfm(req);
struct aspeed_acry_ctx *ctx = akcipher_tfm_ctx(cipher);
int ne, nm;
if (!ctx->n || !ctx->n_sz) {
dev_err(acry_dev->dev, "%s: key n is not set\n", __func__);
return -EINVAL;
}
memzero_explicit(acry_dev->buf_addr, ASPEED_ACRY_BUFF_SIZE);
/* Copy source data to DMA buffer */
aspeed_acry_rsa_sg_copy_to_buffer(acry_dev, acry_dev->buf_addr,
req->src, req->src_len);
nm = aspeed_acry_rsa_ctx_copy(acry_dev, acry_dev->buf_addr, ctx->n,
ctx->n_sz, ASPEED_RSA_MOD_MODE);
if (ctx->enc) {
if (!ctx->e || !ctx->e_sz) {
dev_err(acry_dev->dev, "%s: key e is not set\n",
__func__);
return -EINVAL;
}
/* Copy key e to DMA buffer */
ne = aspeed_acry_rsa_ctx_copy(acry_dev, acry_dev->buf_addr,
ctx->e, ctx->e_sz,
ASPEED_RSA_EXP_MODE);
} else {
if (!ctx->d || !ctx->d_sz) {
dev_err(acry_dev->dev, "%s: key d is not set\n",
__func__);
return -EINVAL;
}
/* Copy key d to DMA buffer */
ne = aspeed_acry_rsa_ctx_copy(acry_dev, acry_dev->buf_addr,
ctx->key.d, ctx->key.d_sz,
ASPEED_RSA_EXP_MODE);
}
ast_acry_write(acry_dev, acry_dev->buf_dma_addr,
ASPEED_ACRY_DMA_SRC_BASE);
ast_acry_write(acry_dev, (ne << 16) + nm,
ASPEED_ACRY_RSA_KEY_LEN);
ast_acry_write(acry_dev, ASPEED_ACRY_BUFF_SIZE,
ASPEED_ACRY_DMA_LEN);
acry_dev->resume = aspeed_acry_rsa_transfer;
/* Enable ACRY SRAM protection */
regmap_update_bits(acry_dev->ahbc, AHBC_REGION_PROT,
REGION_ACRYM, REGION_ACRYM);
ast_acry_write(acry_dev, ACRY_RSA_ISR, ASPEED_ACRY_INT_MASK);
ast_acry_write(acry_dev, ACRY_CMD_DMA_SRAM_MODE_RSA |
ACRY_CMD_DMA_SRAM_AHB_ENGINE, ASPEED_ACRY_DMA_CMD);
/* Trigger RSA engines */
ast_acry_write(acry_dev, ACRY_CMD_RSA_TRIGGER |
ACRY_CMD_DMA_RSA_TRIGGER, ASPEED_ACRY_TRIGGER);
return 0;
}
static int aspeed_acry_rsa_enc(struct akcipher_request *req)
{
struct crypto_akcipher *cipher = crypto_akcipher_reqtfm(req);
struct aspeed_acry_ctx *ctx = akcipher_tfm_ctx(cipher);
struct aspeed_acry_dev *acry_dev = ctx->acry_dev;
ctx->trigger = aspeed_acry_rsa_trigger;
ctx->enc = 1;
return aspeed_acry_handle_queue(acry_dev, req);
}
static int aspeed_acry_rsa_dec(struct akcipher_request *req)
{
struct crypto_akcipher *cipher = crypto_akcipher_reqtfm(req);
struct aspeed_acry_ctx *ctx = akcipher_tfm_ctx(cipher);
struct aspeed_acry_dev *acry_dev = ctx->acry_dev;
ctx->trigger = aspeed_acry_rsa_trigger;
ctx->enc = 0;
return aspeed_acry_handle_queue(acry_dev, req);
}
static u8 *aspeed_rsa_key_copy(u8 *src, size_t len)
{
return kmemdup(src, len, GFP_KERNEL);
}
static int aspeed_rsa_set_n(struct aspeed_acry_ctx *ctx, u8 *value,
size_t len)
{
ctx->n_sz = len;
ctx->n = aspeed_rsa_key_copy(value, len);
if (!ctx->n)
return -ENOMEM;
return 0;
}
static int aspeed_rsa_set_e(struct aspeed_acry_ctx *ctx, u8 *value,
size_t len)
{
ctx->e_sz = len;
ctx->e = aspeed_rsa_key_copy(value, len);
if (!ctx->e)
return -ENOMEM;
return 0;
}
static int aspeed_rsa_set_d(struct aspeed_acry_ctx *ctx, u8 *value,
size_t len)
{
ctx->d_sz = len;
ctx->d = aspeed_rsa_key_copy(value, len);
if (!ctx->d)
return -ENOMEM;
return 0;
}
static void aspeed_rsa_key_free(struct aspeed_acry_ctx *ctx)
{
kfree_sensitive(ctx->n);
kfree_sensitive(ctx->e);
kfree_sensitive(ctx->d);
ctx->n_sz = 0;
ctx->e_sz = 0;
ctx->d_sz = 0;
}
static int aspeed_acry_rsa_setkey(struct crypto_akcipher *tfm, const void *key,
unsigned int keylen, int priv)
{
struct aspeed_acry_ctx *ctx = akcipher_tfm_ctx(tfm);
struct aspeed_acry_dev *acry_dev = ctx->acry_dev;
int ret;
if (priv)
ret = rsa_parse_priv_key(&ctx->key, key, keylen);
else
ret = rsa_parse_pub_key(&ctx->key, key, keylen);
if (ret) {
dev_err(acry_dev->dev, "rsa parse key failed, ret:0x%x\n",
ret);
return ret;
}
/* Aspeed engine supports up to 4096 bits,
* Use software fallback instead.
*/
if (ctx->key.n_sz > ASPEED_ACRY_RSA_MAX_KEY_LEN)
return 0;
ret = aspeed_rsa_set_n(ctx, (u8 *)ctx->key.n, ctx->key.n_sz);
if (ret)
goto err;
ret = aspeed_rsa_set_e(ctx, (u8 *)ctx->key.e, ctx->key.e_sz);
if (ret)
goto err;
if (priv) {
ret = aspeed_rsa_set_d(ctx, (u8 *)ctx->key.d, ctx->key.d_sz);
if (ret)
goto err;
}
return 0;
err:
dev_err(acry_dev->dev, "rsa set key failed\n");
aspeed_rsa_key_free(ctx);
return ret;
}
static int aspeed_acry_rsa_set_pub_key(struct crypto_akcipher *tfm,
const void *key,
unsigned int keylen)
{
struct aspeed_acry_ctx *ctx = akcipher_tfm_ctx(tfm);
int ret;
ret = crypto_akcipher_set_pub_key(ctx->fallback_tfm, key, keylen);
if (ret)
return ret;
return aspeed_acry_rsa_setkey(tfm, key, keylen, 0);
}
static int aspeed_acry_rsa_set_priv_key(struct crypto_akcipher *tfm,
const void *key,
unsigned int keylen)
{
struct aspeed_acry_ctx *ctx = akcipher_tfm_ctx(tfm);
int ret;
ret = crypto_akcipher_set_priv_key(ctx->fallback_tfm, key, keylen);
if (ret)
return ret;
return aspeed_acry_rsa_setkey(tfm, key, keylen, 1);
}
static unsigned int aspeed_acry_rsa_max_size(struct crypto_akcipher *tfm)
{
struct aspeed_acry_ctx *ctx = akcipher_tfm_ctx(tfm);
if (ctx->key.n_sz > ASPEED_ACRY_RSA_MAX_KEY_LEN)
return crypto_akcipher_maxsize(ctx->fallback_tfm);
return ctx->n_sz;
}
static int aspeed_acry_rsa_init_tfm(struct crypto_akcipher *tfm)
{
struct aspeed_acry_ctx *ctx = akcipher_tfm_ctx(tfm);
struct akcipher_alg *alg = crypto_akcipher_alg(tfm);
const char *name = crypto_tfm_alg_name(&tfm->base);
struct aspeed_acry_alg *acry_alg;
acry_alg = container_of(alg, struct aspeed_acry_alg, akcipher.base);
ctx->acry_dev = acry_alg->acry_dev;
ctx->fallback_tfm = crypto_alloc_akcipher(name, 0, CRYPTO_ALG_ASYNC |
CRYPTO_ALG_NEED_FALLBACK);
if (IS_ERR(ctx->fallback_tfm)) {
dev_err(ctx->acry_dev->dev, "ERROR: Cannot allocate fallback for %s %ld\n",
name, PTR_ERR(ctx->fallback_tfm));
return PTR_ERR(ctx->fallback_tfm);
}
return 0;
}
static void aspeed_acry_rsa_exit_tfm(struct crypto_akcipher *tfm)
{
struct aspeed_acry_ctx *ctx = akcipher_tfm_ctx(tfm);
crypto_free_akcipher(ctx->fallback_tfm);
}
static struct aspeed_acry_alg aspeed_acry_akcipher_algs[] = {
{
.akcipher.base = {
.encrypt = aspeed_acry_rsa_enc,
.decrypt = aspeed_acry_rsa_dec,
.set_pub_key = aspeed_acry_rsa_set_pub_key,
.set_priv_key = aspeed_acry_rsa_set_priv_key,
.max_size = aspeed_acry_rsa_max_size,
.init = aspeed_acry_rsa_init_tfm,
.exit = aspeed_acry_rsa_exit_tfm,
.base = {
.cra_name = "rsa",
.cra_driver_name = "aspeed-rsa",
.cra_priority = 300,
.cra_flags = CRYPTO_ALG_TYPE_AKCIPHER |
CRYPTO_ALG_ASYNC |
CRYPTO_ALG_KERN_DRIVER_ONLY |
CRYPTO_ALG_NEED_FALLBACK,
.cra_module = THIS_MODULE,
.cra_ctxsize = sizeof(struct aspeed_acry_ctx),
},
},
.akcipher.op = {
.do_one_request = aspeed_acry_do_request,
},
},
};
static void aspeed_acry_register(struct aspeed_acry_dev *acry_dev)
{
int i, rc;
for (i = 0; i < ARRAY_SIZE(aspeed_acry_akcipher_algs); i++) {
aspeed_acry_akcipher_algs[i].acry_dev = acry_dev;
rc = crypto_engine_register_akcipher(&aspeed_acry_akcipher_algs[i].akcipher);
if (rc) {
ACRY_DBG(acry_dev, "Failed to register %s\n",
aspeed_acry_akcipher_algs[i].akcipher.base.base.cra_name);
}
}
}
static void aspeed_acry_unregister(struct aspeed_acry_dev *acry_dev)
{
int i;
for (i = 0; i < ARRAY_SIZE(aspeed_acry_akcipher_algs); i++)
crypto_engine_unregister_akcipher(&aspeed_acry_akcipher_algs[i].akcipher);
}
/* ACRY interrupt service routine. */
static irqreturn_t aspeed_acry_irq(int irq, void *dev)
{
struct aspeed_acry_dev *acry_dev = (struct aspeed_acry_dev *)dev;
u32 sts;
sts = ast_acry_read(acry_dev, ASPEED_ACRY_STATUS);
ast_acry_write(acry_dev, sts, ASPEED_ACRY_STATUS);
ACRY_DBG(acry_dev, "irq sts:0x%x\n", sts);
if (sts & ACRY_RSA_ISR) {
/* Stop RSA engine */
ast_acry_write(acry_dev, 0, ASPEED_ACRY_TRIGGER);
if (acry_dev->flags & CRYPTO_FLAGS_BUSY)
tasklet_schedule(&acry_dev->done_task);
else
dev_err(acry_dev->dev, "RSA no active requests.\n");
}
return IRQ_HANDLED;
}
/*
* ACRY SRAM has its own memory layout.
* Set the DRAM to SRAM indexing for future used.
*/
static void aspeed_acry_sram_mapping(struct aspeed_acry_dev *acry_dev)
{
int i, j = 0;
for (i = 0; i < (ASPEED_ACRY_SRAM_MAX_LEN / BYTES_PER_DWORD); i++) {
acry_dev->exp_dw_mapping[i] = j;
acry_dev->mod_dw_mapping[i] = j + 4;
acry_dev->data_byte_mapping[(i * 4)] = (j + 8) * 4;
acry_dev->data_byte_mapping[(i * 4) + 1] = (j + 8) * 4 + 1;
acry_dev->data_byte_mapping[(i * 4) + 2] = (j + 8) * 4 + 2;
acry_dev->data_byte_mapping[(i * 4) + 3] = (j + 8) * 4 + 3;
j++;
j = j % 4 ? j : j + 8;
}
}
static void aspeed_acry_done_task(unsigned long data)
{
struct aspeed_acry_dev *acry_dev = (struct aspeed_acry_dev *)data;
(void)acry_dev->resume(acry_dev);
}
static const struct of_device_id aspeed_acry_of_matches[] = {
{ .compatible = "aspeed,ast2600-acry", },
{},
};
static int aspeed_acry_probe(struct platform_device *pdev)
{
struct aspeed_acry_dev *acry_dev;
struct device *dev = &pdev->dev;
int rc;
acry_dev = devm_kzalloc(dev, sizeof(struct aspeed_acry_dev),
GFP_KERNEL);
if (!acry_dev)
return -ENOMEM;
acry_dev->dev = dev;
platform_set_drvdata(pdev, acry_dev);
acry_dev->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(acry_dev->regs))
return PTR_ERR(acry_dev->regs);
acry_dev->acry_sram = devm_platform_ioremap_resource(pdev, 1);
if (IS_ERR(acry_dev->acry_sram))
return PTR_ERR(acry_dev->acry_sram);
/* Get irq number and register it */
acry_dev->irq = platform_get_irq(pdev, 0);
if (acry_dev->irq < 0)
return -ENXIO;
rc = devm_request_irq(dev, acry_dev->irq, aspeed_acry_irq, 0,
dev_name(dev), acry_dev);
if (rc) {
dev_err(dev, "Failed to request irq.\n");
return rc;
}
acry_dev->clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(acry_dev->clk)) {
dev_err(dev, "Failed to get acry clk\n");
return PTR_ERR(acry_dev->clk);
}
acry_dev->ahbc = syscon_regmap_lookup_by_phandle(dev->of_node,
"aspeed,ahbc");
if (IS_ERR(acry_dev->ahbc)) {
dev_err(dev, "Failed to get AHBC regmap\n");
return -ENODEV;
}
/* Initialize crypto hardware engine structure for RSA */
acry_dev->crypt_engine_rsa = crypto_engine_alloc_init(dev, true);
if (!acry_dev->crypt_engine_rsa) {
rc = -ENOMEM;
goto clk_exit;
}
rc = crypto_engine_start(acry_dev->crypt_engine_rsa);
if (rc)
goto err_engine_rsa_start;
tasklet_init(&acry_dev->done_task, aspeed_acry_done_task,
(unsigned long)acry_dev);
/* Set Data Memory to AHB(CPU) Access Mode */
ast_acry_write(acry_dev, ACRY_CMD_DMEM_AHB, ASPEED_ACRY_DMA_CMD);
/* Initialize ACRY SRAM index */
aspeed_acry_sram_mapping(acry_dev);
acry_dev->buf_addr = dmam_alloc_coherent(dev, ASPEED_ACRY_BUFF_SIZE,
&acry_dev->buf_dma_addr,
GFP_KERNEL);
if (!acry_dev->buf_addr) {
rc = -ENOMEM;
goto err_engine_rsa_start;
}
aspeed_acry_register(acry_dev);
dev_info(dev, "Aspeed ACRY Accelerator successfully registered\n");
return 0;
err_engine_rsa_start:
crypto_engine_exit(acry_dev->crypt_engine_rsa);
clk_exit:
return rc;
}
static void aspeed_acry_remove(struct platform_device *pdev)
{
struct aspeed_acry_dev *acry_dev = platform_get_drvdata(pdev);
aspeed_acry_unregister(acry_dev);
crypto_engine_exit(acry_dev->crypt_engine_rsa);
tasklet_kill(&acry_dev->done_task);
}
MODULE_DEVICE_TABLE(of, aspeed_acry_of_matches);
static struct platform_driver aspeed_acry_driver = {
.probe = aspeed_acry_probe,
.remove = aspeed_acry_remove,
.driver = {
.name = KBUILD_MODNAME,
.of_match_table = aspeed_acry_of_matches,
},
};
module_platform_driver(aspeed_acry_driver);
MODULE_AUTHOR("Neal Liu <neal_liu@aspeedtech.com>");
MODULE_DESCRIPTION("ASPEED ACRY driver for hardware RSA Engine");
MODULE_LICENSE("GPL");
@@ -0,0 +1,955 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2021 Aspeed Technology Inc.
*/
#include "aspeed-hace.h"
#include <crypto/des.h>
#include <crypto/engine.h>
#include <crypto/internal/des.h>
#include <crypto/internal/skcipher.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/scatterlist.h>
#include <linux/string.h>
#ifdef CONFIG_CRYPTO_DEV_ASPEED_HACE_CRYPTO_DEBUG
#define CIPHER_DBG(h, fmt, ...) \
dev_info((h)->dev, "%s() " fmt, __func__, ##__VA_ARGS__)
#else
#define CIPHER_DBG(h, fmt, ...) \
dev_dbg((h)->dev, "%s() " fmt, __func__, ##__VA_ARGS__)
#endif
static int aspeed_crypto_do_fallback(struct skcipher_request *areq)
{
struct aspeed_cipher_reqctx *rctx = skcipher_request_ctx(areq);
struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(areq);
struct aspeed_cipher_ctx *ctx = crypto_skcipher_ctx(tfm);
int err;
skcipher_request_set_tfm(&rctx->fallback_req, ctx->fallback_tfm);
skcipher_request_set_callback(&rctx->fallback_req, areq->base.flags,
areq->base.complete, areq->base.data);
skcipher_request_set_crypt(&rctx->fallback_req, areq->src, areq->dst,
areq->cryptlen, areq->iv);
if (rctx->enc_cmd & HACE_CMD_ENCRYPT)
err = crypto_skcipher_encrypt(&rctx->fallback_req);
else
err = crypto_skcipher_decrypt(&rctx->fallback_req);
return err;
}
static bool aspeed_crypto_need_fallback(struct skcipher_request *areq)
{
struct aspeed_cipher_reqctx *rctx = skcipher_request_ctx(areq);
if (areq->cryptlen == 0)
return true;
if ((rctx->enc_cmd & HACE_CMD_DES_SELECT) &&
!IS_ALIGNED(areq->cryptlen, DES_BLOCK_SIZE))
return true;
if ((!(rctx->enc_cmd & HACE_CMD_DES_SELECT)) &&
!IS_ALIGNED(areq->cryptlen, AES_BLOCK_SIZE))
return true;
return false;
}
static int aspeed_hace_crypto_handle_queue(struct aspeed_hace_dev *hace_dev,
struct skcipher_request *req)
{
if (hace_dev->version == AST2500_VERSION &&
aspeed_crypto_need_fallback(req)) {
CIPHER_DBG(hace_dev, "SW fallback\n");
return aspeed_crypto_do_fallback(req);
}
return crypto_transfer_skcipher_request_to_engine(
hace_dev->crypt_engine_crypto, req);
}
static int aspeed_crypto_do_request(struct crypto_engine *engine, void *areq)
{
struct skcipher_request *req = skcipher_request_cast(areq);
struct crypto_skcipher *cipher = crypto_skcipher_reqtfm(req);
struct aspeed_cipher_ctx *ctx = crypto_skcipher_ctx(cipher);
struct aspeed_hace_dev *hace_dev = ctx->hace_dev;
struct aspeed_engine_crypto *crypto_engine;
int rc;
crypto_engine = &hace_dev->crypto_engine;
crypto_engine->req = req;
crypto_engine->flags |= CRYPTO_FLAGS_BUSY;
rc = ctx->start(hace_dev);
if (rc != -EINPROGRESS)
return -EIO;
return 0;
}
static int aspeed_sk_complete(struct aspeed_hace_dev *hace_dev, int err)
{
struct aspeed_engine_crypto *crypto_engine = &hace_dev->crypto_engine;
struct aspeed_cipher_reqctx *rctx;
struct skcipher_request *req;
CIPHER_DBG(hace_dev, "\n");
req = crypto_engine->req;
rctx = skcipher_request_ctx(req);
if (rctx->enc_cmd & HACE_CMD_IV_REQUIRE) {
if (rctx->enc_cmd & HACE_CMD_DES_SELECT)
memcpy(req->iv, crypto_engine->cipher_ctx +
DES_KEY_SIZE, DES_KEY_SIZE);
else
memcpy(req->iv, crypto_engine->cipher_ctx,
AES_BLOCK_SIZE);
}
crypto_engine->flags &= ~CRYPTO_FLAGS_BUSY;
crypto_finalize_skcipher_request(hace_dev->crypt_engine_crypto, req,
err);
return err;
}
static int aspeed_sk_transfer_sg(struct aspeed_hace_dev *hace_dev)
{
struct aspeed_engine_crypto *crypto_engine = &hace_dev->crypto_engine;
struct device *dev = hace_dev->dev;
struct aspeed_cipher_reqctx *rctx;
struct skcipher_request *req;
CIPHER_DBG(hace_dev, "\n");
req = crypto_engine->req;
rctx = skcipher_request_ctx(req);
if (req->src == req->dst) {
dma_unmap_sg(dev, req->src, rctx->src_nents, DMA_BIDIRECTIONAL);
} else {
dma_unmap_sg(dev, req->src, rctx->src_nents, DMA_TO_DEVICE);
dma_unmap_sg(dev, req->dst, rctx->dst_nents, DMA_FROM_DEVICE);
}
return aspeed_sk_complete(hace_dev, 0);
}
static int aspeed_sk_transfer(struct aspeed_hace_dev *hace_dev)
{
struct aspeed_engine_crypto *crypto_engine = &hace_dev->crypto_engine;
struct aspeed_cipher_reqctx *rctx;
struct skcipher_request *req;
struct scatterlist *out_sg;
int nbytes = 0;
int rc = 0;
req = crypto_engine->req;
rctx = skcipher_request_ctx(req);
out_sg = req->dst;
/* Copy output buffer to dst scatter-gather lists */
nbytes = sg_copy_from_buffer(out_sg, rctx->dst_nents,
crypto_engine->cipher_addr, req->cryptlen);
if (!nbytes) {
dev_warn(hace_dev->dev, "invalid sg copy, %s:0x%x, %s:0x%x\n",
"nbytes", nbytes, "cryptlen", req->cryptlen);
rc = -EINVAL;
}
CIPHER_DBG(hace_dev, "%s:%d, %s:%d, %s:%d, %s:%p\n",
"nbytes", nbytes, "req->cryptlen", req->cryptlen,
"nb_out_sg", rctx->dst_nents,
"cipher addr", crypto_engine->cipher_addr);
return aspeed_sk_complete(hace_dev, rc);
}
static int aspeed_sk_start(struct aspeed_hace_dev *hace_dev)
{
struct aspeed_engine_crypto *crypto_engine = &hace_dev->crypto_engine;
struct aspeed_cipher_reqctx *rctx;
struct skcipher_request *req;
struct scatterlist *in_sg;
int nbytes;
req = crypto_engine->req;
rctx = skcipher_request_ctx(req);
in_sg = req->src;
nbytes = sg_copy_to_buffer(in_sg, rctx->src_nents,
crypto_engine->cipher_addr, req->cryptlen);
CIPHER_DBG(hace_dev, "%s:%d, %s:%d, %s:%d, %s:%p\n",
"nbytes", nbytes, "req->cryptlen", req->cryptlen,
"nb_in_sg", rctx->src_nents,
"cipher addr", crypto_engine->cipher_addr);
if (!nbytes) {
dev_warn(hace_dev->dev, "invalid sg copy, %s:0x%x, %s:0x%x\n",
"nbytes", nbytes, "cryptlen", req->cryptlen);
return -EINVAL;
}
crypto_engine->resume = aspeed_sk_transfer;
/* Trigger engines */
ast_hace_write(hace_dev, crypto_engine->cipher_dma_addr,
ASPEED_HACE_SRC);
ast_hace_write(hace_dev, crypto_engine->cipher_dma_addr,
ASPEED_HACE_DEST);
ast_hace_write(hace_dev, req->cryptlen, ASPEED_HACE_DATA_LEN);
ast_hace_write(hace_dev, rctx->enc_cmd, ASPEED_HACE_CMD);
return -EINPROGRESS;
}
static int aspeed_sk_start_sg(struct aspeed_hace_dev *hace_dev)
{
struct aspeed_engine_crypto *crypto_engine = &hace_dev->crypto_engine;
struct aspeed_sg_list *src_list, *dst_list;
dma_addr_t src_dma_addr, dst_dma_addr;
struct aspeed_cipher_reqctx *rctx;
struct skcipher_request *req;
struct scatterlist *s;
int src_sg_len;
int dst_sg_len;
int total, i;
int rc;
CIPHER_DBG(hace_dev, "\n");
req = crypto_engine->req;
rctx = skcipher_request_ctx(req);
rctx->enc_cmd |= HACE_CMD_DES_SG_CTRL | HACE_CMD_SRC_SG_CTRL |
HACE_CMD_AES_KEY_HW_EXP | HACE_CMD_MBUS_REQ_SYNC_EN;
/* BIDIRECTIONAL */
if (req->dst == req->src) {
src_sg_len = dma_map_sg(hace_dev->dev, req->src,
rctx->src_nents, DMA_BIDIRECTIONAL);
dst_sg_len = src_sg_len;
if (!src_sg_len) {
dev_warn(hace_dev->dev, "dma_map_sg() src error\n");
return -EINVAL;
}
} else {
src_sg_len = dma_map_sg(hace_dev->dev, req->src,
rctx->src_nents, DMA_TO_DEVICE);
if (!src_sg_len) {
dev_warn(hace_dev->dev, "dma_map_sg() src error\n");
return -EINVAL;
}
dst_sg_len = dma_map_sg(hace_dev->dev, req->dst,
rctx->dst_nents, DMA_FROM_DEVICE);
if (!dst_sg_len) {
dev_warn(hace_dev->dev, "dma_map_sg() dst error\n");
rc = -EINVAL;
goto free_req_src;
}
}
src_list = (struct aspeed_sg_list *)crypto_engine->cipher_addr;
src_dma_addr = crypto_engine->cipher_dma_addr;
total = req->cryptlen;
for_each_sg(req->src, s, src_sg_len, i) {
u32 phy_addr = sg_dma_address(s);
u32 len = sg_dma_len(s);
if (total > len)
total -= len;
else {
/* last sg list */
len = total;
len |= BIT(31);
total = 0;
}
src_list[i].phy_addr = cpu_to_le32(phy_addr);
src_list[i].len = cpu_to_le32(len);
}
if (total != 0) {
rc = -EINVAL;
goto free_req;
}
if (req->dst == req->src) {
dst_list = src_list;
dst_dma_addr = src_dma_addr;
} else {
dst_list = (struct aspeed_sg_list *)crypto_engine->dst_sg_addr;
dst_dma_addr = crypto_engine->dst_sg_dma_addr;
total = req->cryptlen;
for_each_sg(req->dst, s, dst_sg_len, i) {
u32 phy_addr = sg_dma_address(s);
u32 len = sg_dma_len(s);
if (total > len)
total -= len;
else {
/* last sg list */
len = total;
len |= BIT(31);
total = 0;
}
dst_list[i].phy_addr = cpu_to_le32(phy_addr);
dst_list[i].len = cpu_to_le32(len);
}
dst_list[dst_sg_len].phy_addr = 0;
dst_list[dst_sg_len].len = 0;
}
if (total != 0) {
rc = -EINVAL;
goto free_req;
}
crypto_engine->resume = aspeed_sk_transfer_sg;
/* Memory barrier to ensure all data setup before engine starts */
mb();
/* Trigger engines */
ast_hace_write(hace_dev, src_dma_addr, ASPEED_HACE_SRC);
ast_hace_write(hace_dev, dst_dma_addr, ASPEED_HACE_DEST);
ast_hace_write(hace_dev, req->cryptlen, ASPEED_HACE_DATA_LEN);
ast_hace_write(hace_dev, rctx->enc_cmd, ASPEED_HACE_CMD);
return -EINPROGRESS;
free_req:
if (req->dst == req->src) {
dma_unmap_sg(hace_dev->dev, req->src, rctx->src_nents,
DMA_BIDIRECTIONAL);
} else {
dma_unmap_sg(hace_dev->dev, req->dst, rctx->dst_nents,
DMA_FROM_DEVICE);
dma_unmap_sg(hace_dev->dev, req->src, rctx->src_nents,
DMA_TO_DEVICE);
}
return rc;
free_req_src:
dma_unmap_sg(hace_dev->dev, req->src, rctx->src_nents, DMA_TO_DEVICE);
return rc;
}
static int aspeed_hace_skcipher_trigger(struct aspeed_hace_dev *hace_dev)
{
struct aspeed_engine_crypto *crypto_engine = &hace_dev->crypto_engine;
struct aspeed_cipher_reqctx *rctx;
struct crypto_skcipher *cipher;
struct aspeed_cipher_ctx *ctx;
struct skcipher_request *req;
CIPHER_DBG(hace_dev, "\n");
req = crypto_engine->req;
rctx = skcipher_request_ctx(req);
cipher = crypto_skcipher_reqtfm(req);
ctx = crypto_skcipher_ctx(cipher);
/* enable interrupt */
rctx->enc_cmd |= HACE_CMD_ISR_EN;
rctx->dst_nents = sg_nents(req->dst);
rctx->src_nents = sg_nents(req->src);
ast_hace_write(hace_dev, crypto_engine->cipher_ctx_dma,
ASPEED_HACE_CONTEXT);
if (rctx->enc_cmd & HACE_CMD_IV_REQUIRE) {
if (rctx->enc_cmd & HACE_CMD_DES_SELECT)
memcpy(crypto_engine->cipher_ctx + DES_BLOCK_SIZE,
req->iv, DES_BLOCK_SIZE);
else
memcpy(crypto_engine->cipher_ctx, req->iv,
AES_BLOCK_SIZE);
}
if (hace_dev->version == AST2600_VERSION) {
memcpy(crypto_engine->cipher_ctx + 16, ctx->key, ctx->key_len);
return aspeed_sk_start_sg(hace_dev);
}
memcpy(crypto_engine->cipher_ctx + 16, ctx->key, AES_MAX_KEYLENGTH);
return aspeed_sk_start(hace_dev);
}
static int aspeed_des_crypt(struct skcipher_request *req, u32 cmd)
{
struct aspeed_cipher_reqctx *rctx = skcipher_request_ctx(req);
struct crypto_skcipher *cipher = crypto_skcipher_reqtfm(req);
struct aspeed_cipher_ctx *ctx = crypto_skcipher_ctx(cipher);
struct aspeed_hace_dev *hace_dev = ctx->hace_dev;
u32 crypto_alg = cmd & HACE_CMD_OP_MODE_MASK;
CIPHER_DBG(hace_dev, "\n");
if (crypto_alg == HACE_CMD_CBC || crypto_alg == HACE_CMD_ECB) {
if (!IS_ALIGNED(req->cryptlen, DES_BLOCK_SIZE))
return -EINVAL;
}
rctx->enc_cmd = cmd | HACE_CMD_DES_SELECT | HACE_CMD_RI_WO_DATA_ENABLE |
HACE_CMD_DES | HACE_CMD_CONTEXT_LOAD_ENABLE |
HACE_CMD_CONTEXT_SAVE_ENABLE;
return aspeed_hace_crypto_handle_queue(hace_dev, req);
}
static int aspeed_des_setkey(struct crypto_skcipher *cipher, const u8 *key,
unsigned int keylen)
{
struct aspeed_cipher_ctx *ctx = crypto_skcipher_ctx(cipher);
struct crypto_tfm *tfm = crypto_skcipher_tfm(cipher);
struct aspeed_hace_dev *hace_dev = ctx->hace_dev;
int rc;
CIPHER_DBG(hace_dev, "keylen: %d bits\n", keylen);
if (keylen != DES_KEY_SIZE && keylen != DES3_EDE_KEY_SIZE) {
dev_warn(hace_dev->dev, "invalid keylen: %d bits\n", keylen);
return -EINVAL;
}
if (keylen == DES_KEY_SIZE) {
rc = crypto_des_verify_key(tfm, key);
if (rc)
return rc;
} else if (keylen == DES3_EDE_KEY_SIZE) {
rc = crypto_des3_ede_verify_key(tfm, key);
if (rc)
return rc;
}
memcpy(ctx->key, key, keylen);
ctx->key_len = keylen;
crypto_skcipher_clear_flags(ctx->fallback_tfm, CRYPTO_TFM_REQ_MASK);
crypto_skcipher_set_flags(ctx->fallback_tfm, cipher->base.crt_flags &
CRYPTO_TFM_REQ_MASK);
return crypto_skcipher_setkey(ctx->fallback_tfm, key, keylen);
}
static int aspeed_tdes_ctr_decrypt(struct skcipher_request *req)
{
return aspeed_des_crypt(req, HACE_CMD_DECRYPT | HACE_CMD_CTR |
HACE_CMD_TRIPLE_DES);
}
static int aspeed_tdes_ctr_encrypt(struct skcipher_request *req)
{
return aspeed_des_crypt(req, HACE_CMD_ENCRYPT | HACE_CMD_CTR |
HACE_CMD_TRIPLE_DES);
}
static int aspeed_tdes_cbc_decrypt(struct skcipher_request *req)
{
return aspeed_des_crypt(req, HACE_CMD_DECRYPT | HACE_CMD_CBC |
HACE_CMD_TRIPLE_DES);
}
static int aspeed_tdes_cbc_encrypt(struct skcipher_request *req)
{
return aspeed_des_crypt(req, HACE_CMD_ENCRYPT | HACE_CMD_CBC |
HACE_CMD_TRIPLE_DES);
}
static int aspeed_tdes_ecb_decrypt(struct skcipher_request *req)
{
return aspeed_des_crypt(req, HACE_CMD_DECRYPT | HACE_CMD_ECB |
HACE_CMD_TRIPLE_DES);
}
static int aspeed_tdes_ecb_encrypt(struct skcipher_request *req)
{
return aspeed_des_crypt(req, HACE_CMD_ENCRYPT | HACE_CMD_ECB |
HACE_CMD_TRIPLE_DES);
}
static int aspeed_des_ctr_decrypt(struct skcipher_request *req)
{
return aspeed_des_crypt(req, HACE_CMD_DECRYPT | HACE_CMD_CTR |
HACE_CMD_SINGLE_DES);
}
static int aspeed_des_ctr_encrypt(struct skcipher_request *req)
{
return aspeed_des_crypt(req, HACE_CMD_ENCRYPT | HACE_CMD_CTR |
HACE_CMD_SINGLE_DES);
}
static int aspeed_des_cbc_decrypt(struct skcipher_request *req)
{
return aspeed_des_crypt(req, HACE_CMD_DECRYPT | HACE_CMD_CBC |
HACE_CMD_SINGLE_DES);
}
static int aspeed_des_cbc_encrypt(struct skcipher_request *req)
{
return aspeed_des_crypt(req, HACE_CMD_ENCRYPT | HACE_CMD_CBC |
HACE_CMD_SINGLE_DES);
}
static int aspeed_des_ecb_decrypt(struct skcipher_request *req)
{
return aspeed_des_crypt(req, HACE_CMD_DECRYPT | HACE_CMD_ECB |
HACE_CMD_SINGLE_DES);
}
static int aspeed_des_ecb_encrypt(struct skcipher_request *req)
{
return aspeed_des_crypt(req, HACE_CMD_ENCRYPT | HACE_CMD_ECB |
HACE_CMD_SINGLE_DES);
}
static int aspeed_aes_crypt(struct skcipher_request *req, u32 cmd)
{
struct aspeed_cipher_reqctx *rctx = skcipher_request_ctx(req);
struct crypto_skcipher *cipher = crypto_skcipher_reqtfm(req);
struct aspeed_cipher_ctx *ctx = crypto_skcipher_ctx(cipher);
struct aspeed_hace_dev *hace_dev = ctx->hace_dev;
u32 crypto_alg = cmd & HACE_CMD_OP_MODE_MASK;
if (crypto_alg == HACE_CMD_CBC || crypto_alg == HACE_CMD_ECB) {
if (!IS_ALIGNED(req->cryptlen, AES_BLOCK_SIZE))
return -EINVAL;
}
CIPHER_DBG(hace_dev, "%s\n",
(cmd & HACE_CMD_ENCRYPT) ? "encrypt" : "decrypt");
cmd |= HACE_CMD_AES_SELECT | HACE_CMD_RI_WO_DATA_ENABLE |
HACE_CMD_CONTEXT_LOAD_ENABLE | HACE_CMD_CONTEXT_SAVE_ENABLE;
switch (ctx->key_len) {
case AES_KEYSIZE_128:
cmd |= HACE_CMD_AES128;
break;
case AES_KEYSIZE_192:
cmd |= HACE_CMD_AES192;
break;
case AES_KEYSIZE_256:
cmd |= HACE_CMD_AES256;
break;
default:
return -EINVAL;
}
rctx->enc_cmd = cmd;
return aspeed_hace_crypto_handle_queue(hace_dev, req);
}
static int aspeed_aes_setkey(struct crypto_skcipher *cipher, const u8 *key,
unsigned int keylen)
{
struct aspeed_cipher_ctx *ctx = crypto_skcipher_ctx(cipher);
struct aspeed_hace_dev *hace_dev = ctx->hace_dev;
struct crypto_aes_ctx gen_aes_key;
CIPHER_DBG(hace_dev, "keylen: %d bits\n", (keylen * 8));
if (keylen != AES_KEYSIZE_128 && keylen != AES_KEYSIZE_192 &&
keylen != AES_KEYSIZE_256)
return -EINVAL;
if (ctx->hace_dev->version == AST2500_VERSION) {
aes_expandkey(&gen_aes_key, key, keylen);
memcpy(ctx->key, gen_aes_key.key_enc, AES_MAX_KEYLENGTH);
} else {
memcpy(ctx->key, key, keylen);
}
ctx->key_len = keylen;
crypto_skcipher_clear_flags(ctx->fallback_tfm, CRYPTO_TFM_REQ_MASK);
crypto_skcipher_set_flags(ctx->fallback_tfm, cipher->base.crt_flags &
CRYPTO_TFM_REQ_MASK);
return crypto_skcipher_setkey(ctx->fallback_tfm, key, keylen);
}
static int aspeed_aes_ctr_decrypt(struct skcipher_request *req)
{
return aspeed_aes_crypt(req, HACE_CMD_DECRYPT | HACE_CMD_CTR);
}
static int aspeed_aes_ctr_encrypt(struct skcipher_request *req)
{
return aspeed_aes_crypt(req, HACE_CMD_ENCRYPT | HACE_CMD_CTR);
}
static int aspeed_aes_cbc_decrypt(struct skcipher_request *req)
{
return aspeed_aes_crypt(req, HACE_CMD_DECRYPT | HACE_CMD_CBC);
}
static int aspeed_aes_cbc_encrypt(struct skcipher_request *req)
{
return aspeed_aes_crypt(req, HACE_CMD_ENCRYPT | HACE_CMD_CBC);
}
static int aspeed_aes_ecb_decrypt(struct skcipher_request *req)
{
return aspeed_aes_crypt(req, HACE_CMD_DECRYPT | HACE_CMD_ECB);
}
static int aspeed_aes_ecb_encrypt(struct skcipher_request *req)
{
return aspeed_aes_crypt(req, HACE_CMD_ENCRYPT | HACE_CMD_ECB);
}
static int aspeed_crypto_cra_init(struct crypto_skcipher *tfm)
{
struct aspeed_cipher_ctx *ctx = crypto_skcipher_ctx(tfm);
struct skcipher_alg *alg = crypto_skcipher_alg(tfm);
const char *name = crypto_tfm_alg_name(&tfm->base);
struct aspeed_hace_alg *crypto_alg;
crypto_alg = container_of(alg, struct aspeed_hace_alg, alg.skcipher.base);
ctx->hace_dev = crypto_alg->hace_dev;
ctx->start = aspeed_hace_skcipher_trigger;
CIPHER_DBG(ctx->hace_dev, "%s\n", name);
ctx->fallback_tfm = crypto_alloc_skcipher(name, 0, CRYPTO_ALG_ASYNC |
CRYPTO_ALG_NEED_FALLBACK);
if (IS_ERR(ctx->fallback_tfm)) {
dev_err(ctx->hace_dev->dev, "ERROR: Cannot allocate fallback for %s %ld\n",
name, PTR_ERR(ctx->fallback_tfm));
return PTR_ERR(ctx->fallback_tfm);
}
crypto_skcipher_set_reqsize(tfm, sizeof(struct aspeed_cipher_reqctx) +
crypto_skcipher_reqsize(ctx->fallback_tfm));
return 0;
}
static void aspeed_crypto_cra_exit(struct crypto_skcipher *tfm)
{
struct aspeed_cipher_ctx *ctx = crypto_skcipher_ctx(tfm);
struct aspeed_hace_dev *hace_dev = ctx->hace_dev;
CIPHER_DBG(hace_dev, "%s\n", crypto_tfm_alg_name(&tfm->base));
crypto_free_skcipher(ctx->fallback_tfm);
}
static struct aspeed_hace_alg aspeed_crypto_algs[] = {
{
.alg.skcipher.base = {
.min_keysize = AES_MIN_KEY_SIZE,
.max_keysize = AES_MAX_KEY_SIZE,
.setkey = aspeed_aes_setkey,
.encrypt = aspeed_aes_ecb_encrypt,
.decrypt = aspeed_aes_ecb_decrypt,
.init = aspeed_crypto_cra_init,
.exit = aspeed_crypto_cra_exit,
.base = {
.cra_name = "ecb(aes)",
.cra_driver_name = "aspeed-ecb-aes",
.cra_priority = 300,
.cra_flags = CRYPTO_ALG_KERN_DRIVER_ONLY |
CRYPTO_ALG_ASYNC |
CRYPTO_ALG_NEED_FALLBACK,
.cra_blocksize = AES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct aspeed_cipher_ctx),
.cra_alignmask = 0x0f,
.cra_module = THIS_MODULE,
}
},
.alg.skcipher.op = {
.do_one_request = aspeed_crypto_do_request,
},
},
{
.alg.skcipher.base = {
.ivsize = AES_BLOCK_SIZE,
.min_keysize = AES_MIN_KEY_SIZE,
.max_keysize = AES_MAX_KEY_SIZE,
.setkey = aspeed_aes_setkey,
.encrypt = aspeed_aes_cbc_encrypt,
.decrypt = aspeed_aes_cbc_decrypt,
.init = aspeed_crypto_cra_init,
.exit = aspeed_crypto_cra_exit,
.base = {
.cra_name = "cbc(aes)",
.cra_driver_name = "aspeed-cbc-aes",
.cra_priority = 300,
.cra_flags = CRYPTO_ALG_KERN_DRIVER_ONLY |
CRYPTO_ALG_ASYNC |
CRYPTO_ALG_NEED_FALLBACK,
.cra_blocksize = AES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct aspeed_cipher_ctx),
.cra_alignmask = 0x0f,
.cra_module = THIS_MODULE,
}
},
.alg.skcipher.op = {
.do_one_request = aspeed_crypto_do_request,
},
},
{
.alg.skcipher.base = {
.min_keysize = DES_KEY_SIZE,
.max_keysize = DES_KEY_SIZE,
.setkey = aspeed_des_setkey,
.encrypt = aspeed_des_ecb_encrypt,
.decrypt = aspeed_des_ecb_decrypt,
.init = aspeed_crypto_cra_init,
.exit = aspeed_crypto_cra_exit,
.base = {
.cra_name = "ecb(des)",
.cra_driver_name = "aspeed-ecb-des",
.cra_priority = 300,
.cra_flags = CRYPTO_ALG_KERN_DRIVER_ONLY |
CRYPTO_ALG_ASYNC |
CRYPTO_ALG_NEED_FALLBACK,
.cra_blocksize = DES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct aspeed_cipher_ctx),
.cra_alignmask = 0x0f,
.cra_module = THIS_MODULE,
}
},
.alg.skcipher.op = {
.do_one_request = aspeed_crypto_do_request,
},
},
{
.alg.skcipher.base = {
.ivsize = DES_BLOCK_SIZE,
.min_keysize = DES_KEY_SIZE,
.max_keysize = DES_KEY_SIZE,
.setkey = aspeed_des_setkey,
.encrypt = aspeed_des_cbc_encrypt,
.decrypt = aspeed_des_cbc_decrypt,
.init = aspeed_crypto_cra_init,
.exit = aspeed_crypto_cra_exit,
.base = {
.cra_name = "cbc(des)",
.cra_driver_name = "aspeed-cbc-des",
.cra_priority = 300,
.cra_flags = CRYPTO_ALG_KERN_DRIVER_ONLY |
CRYPTO_ALG_ASYNC |
CRYPTO_ALG_NEED_FALLBACK,
.cra_blocksize = DES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct aspeed_cipher_ctx),
.cra_alignmask = 0x0f,
.cra_module = THIS_MODULE,
}
},
.alg.skcipher.op = {
.do_one_request = aspeed_crypto_do_request,
},
},
{
.alg.skcipher.base = {
.min_keysize = DES3_EDE_KEY_SIZE,
.max_keysize = DES3_EDE_KEY_SIZE,
.setkey = aspeed_des_setkey,
.encrypt = aspeed_tdes_ecb_encrypt,
.decrypt = aspeed_tdes_ecb_decrypt,
.init = aspeed_crypto_cra_init,
.exit = aspeed_crypto_cra_exit,
.base = {
.cra_name = "ecb(des3_ede)",
.cra_driver_name = "aspeed-ecb-tdes",
.cra_priority = 300,
.cra_flags = CRYPTO_ALG_KERN_DRIVER_ONLY |
CRYPTO_ALG_ASYNC |
CRYPTO_ALG_NEED_FALLBACK,
.cra_blocksize = DES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct aspeed_cipher_ctx),
.cra_alignmask = 0x0f,
.cra_module = THIS_MODULE,
}
},
.alg.skcipher.op = {
.do_one_request = aspeed_crypto_do_request,
},
},
{
.alg.skcipher.base = {
.ivsize = DES_BLOCK_SIZE,
.min_keysize = DES3_EDE_KEY_SIZE,
.max_keysize = DES3_EDE_KEY_SIZE,
.setkey = aspeed_des_setkey,
.encrypt = aspeed_tdes_cbc_encrypt,
.decrypt = aspeed_tdes_cbc_decrypt,
.init = aspeed_crypto_cra_init,
.exit = aspeed_crypto_cra_exit,
.base = {
.cra_name = "cbc(des3_ede)",
.cra_driver_name = "aspeed-cbc-tdes",
.cra_priority = 300,
.cra_flags = CRYPTO_ALG_KERN_DRIVER_ONLY |
CRYPTO_ALG_ASYNC |
CRYPTO_ALG_NEED_FALLBACK,
.cra_blocksize = DES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct aspeed_cipher_ctx),
.cra_alignmask = 0x0f,
.cra_module = THIS_MODULE,
}
},
.alg.skcipher.op = {
.do_one_request = aspeed_crypto_do_request,
},
},
};
static struct aspeed_hace_alg aspeed_crypto_algs_g6[] = {
{
.alg.skcipher.base = {
.ivsize = AES_BLOCK_SIZE,
.min_keysize = AES_MIN_KEY_SIZE,
.max_keysize = AES_MAX_KEY_SIZE,
.setkey = aspeed_aes_setkey,
.encrypt = aspeed_aes_ctr_encrypt,
.decrypt = aspeed_aes_ctr_decrypt,
.init = aspeed_crypto_cra_init,
.exit = aspeed_crypto_cra_exit,
.base = {
.cra_name = "ctr(aes)",
.cra_driver_name = "aspeed-ctr-aes",
.cra_priority = 300,
.cra_flags = CRYPTO_ALG_KERN_DRIVER_ONLY |
CRYPTO_ALG_ASYNC,
.cra_blocksize = 1,
.cra_ctxsize = sizeof(struct aspeed_cipher_ctx),
.cra_alignmask = 0x0f,
.cra_module = THIS_MODULE,
}
},
.alg.skcipher.op = {
.do_one_request = aspeed_crypto_do_request,
},
},
{
.alg.skcipher.base = {
.ivsize = DES_BLOCK_SIZE,
.min_keysize = DES_KEY_SIZE,
.max_keysize = DES_KEY_SIZE,
.setkey = aspeed_des_setkey,
.encrypt = aspeed_des_ctr_encrypt,
.decrypt = aspeed_des_ctr_decrypt,
.init = aspeed_crypto_cra_init,
.exit = aspeed_crypto_cra_exit,
.base = {
.cra_name = "ctr(des)",
.cra_driver_name = "aspeed-ctr-des",
.cra_priority = 300,
.cra_flags = CRYPTO_ALG_KERN_DRIVER_ONLY |
CRYPTO_ALG_ASYNC,
.cra_blocksize = 1,
.cra_ctxsize = sizeof(struct aspeed_cipher_ctx),
.cra_alignmask = 0x0f,
.cra_module = THIS_MODULE,
}
},
.alg.skcipher.op = {
.do_one_request = aspeed_crypto_do_request,
},
},
{
.alg.skcipher.base = {
.ivsize = DES_BLOCK_SIZE,
.min_keysize = DES3_EDE_KEY_SIZE,
.max_keysize = DES3_EDE_KEY_SIZE,
.setkey = aspeed_des_setkey,
.encrypt = aspeed_tdes_ctr_encrypt,
.decrypt = aspeed_tdes_ctr_decrypt,
.init = aspeed_crypto_cra_init,
.exit = aspeed_crypto_cra_exit,
.base = {
.cra_name = "ctr(des3_ede)",
.cra_driver_name = "aspeed-ctr-tdes",
.cra_priority = 300,
.cra_flags = CRYPTO_ALG_KERN_DRIVER_ONLY |
CRYPTO_ALG_ASYNC,
.cra_blocksize = 1,
.cra_ctxsize = sizeof(struct aspeed_cipher_ctx),
.cra_alignmask = 0x0f,
.cra_module = THIS_MODULE,
}
},
.alg.skcipher.op = {
.do_one_request = aspeed_crypto_do_request,
},
},
};
void aspeed_unregister_hace_crypto_algs(struct aspeed_hace_dev *hace_dev)
{
int i;
for (i = 0; i < ARRAY_SIZE(aspeed_crypto_algs); i++)
crypto_engine_unregister_skcipher(&aspeed_crypto_algs[i].alg.skcipher);
if (hace_dev->version != AST2600_VERSION)
return;
for (i = 0; i < ARRAY_SIZE(aspeed_crypto_algs_g6); i++)
crypto_engine_unregister_skcipher(&aspeed_crypto_algs_g6[i].alg.skcipher);
}
void aspeed_register_hace_crypto_algs(struct aspeed_hace_dev *hace_dev)
{
int rc, i;
CIPHER_DBG(hace_dev, "\n");
for (i = 0; i < ARRAY_SIZE(aspeed_crypto_algs); i++) {
aspeed_crypto_algs[i].hace_dev = hace_dev;
rc = crypto_engine_register_skcipher(&aspeed_crypto_algs[i].alg.skcipher);
if (rc) {
CIPHER_DBG(hace_dev, "Failed to register %s\n",
aspeed_crypto_algs[i].alg.skcipher.base.base.cra_name);
}
}
if (hace_dev->version != AST2600_VERSION)
return;
for (i = 0; i < ARRAY_SIZE(aspeed_crypto_algs_g6); i++) {
aspeed_crypto_algs_g6[i].hace_dev = hace_dev;
rc = crypto_engine_register_skcipher(&aspeed_crypto_algs_g6[i].alg.skcipher);
if (rc) {
CIPHER_DBG(hace_dev, "Failed to register %s\n",
aspeed_crypto_algs_g6[i].alg.skcipher.base.base.cra_name);
}
}
}
@@ -0,0 +1,825 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2021 Aspeed Technology Inc.
*/
#include "aspeed-hace.h"
#include <crypto/engine.h>
#include <crypto/internal/hash.h>
#include <crypto/scatterwalk.h>
#include <crypto/sha1.h>
#include <crypto/sha2.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/scatterlist.h>
#include <linux/string.h>
#ifdef CONFIG_CRYPTO_DEV_ASPEED_DEBUG
#define AHASH_DBG(h, fmt, ...) \
dev_info((h)->dev, "%s() " fmt, __func__, ##__VA_ARGS__)
#else
#define AHASH_DBG(h, fmt, ...) \
dev_dbg((h)->dev, "%s() " fmt, __func__, ##__VA_ARGS__)
#endif
/* Initialization Vectors for SHA-family */
static const __be32 sha1_iv[8] = {
cpu_to_be32(SHA1_H0), cpu_to_be32(SHA1_H1),
cpu_to_be32(SHA1_H2), cpu_to_be32(SHA1_H3),
cpu_to_be32(SHA1_H4), 0, 0, 0
};
static const __be32 sha224_iv[8] = {
cpu_to_be32(SHA224_H0), cpu_to_be32(SHA224_H1),
cpu_to_be32(SHA224_H2), cpu_to_be32(SHA224_H3),
cpu_to_be32(SHA224_H4), cpu_to_be32(SHA224_H5),
cpu_to_be32(SHA224_H6), cpu_to_be32(SHA224_H7),
};
static const __be32 sha256_iv[8] = {
cpu_to_be32(SHA256_H0), cpu_to_be32(SHA256_H1),
cpu_to_be32(SHA256_H2), cpu_to_be32(SHA256_H3),
cpu_to_be32(SHA256_H4), cpu_to_be32(SHA256_H5),
cpu_to_be32(SHA256_H6), cpu_to_be32(SHA256_H7),
};
static const __be64 sha384_iv[8] = {
cpu_to_be64(SHA384_H0), cpu_to_be64(SHA384_H1),
cpu_to_be64(SHA384_H2), cpu_to_be64(SHA384_H3),
cpu_to_be64(SHA384_H4), cpu_to_be64(SHA384_H5),
cpu_to_be64(SHA384_H6), cpu_to_be64(SHA384_H7)
};
static const __be64 sha512_iv[8] = {
cpu_to_be64(SHA512_H0), cpu_to_be64(SHA512_H1),
cpu_to_be64(SHA512_H2), cpu_to_be64(SHA512_H3),
cpu_to_be64(SHA512_H4), cpu_to_be64(SHA512_H5),
cpu_to_be64(SHA512_H6), cpu_to_be64(SHA512_H7)
};
static int aspeed_sham_init(struct ahash_request *req);
static int aspeed_ahash_req_update(struct aspeed_hace_dev *hace_dev);
static int aspeed_sham_export(struct ahash_request *req, void *out)
{
struct aspeed_sham_reqctx *rctx = ahash_request_ctx(req);
union {
u8 *u8;
u64 *u64;
} p = { .u8 = out };
memcpy(out, rctx->digest, rctx->ivsize);
p.u8 += rctx->ivsize;
put_unaligned(rctx->digcnt[0], p.u64++);
if (rctx->ivsize == 64)
put_unaligned(rctx->digcnt[1], p.u64);
return 0;
}
static int aspeed_sham_import(struct ahash_request *req, const void *in)
{
struct aspeed_sham_reqctx *rctx = ahash_request_ctx(req);
union {
const u8 *u8;
const u64 *u64;
} p = { .u8 = in };
int err;
err = aspeed_sham_init(req);
if (err)
return err;
memcpy(rctx->digest, in, rctx->ivsize);
p.u8 += rctx->ivsize;
rctx->digcnt[0] = get_unaligned(p.u64++);
if (rctx->ivsize == 64)
rctx->digcnt[1] = get_unaligned(p.u64);
return 0;
}
/* The purpose of this padding is to ensure that the padded message is a
* multiple of 512 bits (SHA1/SHA224/SHA256) or 1024 bits (SHA384/SHA512).
* The bit "1" is appended at the end of the message followed by
* "padlen-1" zero bits. Then a 64 bits block (SHA1/SHA224/SHA256) or
* 128 bits block (SHA384/SHA512) equals to the message length in bits
* is appended.
*
* For SHA1/SHA224/SHA256, padlen is calculated as followed:
* - if message length < 56 bytes then padlen = 56 - message length
* - else padlen = 64 + 56 - message length
*
* For SHA384/SHA512, padlen is calculated as followed:
* - if message length < 112 bytes then padlen = 112 - message length
* - else padlen = 128 + 112 - message length
*/
static int aspeed_ahash_fill_padding(struct aspeed_hace_dev *hace_dev,
struct aspeed_sham_reqctx *rctx, u8 *buf)
{
unsigned int index, padlen, bitslen;
__be64 bits[2];
AHASH_DBG(hace_dev, "rctx flags:0x%x\n", (u32)rctx->flags);
switch (rctx->flags & SHA_FLAGS_MASK) {
case SHA_FLAGS_SHA1:
case SHA_FLAGS_SHA224:
case SHA_FLAGS_SHA256:
bits[0] = cpu_to_be64(rctx->digcnt[0] << 3);
index = rctx->digcnt[0] & 0x3f;
padlen = (index < 56) ? (56 - index) : ((64 + 56) - index);
bitslen = 8;
break;
default:
bits[1] = cpu_to_be64(rctx->digcnt[0] << 3);
bits[0] = cpu_to_be64(rctx->digcnt[1] << 3 |
rctx->digcnt[0] >> 61);
index = rctx->digcnt[0] & 0x7f;
padlen = (index < 112) ? (112 - index) : ((128 + 112) - index);
bitslen = 16;
break;
}
buf[0] = 0x80;
memset(buf + 1, 0, padlen - 1);
memcpy(buf + padlen, bits, bitslen);
return padlen + bitslen;
}
static void aspeed_ahash_update_counter(struct aspeed_sham_reqctx *rctx,
unsigned int len)
{
rctx->offset += len;
rctx->digcnt[0] += len;
if (rctx->digcnt[0] < len)
rctx->digcnt[1]++;
}
/*
* Prepare DMA buffer before hardware engine
* processing.
*/
static int aspeed_ahash_dma_prepare(struct aspeed_hace_dev *hace_dev)
{
struct aspeed_engine_hash *hash_engine = &hace_dev->hash_engine;
struct ahash_request *req = hash_engine->req;
struct aspeed_sham_reqctx *rctx = ahash_request_ctx(req);
unsigned int length, remain;
bool final = false;
length = rctx->total - rctx->offset;
remain = length - round_down(length, rctx->block_size);
AHASH_DBG(hace_dev, "length:0x%x, remain:0x%x\n", length, remain);
if (length > ASPEED_HASH_SRC_DMA_BUF_LEN)
length = ASPEED_HASH_SRC_DMA_BUF_LEN;
else if (rctx->flags & SHA_FLAGS_FINUP) {
if (round_up(length, rctx->block_size) + rctx->block_size >
ASPEED_CRYPTO_SRC_DMA_BUF_LEN)
length = round_down(length - 1, rctx->block_size);
else
final = true;
} else
length -= remain;
memcpy_from_sglist(hash_engine->ahash_src_addr, rctx->src_sg, rctx->offset, length);
aspeed_ahash_update_counter(rctx, length);
if (final)
length += aspeed_ahash_fill_padding(
hace_dev, rctx, hash_engine->ahash_src_addr + length);
rctx->digest_dma_addr = dma_map_single(hace_dev->dev, rctx->digest,
SHA512_DIGEST_SIZE,
DMA_BIDIRECTIONAL);
if (dma_mapping_error(hace_dev->dev, rctx->digest_dma_addr)) {
dev_warn(hace_dev->dev, "dma_map() rctx digest error\n");
return -ENOMEM;
}
hash_engine->src_length = length;
hash_engine->src_dma = hash_engine->ahash_src_dma_addr;
hash_engine->digest_dma = rctx->digest_dma_addr;
return 0;
}
/*
* Prepare DMA buffer as SG list buffer before
* hardware engine processing.
*/
static int aspeed_ahash_dma_prepare_sg(struct aspeed_hace_dev *hace_dev)
{
struct aspeed_engine_hash *hash_engine = &hace_dev->hash_engine;
struct ahash_request *req = hash_engine->req;
struct aspeed_sham_reqctx *rctx = ahash_request_ctx(req);
bool final = rctx->flags & SHA_FLAGS_FINUP;
int remain, sg_len, i, max_sg_nents;
unsigned int length, offset, total;
struct aspeed_sg_list *src_list;
struct scatterlist *s;
int rc = 0;
offset = rctx->offset;
length = rctx->total - offset;
remain = final ? 0 : length - round_down(length, rctx->block_size);
length -= remain;
AHASH_DBG(hace_dev, "%s:0x%x, %s:0x%x, %s:0x%x\n",
"rctx total", rctx->total,
"length", length, "remain", remain);
sg_len = dma_map_sg(hace_dev->dev, rctx->src_sg, rctx->src_nents,
DMA_TO_DEVICE);
if (!sg_len) {
dev_warn(hace_dev->dev, "dma_map_sg() src error\n");
rc = -ENOMEM;
goto end;
}
max_sg_nents = ASPEED_HASH_SRC_DMA_BUF_LEN / sizeof(*src_list) - final;
sg_len = min(sg_len, max_sg_nents);
src_list = (struct aspeed_sg_list *)hash_engine->ahash_src_addr;
rctx->digest_dma_addr = dma_map_single(hace_dev->dev, rctx->digest,
SHA512_DIGEST_SIZE,
DMA_BIDIRECTIONAL);
if (dma_mapping_error(hace_dev->dev, rctx->digest_dma_addr)) {
dev_warn(hace_dev->dev, "dma_map() rctx digest error\n");
rc = -ENOMEM;
goto free_src_sg;
}
total = 0;
for_each_sg(rctx->src_sg, s, sg_len, i) {
u32 phy_addr = sg_dma_address(s);
u32 len = sg_dma_len(s);
if (len <= offset) {
offset -= len;
continue;
}
len -= offset;
phy_addr += offset;
offset = 0;
if (length > len)
length -= len;
else {
/* Last sg list */
len = length;
length = 0;
}
total += len;
src_list[i].phy_addr = cpu_to_le32(phy_addr);
src_list[i].len = cpu_to_le32(len);
}
if (length != 0) {
total = round_down(total, rctx->block_size);
final = false;
}
aspeed_ahash_update_counter(rctx, total);
if (final) {
int len = aspeed_ahash_fill_padding(hace_dev, rctx,
rctx->buffer);
total += len;
rctx->buffer_dma_addr = dma_map_single(hace_dev->dev,
rctx->buffer,
sizeof(rctx->buffer),
DMA_TO_DEVICE);
if (dma_mapping_error(hace_dev->dev, rctx->buffer_dma_addr)) {
dev_warn(hace_dev->dev, "dma_map() rctx buffer error\n");
rc = -ENOMEM;
goto free_rctx_digest;
}
src_list[i].phy_addr = cpu_to_le32(rctx->buffer_dma_addr);
src_list[i].len = cpu_to_le32(len);
i++;
}
src_list[i - 1].len |= cpu_to_le32(HASH_SG_LAST_LIST);
hash_engine->src_length = total;
hash_engine->src_dma = hash_engine->ahash_src_dma_addr;
hash_engine->digest_dma = rctx->digest_dma_addr;
return 0;
free_rctx_digest:
dma_unmap_single(hace_dev->dev, rctx->digest_dma_addr,
SHA512_DIGEST_SIZE, DMA_BIDIRECTIONAL);
free_src_sg:
dma_unmap_sg(hace_dev->dev, rctx->src_sg, rctx->src_nents,
DMA_TO_DEVICE);
end:
return rc;
}
static int aspeed_ahash_complete(struct aspeed_hace_dev *hace_dev)
{
struct aspeed_engine_hash *hash_engine = &hace_dev->hash_engine;
struct ahash_request *req = hash_engine->req;
struct aspeed_sham_reqctx *rctx = ahash_request_ctx(req);
AHASH_DBG(hace_dev, "\n");
dma_unmap_single(hace_dev->dev, rctx->digest_dma_addr,
SHA512_DIGEST_SIZE, DMA_BIDIRECTIONAL);
if (rctx->total - rctx->offset >= rctx->block_size ||
(rctx->total != rctx->offset && rctx->flags & SHA_FLAGS_FINUP))
return aspeed_ahash_req_update(hace_dev);
hash_engine->flags &= ~CRYPTO_FLAGS_BUSY;
if (rctx->flags & SHA_FLAGS_FINUP)
memcpy(req->result, rctx->digest, rctx->digsize);
crypto_finalize_hash_request(hace_dev->crypt_engine_hash, req,
rctx->total - rctx->offset);
return 0;
}
/*
* Trigger hardware engines to do the math.
*/
static int aspeed_hace_ahash_trigger(struct aspeed_hace_dev *hace_dev,
aspeed_hace_fn_t resume)
{
struct aspeed_engine_hash *hash_engine = &hace_dev->hash_engine;
struct ahash_request *req = hash_engine->req;
struct aspeed_sham_reqctx *rctx = ahash_request_ctx(req);
AHASH_DBG(hace_dev, "src_dma:%pad, digest_dma:%pad, length:%zu\n",
&hash_engine->src_dma, &hash_engine->digest_dma,
hash_engine->src_length);
rctx->cmd |= HASH_CMD_INT_ENABLE;
hash_engine->resume = resume;
ast_hace_write(hace_dev, hash_engine->src_dma, ASPEED_HACE_HASH_SRC);
ast_hace_write(hace_dev, hash_engine->digest_dma,
ASPEED_HACE_HASH_DIGEST_BUFF);
ast_hace_write(hace_dev, hash_engine->digest_dma,
ASPEED_HACE_HASH_KEY_BUFF);
ast_hace_write(hace_dev, hash_engine->src_length,
ASPEED_HACE_HASH_DATA_LEN);
/* Memory barrier to ensure all data setup before engine starts */
mb();
ast_hace_write(hace_dev, rctx->cmd, ASPEED_HACE_HASH_CMD);
return -EINPROGRESS;
}
static int aspeed_ahash_update_resume_sg(struct aspeed_hace_dev *hace_dev)
{
struct aspeed_engine_hash *hash_engine = &hace_dev->hash_engine;
struct ahash_request *req = hash_engine->req;
struct aspeed_sham_reqctx *rctx = ahash_request_ctx(req);
AHASH_DBG(hace_dev, "\n");
dma_unmap_sg(hace_dev->dev, rctx->src_sg, rctx->src_nents,
DMA_TO_DEVICE);
if (rctx->flags & SHA_FLAGS_FINUP && rctx->total == rctx->offset)
dma_unmap_single(hace_dev->dev, rctx->buffer_dma_addr,
sizeof(rctx->buffer), DMA_TO_DEVICE);
rctx->cmd &= ~HASH_CMD_HASH_SRC_SG_CTRL;
return aspeed_ahash_complete(hace_dev);
}
static int aspeed_ahash_req_update(struct aspeed_hace_dev *hace_dev)
{
struct aspeed_engine_hash *hash_engine = &hace_dev->hash_engine;
struct ahash_request *req = hash_engine->req;
struct aspeed_sham_reqctx *rctx = ahash_request_ctx(req);
aspeed_hace_fn_t resume;
int ret;
AHASH_DBG(hace_dev, "\n");
if (hace_dev->version == AST2600_VERSION) {
rctx->cmd |= HASH_CMD_HASH_SRC_SG_CTRL;
resume = aspeed_ahash_update_resume_sg;
} else {
resume = aspeed_ahash_complete;
}
ret = hash_engine->dma_prepare(hace_dev);
if (ret)
return ret;
return aspeed_hace_ahash_trigger(hace_dev, resume);
}
static int aspeed_hace_hash_handle_queue(struct aspeed_hace_dev *hace_dev,
struct ahash_request *req)
{
return crypto_transfer_hash_request_to_engine(
hace_dev->crypt_engine_hash, req);
}
static noinline int aspeed_ahash_fallback(struct ahash_request *req)
{
struct aspeed_sham_reqctx *rctx = ahash_request_ctx(req);
HASH_FBREQ_ON_STACK(fbreq, req);
u8 *state = rctx->buffer;
struct scatterlist sg[2];
struct scatterlist *ssg;
int ret;
ssg = scatterwalk_ffwd(sg, req->src, rctx->offset);
ahash_request_set_crypt(fbreq, ssg, req->result,
rctx->total - rctx->offset);
ret = aspeed_sham_export(req, state) ?:
crypto_ahash_import_core(fbreq, state);
if (rctx->flags & SHA_FLAGS_FINUP)
ret = ret ?: crypto_ahash_finup(fbreq);
else
ret = ret ?: crypto_ahash_update(fbreq) ?:
crypto_ahash_export_core(fbreq, state) ?:
aspeed_sham_import(req, state);
HASH_REQUEST_ZERO(fbreq);
return ret;
}
static int aspeed_ahash_do_request(struct crypto_engine *engine, void *areq)
{
struct ahash_request *req = ahash_request_cast(areq);
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
struct aspeed_sham_ctx *tctx = crypto_ahash_ctx(tfm);
struct aspeed_hace_dev *hace_dev = tctx->hace_dev;
struct aspeed_engine_hash *hash_engine;
int ret;
hash_engine = &hace_dev->hash_engine;
hash_engine->flags |= CRYPTO_FLAGS_BUSY;
ret = aspeed_ahash_req_update(hace_dev);
if (ret != -EINPROGRESS)
return aspeed_ahash_fallback(req);
return 0;
}
static void aspeed_ahash_prepare_request(struct crypto_engine *engine,
void *areq)
{
struct ahash_request *req = ahash_request_cast(areq);
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
struct aspeed_sham_ctx *tctx = crypto_ahash_ctx(tfm);
struct aspeed_hace_dev *hace_dev = tctx->hace_dev;
struct aspeed_engine_hash *hash_engine;
hash_engine = &hace_dev->hash_engine;
hash_engine->req = req;
if (hace_dev->version == AST2600_VERSION)
hash_engine->dma_prepare = aspeed_ahash_dma_prepare_sg;
else
hash_engine->dma_prepare = aspeed_ahash_dma_prepare;
}
static int aspeed_ahash_do_one(struct crypto_engine *engine, void *areq)
{
aspeed_ahash_prepare_request(engine, areq);
return aspeed_ahash_do_request(engine, areq);
}
static int aspeed_sham_update(struct ahash_request *req)
{
struct aspeed_sham_reqctx *rctx = ahash_request_ctx(req);
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
struct aspeed_sham_ctx *tctx = crypto_ahash_ctx(tfm);
struct aspeed_hace_dev *hace_dev = tctx->hace_dev;
AHASH_DBG(hace_dev, "req->nbytes: %d\n", req->nbytes);
rctx->total = req->nbytes;
rctx->src_sg = req->src;
rctx->offset = 0;
rctx->src_nents = sg_nents_for_len(req->src, req->nbytes);
return aspeed_hace_hash_handle_queue(hace_dev, req);
}
static int aspeed_sham_finup(struct ahash_request *req)
{
struct aspeed_sham_reqctx *rctx = ahash_request_ctx(req);
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
struct aspeed_sham_ctx *tctx = crypto_ahash_ctx(tfm);
struct aspeed_hace_dev *hace_dev = tctx->hace_dev;
AHASH_DBG(hace_dev, "req->nbytes: %d\n", req->nbytes);
rctx->flags |= SHA_FLAGS_FINUP;
return aspeed_sham_update(req);
}
static int aspeed_sham_init(struct ahash_request *req)
{
struct aspeed_sham_reqctx *rctx = ahash_request_ctx(req);
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
struct aspeed_sham_ctx *tctx = crypto_ahash_ctx(tfm);
struct aspeed_hace_dev *hace_dev = tctx->hace_dev;
AHASH_DBG(hace_dev, "%s: digest size:%d\n",
crypto_tfm_alg_name(&tfm->base),
crypto_ahash_digestsize(tfm));
rctx->cmd = HASH_CMD_ACC_MODE;
rctx->flags = 0;
switch (crypto_ahash_digestsize(tfm)) {
case SHA1_DIGEST_SIZE:
rctx->cmd |= HASH_CMD_SHA1 | HASH_CMD_SHA_SWAP;
rctx->flags |= SHA_FLAGS_SHA1;
rctx->digsize = SHA1_DIGEST_SIZE;
rctx->block_size = SHA1_BLOCK_SIZE;
rctx->ivsize = 32;
memcpy(rctx->digest, sha1_iv, rctx->ivsize);
break;
case SHA224_DIGEST_SIZE:
rctx->cmd |= HASH_CMD_SHA224 | HASH_CMD_SHA_SWAP;
rctx->flags |= SHA_FLAGS_SHA224;
rctx->digsize = SHA224_DIGEST_SIZE;
rctx->block_size = SHA224_BLOCK_SIZE;
rctx->ivsize = 32;
memcpy(rctx->digest, sha224_iv, rctx->ivsize);
break;
case SHA256_DIGEST_SIZE:
rctx->cmd |= HASH_CMD_SHA256 | HASH_CMD_SHA_SWAP;
rctx->flags |= SHA_FLAGS_SHA256;
rctx->digsize = SHA256_DIGEST_SIZE;
rctx->block_size = SHA256_BLOCK_SIZE;
rctx->ivsize = 32;
memcpy(rctx->digest, sha256_iv, rctx->ivsize);
break;
case SHA384_DIGEST_SIZE:
rctx->cmd |= HASH_CMD_SHA512_SER | HASH_CMD_SHA384 |
HASH_CMD_SHA_SWAP;
rctx->flags |= SHA_FLAGS_SHA384;
rctx->digsize = SHA384_DIGEST_SIZE;
rctx->block_size = SHA384_BLOCK_SIZE;
rctx->ivsize = 64;
memcpy(rctx->digest, sha384_iv, rctx->ivsize);
break;
case SHA512_DIGEST_SIZE:
rctx->cmd |= HASH_CMD_SHA512_SER | HASH_CMD_SHA512 |
HASH_CMD_SHA_SWAP;
rctx->flags |= SHA_FLAGS_SHA512;
rctx->digsize = SHA512_DIGEST_SIZE;
rctx->block_size = SHA512_BLOCK_SIZE;
rctx->ivsize = 64;
memcpy(rctx->digest, sha512_iv, rctx->ivsize);
break;
default:
dev_warn(tctx->hace_dev->dev, "digest size %d not support\n",
crypto_ahash_digestsize(tfm));
return -EINVAL;
}
rctx->total = 0;
rctx->digcnt[0] = 0;
rctx->digcnt[1] = 0;
return 0;
}
static int aspeed_sham_digest(struct ahash_request *req)
{
return aspeed_sham_init(req) ? : aspeed_sham_finup(req);
}
static int aspeed_sham_cra_init(struct crypto_ahash *tfm)
{
struct ahash_alg *alg = crypto_ahash_alg(tfm);
struct aspeed_sham_ctx *tctx = crypto_ahash_ctx(tfm);
struct aspeed_hace_alg *ast_alg;
ast_alg = container_of(alg, struct aspeed_hace_alg, alg.ahash.base);
tctx->hace_dev = ast_alg->hace_dev;
return 0;
}
static struct aspeed_hace_alg aspeed_ahash_algs[] = {
{
.alg.ahash.base = {
.init = aspeed_sham_init,
.update = aspeed_sham_update,
.finup = aspeed_sham_finup,
.digest = aspeed_sham_digest,
.export = aspeed_sham_export,
.import = aspeed_sham_import,
.init_tfm = aspeed_sham_cra_init,
.halg = {
.digestsize = SHA1_DIGEST_SIZE,
.statesize = sizeof(struct aspeed_sham_reqctx),
.base = {
.cra_name = "sha1",
.cra_driver_name = "aspeed-sha1",
.cra_priority = 300,
.cra_flags = CRYPTO_ALG_TYPE_AHASH |
CRYPTO_ALG_ASYNC |
CRYPTO_AHASH_ALG_BLOCK_ONLY |
CRYPTO_ALG_KERN_DRIVER_ONLY,
.cra_blocksize = SHA1_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct aspeed_sham_ctx),
.cra_reqsize = sizeof(struct aspeed_sham_reqctx),
.cra_alignmask = 0,
.cra_module = THIS_MODULE,
}
}
},
.alg.ahash.op = {
.do_one_request = aspeed_ahash_do_one,
},
},
{
.alg.ahash.base = {
.init = aspeed_sham_init,
.update = aspeed_sham_update,
.finup = aspeed_sham_finup,
.digest = aspeed_sham_digest,
.export = aspeed_sham_export,
.import = aspeed_sham_import,
.init_tfm = aspeed_sham_cra_init,
.halg = {
.digestsize = SHA256_DIGEST_SIZE,
.statesize = sizeof(struct aspeed_sham_reqctx),
.base = {
.cra_name = "sha256",
.cra_driver_name = "aspeed-sha256",
.cra_priority = 300,
.cra_flags = CRYPTO_ALG_TYPE_AHASH |
CRYPTO_ALG_ASYNC |
CRYPTO_AHASH_ALG_BLOCK_ONLY |
CRYPTO_ALG_KERN_DRIVER_ONLY,
.cra_blocksize = SHA256_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct aspeed_sham_ctx),
.cra_reqsize = sizeof(struct aspeed_sham_reqctx),
.cra_alignmask = 0,
.cra_module = THIS_MODULE,
}
}
},
.alg.ahash.op = {
.do_one_request = aspeed_ahash_do_one,
},
},
{
.alg.ahash.base = {
.init = aspeed_sham_init,
.update = aspeed_sham_update,
.finup = aspeed_sham_finup,
.digest = aspeed_sham_digest,
.export = aspeed_sham_export,
.import = aspeed_sham_import,
.init_tfm = aspeed_sham_cra_init,
.halg = {
.digestsize = SHA224_DIGEST_SIZE,
.statesize = sizeof(struct aspeed_sham_reqctx),
.base = {
.cra_name = "sha224",
.cra_driver_name = "aspeed-sha224",
.cra_priority = 300,
.cra_flags = CRYPTO_ALG_TYPE_AHASH |
CRYPTO_ALG_ASYNC |
CRYPTO_AHASH_ALG_BLOCK_ONLY |
CRYPTO_ALG_KERN_DRIVER_ONLY,
.cra_blocksize = SHA224_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct aspeed_sham_ctx),
.cra_reqsize = sizeof(struct aspeed_sham_reqctx),
.cra_alignmask = 0,
.cra_module = THIS_MODULE,
}
}
},
.alg.ahash.op = {
.do_one_request = aspeed_ahash_do_one,
},
},
};
static struct aspeed_hace_alg aspeed_ahash_algs_g6[] = {
{
.alg.ahash.base = {
.init = aspeed_sham_init,
.update = aspeed_sham_update,
.finup = aspeed_sham_finup,
.digest = aspeed_sham_digest,
.export = aspeed_sham_export,
.import = aspeed_sham_import,
.init_tfm = aspeed_sham_cra_init,
.halg = {
.digestsize = SHA384_DIGEST_SIZE,
.statesize = sizeof(struct aspeed_sham_reqctx),
.base = {
.cra_name = "sha384",
.cra_driver_name = "aspeed-sha384",
.cra_priority = 300,
.cra_flags = CRYPTO_ALG_TYPE_AHASH |
CRYPTO_ALG_ASYNC |
CRYPTO_AHASH_ALG_BLOCK_ONLY |
CRYPTO_ALG_KERN_DRIVER_ONLY,
.cra_blocksize = SHA384_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct aspeed_sham_ctx),
.cra_reqsize = sizeof(struct aspeed_sham_reqctx),
.cra_alignmask = 0,
.cra_module = THIS_MODULE,
}
}
},
.alg.ahash.op = {
.do_one_request = aspeed_ahash_do_one,
},
},
{
.alg.ahash.base = {
.init = aspeed_sham_init,
.update = aspeed_sham_update,
.finup = aspeed_sham_finup,
.digest = aspeed_sham_digest,
.export = aspeed_sham_export,
.import = aspeed_sham_import,
.init_tfm = aspeed_sham_cra_init,
.halg = {
.digestsize = SHA512_DIGEST_SIZE,
.statesize = sizeof(struct aspeed_sham_reqctx),
.base = {
.cra_name = "sha512",
.cra_driver_name = "aspeed-sha512",
.cra_priority = 300,
.cra_flags = CRYPTO_ALG_TYPE_AHASH |
CRYPTO_ALG_ASYNC |
CRYPTO_AHASH_ALG_BLOCK_ONLY |
CRYPTO_ALG_KERN_DRIVER_ONLY,
.cra_blocksize = SHA512_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct aspeed_sham_ctx),
.cra_reqsize = sizeof(struct aspeed_sham_reqctx),
.cra_alignmask = 0,
.cra_module = THIS_MODULE,
}
}
},
.alg.ahash.op = {
.do_one_request = aspeed_ahash_do_one,
},
},
};
void aspeed_unregister_hace_hash_algs(struct aspeed_hace_dev *hace_dev)
{
int i;
for (i = 0; i < ARRAY_SIZE(aspeed_ahash_algs); i++)
crypto_engine_unregister_ahash(&aspeed_ahash_algs[i].alg.ahash);
if (hace_dev->version != AST2600_VERSION)
return;
for (i = 0; i < ARRAY_SIZE(aspeed_ahash_algs_g6); i++)
crypto_engine_unregister_ahash(&aspeed_ahash_algs_g6[i].alg.ahash);
}
void aspeed_register_hace_hash_algs(struct aspeed_hace_dev *hace_dev)
{
int rc, i;
AHASH_DBG(hace_dev, "\n");
for (i = 0; i < ARRAY_SIZE(aspeed_ahash_algs); i++) {
aspeed_ahash_algs[i].hace_dev = hace_dev;
rc = crypto_engine_register_ahash(&aspeed_ahash_algs[i].alg.ahash);
if (rc) {
AHASH_DBG(hace_dev, "Failed to register %s\n",
aspeed_ahash_algs[i].alg.ahash.base.halg.base.cra_name);
}
}
if (hace_dev->version != AST2600_VERSION)
return;
for (i = 0; i < ARRAY_SIZE(aspeed_ahash_algs_g6); i++) {
aspeed_ahash_algs_g6[i].hace_dev = hace_dev;
rc = crypto_engine_register_ahash(&aspeed_ahash_algs_g6[i].alg.ahash);
if (rc) {
AHASH_DBG(hace_dev, "Failed to register %s\n",
aspeed_ahash_algs_g6[i].alg.ahash.base.halg.base.cra_name);
}
}
}
@@ -0,0 +1,280 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2021 Aspeed Technology Inc.
*/
#include "aspeed-hace.h"
#include <crypto/engine.h>
#include <linux/clk.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#ifdef CONFIG_CRYPTO_DEV_ASPEED_DEBUG
#define HACE_DBG(d, fmt, ...) \
dev_info((d)->dev, "%s() " fmt, __func__, ##__VA_ARGS__)
#else
#define HACE_DBG(d, fmt, ...) \
dev_dbg((d)->dev, "%s() " fmt, __func__, ##__VA_ARGS__)
#endif
/* HACE interrupt service routine */
static irqreturn_t aspeed_hace_irq(int irq, void *dev)
{
struct aspeed_hace_dev *hace_dev = (struct aspeed_hace_dev *)dev;
struct aspeed_engine_crypto *crypto_engine = &hace_dev->crypto_engine;
struct aspeed_engine_hash *hash_engine = &hace_dev->hash_engine;
u32 sts;
sts = ast_hace_read(hace_dev, ASPEED_HACE_STS);
ast_hace_write(hace_dev, sts, ASPEED_HACE_STS);
HACE_DBG(hace_dev, "irq status: 0x%x\n", sts);
if (sts & HACE_HASH_ISR) {
if (hash_engine->flags & CRYPTO_FLAGS_BUSY)
tasklet_schedule(&hash_engine->done_task);
else
dev_warn(hace_dev->dev, "HASH no active requests.\n");
}
if (sts & HACE_CRYPTO_ISR) {
if (crypto_engine->flags & CRYPTO_FLAGS_BUSY)
tasklet_schedule(&crypto_engine->done_task);
else
dev_warn(hace_dev->dev, "CRYPTO no active requests.\n");
}
return IRQ_HANDLED;
}
static void aspeed_hace_crypto_done_task(unsigned long data)
{
struct aspeed_hace_dev *hace_dev = (struct aspeed_hace_dev *)data;
struct aspeed_engine_crypto *crypto_engine = &hace_dev->crypto_engine;
crypto_engine->resume(hace_dev);
}
static void aspeed_hace_hash_done_task(unsigned long data)
{
struct aspeed_hace_dev *hace_dev = (struct aspeed_hace_dev *)data;
struct aspeed_engine_hash *hash_engine = &hace_dev->hash_engine;
hash_engine->resume(hace_dev);
}
static void aspeed_hace_register(struct aspeed_hace_dev *hace_dev)
{
#ifdef CONFIG_CRYPTO_DEV_ASPEED_HACE_HASH
aspeed_register_hace_hash_algs(hace_dev);
#endif
#ifdef CONFIG_CRYPTO_DEV_ASPEED_HACE_CRYPTO
aspeed_register_hace_crypto_algs(hace_dev);
#endif
}
static void aspeed_hace_unregister(struct aspeed_hace_dev *hace_dev)
{
#ifdef CONFIG_CRYPTO_DEV_ASPEED_HACE_HASH
aspeed_unregister_hace_hash_algs(hace_dev);
#endif
#ifdef CONFIG_CRYPTO_DEV_ASPEED_HACE_CRYPTO
aspeed_unregister_hace_crypto_algs(hace_dev);
#endif
}
static const struct of_device_id aspeed_hace_of_matches[] = {
{ .compatible = "aspeed,ast2500-hace", .data = (void *)5, },
{ .compatible = "aspeed,ast2600-hace", .data = (void *)6, },
{},
};
static int aspeed_hace_probe(struct platform_device *pdev)
{
struct aspeed_engine_crypto *crypto_engine;
struct aspeed_engine_hash *hash_engine;
struct aspeed_hace_dev *hace_dev;
int rc;
hace_dev = devm_kzalloc(&pdev->dev, sizeof(struct aspeed_hace_dev),
GFP_KERNEL);
if (!hace_dev)
return -ENOMEM;
hace_dev->version = (uintptr_t)device_get_match_data(&pdev->dev);
if (!hace_dev->version) {
dev_err(&pdev->dev, "Failed to match hace dev id\n");
return -EINVAL;
}
hace_dev->dev = &pdev->dev;
hash_engine = &hace_dev->hash_engine;
crypto_engine = &hace_dev->crypto_engine;
platform_set_drvdata(pdev, hace_dev);
hace_dev->regs = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
if (IS_ERR(hace_dev->regs))
return PTR_ERR(hace_dev->regs);
/* Get irq number and register it */
hace_dev->irq = platform_get_irq(pdev, 0);
if (hace_dev->irq < 0)
return -ENXIO;
rc = devm_request_irq(&pdev->dev, hace_dev->irq, aspeed_hace_irq, 0,
dev_name(&pdev->dev), hace_dev);
if (rc) {
dev_err(&pdev->dev, "Failed to request interrupt\n");
return rc;
}
/* Get clk and enable it */
hace_dev->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(hace_dev->clk)) {
dev_err(&pdev->dev, "Failed to get clk\n");
return -ENODEV;
}
rc = clk_prepare_enable(hace_dev->clk);
if (rc) {
dev_err(&pdev->dev, "Failed to enable clock 0x%x\n", rc);
return rc;
}
/* Initialize crypto hardware engine structure for hash */
hace_dev->crypt_engine_hash = crypto_engine_alloc_init(hace_dev->dev,
true);
if (!hace_dev->crypt_engine_hash) {
rc = -ENOMEM;
goto clk_exit;
}
rc = crypto_engine_start(hace_dev->crypt_engine_hash);
if (rc)
goto err_engine_hash_start;
tasklet_init(&hash_engine->done_task, aspeed_hace_hash_done_task,
(unsigned long)hace_dev);
/* Initialize crypto hardware engine structure for crypto */
hace_dev->crypt_engine_crypto = crypto_engine_alloc_init(hace_dev->dev,
true);
if (!hace_dev->crypt_engine_crypto) {
rc = -ENOMEM;
goto err_engine_hash_start;
}
rc = crypto_engine_start(hace_dev->crypt_engine_crypto);
if (rc)
goto err_engine_crypto_start;
tasklet_init(&crypto_engine->done_task, aspeed_hace_crypto_done_task,
(unsigned long)hace_dev);
/* Allocate DMA buffer for hash engine input used */
hash_engine->ahash_src_addr =
dmam_alloc_coherent(&pdev->dev,
ASPEED_HASH_SRC_DMA_BUF_LEN,
&hash_engine->ahash_src_dma_addr,
GFP_KERNEL);
if (!hash_engine->ahash_src_addr) {
dev_err(&pdev->dev, "Failed to allocate dma buffer\n");
rc = -ENOMEM;
goto err_engine_crypto_start;
}
/* Allocate DMA buffer for crypto engine context used */
crypto_engine->cipher_ctx =
dmam_alloc_coherent(&pdev->dev,
PAGE_SIZE,
&crypto_engine->cipher_ctx_dma,
GFP_KERNEL);
if (!crypto_engine->cipher_ctx) {
dev_err(&pdev->dev, "Failed to allocate cipher ctx dma\n");
rc = -ENOMEM;
goto err_engine_crypto_start;
}
/* Allocate DMA buffer for crypto engine input used */
crypto_engine->cipher_addr =
dmam_alloc_coherent(&pdev->dev,
ASPEED_CRYPTO_SRC_DMA_BUF_LEN,
&crypto_engine->cipher_dma_addr,
GFP_KERNEL);
if (!crypto_engine->cipher_addr) {
dev_err(&pdev->dev, "Failed to allocate cipher addr dma\n");
rc = -ENOMEM;
goto err_engine_crypto_start;
}
/* Allocate DMA buffer for crypto engine output used */
if (hace_dev->version == AST2600_VERSION) {
crypto_engine->dst_sg_addr =
dmam_alloc_coherent(&pdev->dev,
ASPEED_CRYPTO_DST_DMA_BUF_LEN,
&crypto_engine->dst_sg_dma_addr,
GFP_KERNEL);
if (!crypto_engine->dst_sg_addr) {
dev_err(&pdev->dev, "Failed to allocate dst_sg dma\n");
rc = -ENOMEM;
goto err_engine_crypto_start;
}
}
aspeed_hace_register(hace_dev);
dev_info(&pdev->dev, "Aspeed Crypto Accelerator successfully registered\n");
return 0;
err_engine_crypto_start:
crypto_engine_exit(hace_dev->crypt_engine_crypto);
err_engine_hash_start:
crypto_engine_exit(hace_dev->crypt_engine_hash);
clk_exit:
clk_disable_unprepare(hace_dev->clk);
return rc;
}
static void aspeed_hace_remove(struct platform_device *pdev)
{
struct aspeed_hace_dev *hace_dev = platform_get_drvdata(pdev);
struct aspeed_engine_crypto *crypto_engine = &hace_dev->crypto_engine;
struct aspeed_engine_hash *hash_engine = &hace_dev->hash_engine;
aspeed_hace_unregister(hace_dev);
crypto_engine_exit(hace_dev->crypt_engine_hash);
crypto_engine_exit(hace_dev->crypt_engine_crypto);
tasklet_kill(&hash_engine->done_task);
tasklet_kill(&crypto_engine->done_task);
clk_disable_unprepare(hace_dev->clk);
}
MODULE_DEVICE_TABLE(of, aspeed_hace_of_matches);
static struct platform_driver aspeed_hace_driver = {
.probe = aspeed_hace_probe,
.remove = aspeed_hace_remove,
.driver = {
.name = KBUILD_MODNAME,
.of_match_table = aspeed_hace_of_matches,
},
};
module_platform_driver(aspeed_hace_driver);
MODULE_AUTHOR("Neal Liu <neal_liu@aspeedtech.com>");
MODULE_DESCRIPTION("Aspeed HACE driver Crypto Accelerator");
MODULE_LICENSE("GPL");
@@ -0,0 +1,274 @@
/* SPDX-License-Identifier: GPL-2.0+ */
#ifndef __ASPEED_HACE_H__
#define __ASPEED_HACE_H__
#include <crypto/aes.h>
#include <crypto/engine.h>
#include <crypto/hash.h>
#include <crypto/sha2.h>
#include <linux/bits.h>
#include <linux/compiler_attributes.h>
#include <linux/interrupt.h>
#include <linux/types.h>
/*****************************
* *
* HACE register definitions *
* *
* ***************************/
#define ASPEED_HACE_SRC 0x00 /* Crypto Data Source Base Address Register */
#define ASPEED_HACE_DEST 0x04 /* Crypto Data Destination Base Address Register */
#define ASPEED_HACE_CONTEXT 0x08 /* Crypto Context Buffer Base Address Register */
#define ASPEED_HACE_DATA_LEN 0x0C /* Crypto Data Length Register */
#define ASPEED_HACE_CMD 0x10 /* Crypto Engine Command Register */
/* G5 */
#define ASPEED_HACE_TAG 0x18 /* HACE Tag Register */
/* G6 */
#define ASPEED_HACE_GCM_ADD_LEN 0x14 /* Crypto AES-GCM Additional Data Length Register */
#define ASPEED_HACE_GCM_TAG_BASE_ADDR 0x18 /* Crypto AES-GCM Tag Write Buff Base Address Reg */
#define ASPEED_HACE_STS 0x1C /* HACE Status Register */
#define ASPEED_HACE_HASH_SRC 0x20 /* Hash Data Source Base Address Register */
#define ASPEED_HACE_HASH_DIGEST_BUFF 0x24 /* Hash Digest Write Buffer Base Address Register */
#define ASPEED_HACE_HASH_KEY_BUFF 0x28 /* Hash HMAC Key Buffer Base Address Register */
#define ASPEED_HACE_HASH_DATA_LEN 0x2C /* Hash Data Length Register */
#define ASPEED_HACE_HASH_CMD 0x30 /* Hash Engine Command Register */
/* crypto cmd */
#define HACE_CMD_SINGLE_DES 0
#define HACE_CMD_TRIPLE_DES BIT(17)
#define HACE_CMD_AES_SELECT 0
#define HACE_CMD_DES_SELECT BIT(16)
#define HACE_CMD_ISR_EN BIT(12)
#define HACE_CMD_CONTEXT_SAVE_ENABLE (0)
#define HACE_CMD_CONTEXT_SAVE_DISABLE BIT(9)
#define HACE_CMD_AES (0)
#define HACE_CMD_DES (0)
#define HACE_CMD_RC4 BIT(8)
#define HACE_CMD_DECRYPT (0)
#define HACE_CMD_ENCRYPT BIT(7)
#define HACE_CMD_ECB (0x0 << 4)
#define HACE_CMD_CBC (0x1 << 4)
#define HACE_CMD_CFB (0x2 << 4)
#define HACE_CMD_OFB (0x3 << 4)
#define HACE_CMD_CTR (0x4 << 4)
#define HACE_CMD_OP_MODE_MASK (0x7 << 4)
#define HACE_CMD_AES128 (0x0 << 2)
#define HACE_CMD_AES192 (0x1 << 2)
#define HACE_CMD_AES256 (0x2 << 2)
#define HACE_CMD_OP_CASCADE (0x3)
#define HACE_CMD_OP_INDEPENDENT (0x1)
/* G5 */
#define HACE_CMD_RI_WO_DATA_ENABLE (0)
#define HACE_CMD_RI_WO_DATA_DISABLE BIT(11)
#define HACE_CMD_CONTEXT_LOAD_ENABLE (0)
#define HACE_CMD_CONTEXT_LOAD_DISABLE BIT(10)
/* G6 */
#define HACE_CMD_AES_KEY_FROM_OTP BIT(24)
#define HACE_CMD_GHASH_TAG_XOR_EN BIT(23)
#define HACE_CMD_GHASH_PAD_LEN_INV BIT(22)
#define HACE_CMD_GCM_TAG_ADDR_SEL BIT(21)
#define HACE_CMD_MBUS_REQ_SYNC_EN BIT(20)
#define HACE_CMD_DES_SG_CTRL BIT(19)
#define HACE_CMD_SRC_SG_CTRL BIT(18)
#define HACE_CMD_CTR_IV_AES_96 (0x1 << 14)
#define HACE_CMD_CTR_IV_DES_32 (0x1 << 14)
#define HACE_CMD_CTR_IV_AES_64 (0x2 << 14)
#define HACE_CMD_CTR_IV_AES_32 (0x3 << 14)
#define HACE_CMD_AES_KEY_HW_EXP BIT(13)
#define HACE_CMD_GCM (0x5 << 4)
/* interrupt status reg */
#define HACE_CRYPTO_ISR BIT(12)
#define HACE_HASH_ISR BIT(9)
#define HACE_HASH_BUSY BIT(0)
/* hash cmd reg */
#define HASH_CMD_MBUS_REQ_SYNC_EN BIT(20)
#define HASH_CMD_HASH_SRC_SG_CTRL BIT(18)
#define HASH_CMD_SHA512_224 (0x3 << 10)
#define HASH_CMD_SHA512_256 (0x2 << 10)
#define HASH_CMD_SHA384 (0x1 << 10)
#define HASH_CMD_SHA512 (0)
#define HASH_CMD_INT_ENABLE BIT(9)
#define HASH_CMD_HMAC (0x1 << 7)
#define HASH_CMD_ACC_MODE (0x2 << 7)
#define HASH_CMD_HMAC_KEY (0x3 << 7)
#define HASH_CMD_SHA1 (0x2 << 4)
#define HASH_CMD_SHA224 (0x4 << 4)
#define HASH_CMD_SHA256 (0x5 << 4)
#define HASH_CMD_SHA512_SER (0x6 << 4)
#define HASH_CMD_SHA_SWAP (0x2 << 2)
#define HASH_SG_LAST_LIST BIT(31)
#define CRYPTO_FLAGS_BUSY BIT(1)
#define SHA_OP_UPDATE 1
#define SHA_OP_FINAL 2
#define SHA_FLAGS_SHA1 BIT(0)
#define SHA_FLAGS_SHA224 BIT(1)
#define SHA_FLAGS_SHA256 BIT(2)
#define SHA_FLAGS_SHA384 BIT(3)
#define SHA_FLAGS_SHA512 BIT(4)
#define SHA_FLAGS_SHA512_224 BIT(5)
#define SHA_FLAGS_SHA512_256 BIT(6)
#define SHA_FLAGS_FINUP BIT(9)
#define SHA_FLAGS_MASK (0xff)
#define ASPEED_CRYPTO_SRC_DMA_BUF_LEN 0xa000
#define ASPEED_CRYPTO_DST_DMA_BUF_LEN 0xa000
#define ASPEED_CRYPTO_GCM_TAG_OFFSET 0x9ff0
#define ASPEED_HASH_SRC_DMA_BUF_LEN 0xa000
#define ASPEED_HASH_QUEUE_LENGTH 50
#define HACE_CMD_IV_REQUIRE (HACE_CMD_CBC | HACE_CMD_CFB | \
HACE_CMD_OFB | HACE_CMD_CTR)
struct aspeed_hace_dev;
struct scatterlist;
typedef int (*aspeed_hace_fn_t)(struct aspeed_hace_dev *);
struct aspeed_sg_list {
__le32 len;
__le32 phy_addr;
};
struct aspeed_engine_hash {
struct tasklet_struct done_task;
unsigned long flags;
struct ahash_request *req;
/* input buffer */
void *ahash_src_addr;
dma_addr_t ahash_src_dma_addr;
dma_addr_t src_dma;
dma_addr_t digest_dma;
size_t src_length;
/* callback func */
aspeed_hace_fn_t resume;
aspeed_hace_fn_t dma_prepare;
};
struct aspeed_sham_ctx {
struct aspeed_hace_dev *hace_dev;
};
struct aspeed_sham_reqctx {
/* DMA buffer written by hardware */
u8 digest[SHA512_DIGEST_SIZE] __aligned(64);
/* Software state sorted by size. */
u64 digcnt[2];
unsigned long flags; /* final update flag should no use*/
u32 cmd; /* trigger cmd */
/* walk state */
struct scatterlist *src_sg;
int src_nents;
unsigned int offset; /* offset in current sg */
unsigned int total; /* per update length */
size_t digsize;
size_t block_size;
size_t ivsize;
dma_addr_t buffer_dma_addr;
dma_addr_t digest_dma_addr;
/* This is DMA too but read-only for hardware. */
u8 buffer[SHA512_BLOCK_SIZE + 16];
};
struct aspeed_engine_crypto {
struct tasklet_struct done_task;
unsigned long flags;
struct skcipher_request *req;
/* context buffer */
void *cipher_ctx;
dma_addr_t cipher_ctx_dma;
/* input buffer, could be single/scatter-gather lists */
void *cipher_addr;
dma_addr_t cipher_dma_addr;
/* output buffer, only used in scatter-gather lists */
void *dst_sg_addr;
dma_addr_t dst_sg_dma_addr;
/* callback func */
aspeed_hace_fn_t resume;
};
struct aspeed_cipher_ctx {
struct aspeed_hace_dev *hace_dev;
int key_len;
u8 key[AES_MAX_KEYLENGTH];
/* callback func */
aspeed_hace_fn_t start;
struct crypto_skcipher *fallback_tfm;
};
struct aspeed_cipher_reqctx {
int enc_cmd;
int src_nents;
int dst_nents;
struct skcipher_request fallback_req; /* keep at the end */
};
struct aspeed_hace_dev {
void __iomem *regs;
struct device *dev;
int irq;
struct clk *clk;
unsigned long version;
struct crypto_engine *crypt_engine_hash;
struct crypto_engine *crypt_engine_crypto;
struct aspeed_engine_hash hash_engine;
struct aspeed_engine_crypto crypto_engine;
};
struct aspeed_hace_alg {
struct aspeed_hace_dev *hace_dev;
const char *alg_base;
union {
struct skcipher_engine_alg skcipher;
struct ahash_engine_alg ahash;
} alg;
};
enum aspeed_version {
AST2500_VERSION = 5,
AST2600_VERSION
};
#define ast_hace_write(hace, val, offset) \
writel((val), (hace)->regs + (offset))
#define ast_hace_read(hace, offset) \
readl((hace)->regs + (offset))
void aspeed_register_hace_hash_algs(struct aspeed_hace_dev *hace_dev);
void aspeed_unregister_hace_hash_algs(struct aspeed_hace_dev *hace_dev);
void aspeed_register_hace_crypto_algs(struct aspeed_hace_dev *hace_dev);
void aspeed_unregister_hace_crypto_algs(struct aspeed_hace_dev *hace_dev);
#endif