// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2021, Artinchip Technology Co., Ltd * * Wu Dehuang */ #include #include #include #include #include #include #include #include #include #include #include #include #include "crypto.h" #define CE_MAX_DIGEST_SIZE 64 #define CE_TOTAL_BITLEN_SIZE 32 #define MD5_CE_OUTPUT_LEN 16 #define SHA1_CE_OUTPUT_LEN 20 #define SHA224_CE_OUTPUT_LEN 32 #define SHA256_CE_OUTPUT_LEN 32 #define SHA384_CE_OUTPUT_LEN 64 #define SHA512_CE_OUTPUT_LEN 64 #define FLG_HMAC BIT(0) #define FLG_MD5 BIT(1) #define FLG_SHA1 BIT(2) #define FLG_SHA224 BIT(3) #define FLG_SHA256 BIT(4) #define FLG_SHA384 BIT(5) #define FLG_SHA512 BIT(6) #define FLG_FIRST BIT(7) #define FLG_UPDATE BIT(8) #define FLG_FINAL BIT(9) #define aligned_addr64bit(ptr) (((((dma_addr_t)(ptr)) + 7) >> 3) << 3) struct aic_hash_alg { struct ahash_alg alg; struct aic_crypto_dev *ce; }; struct aic_hash_tfm_ctx { struct crypto_engine_ctx enginectx; struct aic_crypto_dev *ce; bool hmac; void *remain_buf; u32 remain_len; u32 total_len; }; struct aic_hash_reqctx { struct task_desc *task; dma_addr_t phy_task; unsigned char *ivbuf; dma_addr_t phy_ivbuf; void *src_cpy_buf; dma_addr_t src_phy_buf; int src_cpy_buf_len; int tasklen; unsigned int digest_size; unsigned long flags; unsigned char digest[CE_MAX_DIGEST_SIZE]; bool src_map_sg; }; static inline bool is_hmac(unsigned long flg) { return (flg & FLG_HMAC); } static inline bool is_md5(unsigned long flg) { return (flg & FLG_MD5); } static inline bool is_sha1(unsigned long flg) { return (flg & FLG_SHA1); } static inline bool is_sha224(unsigned long flg) { return (flg & FLG_SHA224); } static inline bool is_sha256(unsigned long flg) { return (flg & FLG_SHA256); } static inline bool is_sha384(unsigned long flg) { return (flg & FLG_SHA384); } static inline bool is_sha512(unsigned long flg) { return (flg & FLG_SHA512); } static inline bool is_hmacsha1(unsigned long flg) { return (flg & FLG_HMAC && (flg) & FLG_SHA1); } static inline bool is_hmacsha256(unsigned long flg) { return (flg & FLG_HMAC && (flg) & FLG_SHA256); } static inline bool is_final(unsigned long flg) { return (flg & FLG_FINAL); } static void aic_hash_task_cfg(struct task_desc *task, struct aic_hash_reqctx *rctx, dma_addr_t din, dma_addr_t iv_addr, dma_addr_t dout, u32 dlen, u32 total, u32 last_flag) { if (last_flag) task->data.total_bytelen = total; task->data.last_flag = last_flag; task->data.in_addr = cpu_to_le32(din); task->data.in_len = dlen; task->data.out_addr = cpu_to_le32(dout); task->alg.hash.iv_mode = 1; task->alg.hash.iv_addr = cpu_to_le32(iv_addr); if (is_md5(rctx->flags)) { task->alg.hash.alg_tag = ALG_TAG_MD5; task->data.out_len = MD5_CE_OUTPUT_LEN; } else if (is_sha1(rctx->flags)) { task->alg.hash.alg_tag = ALG_TAG_SHA1; task->data.out_len = SHA1_CE_OUTPUT_LEN; } else if (is_sha224(rctx->flags)) { task->alg.hash.alg_tag = ALG_TAG_SHA224; task->data.out_len = SHA224_CE_OUTPUT_LEN; } else if (is_sha256(rctx->flags)) { task->alg.hash.alg_tag = ALG_TAG_SHA256; task->data.out_len = SHA256_CE_OUTPUT_LEN; } else if (is_sha384(rctx->flags)) { task->alg.hash.alg_tag = ALG_TAG_SHA384; task->data.out_len = SHA384_CE_OUTPUT_LEN; } else if (is_sha512(rctx->flags)) { task->alg.hash.alg_tag = ALG_TAG_SHA512; task->data.out_len = SHA512_CE_OUTPUT_LEN; } } /* * Try best to reuse sg buffer as hardware buffer, if the last sg buffer is not * block aligned, just backup in remain buffer, and will be process in the next * request. */ static int prepare_task_with_src_sg_buf(struct aic_crypto_dev *ce, struct ahash_request *req) { unsigned int bytelen, remain, todo, blocksz, cpycnt; dma_addr_t din, dout, next_addr; struct aic_hash_reqctx *rctx; struct aic_hash_tfm_ctx *ctx; struct scatterlist *sgs; struct crypto_ahash *tfm; struct task_desc *task; int ret, i, sg_cnt; u32 last_task, final; pr_debug("%s\n", __func__); rctx = ahash_request_ctx(req); tfm = crypto_ahash_reqtfm(req); ctx = crypto_ahash_ctx(tfm); blocksz = crypto_ahash_blocksize(tfm); sg_cnt = sg_nents_for_len(req->src, req->nbytes); if (is_final(rctx->flags)) todo = req->nbytes; else todo = rounddown(req->nbytes, blocksz); cpycnt = req->nbytes - todo; if (cpycnt) { /* Backup not block aligned tail data in remain buffer, if this * is not final step */ sg_copy_buffer(req->src, sg_cnt, ctx->remain_buf, cpycnt, todo, true); ctx->remain_len = cpycnt; } if (0 == todo) return 0; ret = dma_map_sg(ce->dev, req->src, sg_cnt, DMA_TO_DEVICE); if (ret != sg_cnt) { dev_err(ce->dev, "Failed to dma map src sg\n"); return -EFAULT; } rctx->src_map_sg = true; rctx->tasklen = sizeof(struct task_desc) * sg_cnt; rctx->task = dma_alloc_coherent(ce->dev, rctx->tasklen, &rctx->phy_task, GFP_KERNEL); if (!rctx->task) return -ENOMEM; memset(rctx->task, 0, rctx->tasklen); remain = todo; dout = rctx->phy_ivbuf; final = 0; for (i = 0, sgs = req->src; i < sg_cnt; i++, sgs = sg_next(sgs)) { task = &rctx->task[i]; next_addr = rctx->phy_task + ((i + 1) * sizeof(*task)); bytelen = min(remain, sg_dma_len(sgs)); remain -= bytelen; ctx->total_len += bytelen; last_task = (remain == 0); din = sg_dma_address(sgs); aic_hash_task_cfg(task, rctx, din, dout, dout, bytelen, ctx->total_len, final); if (last_task) task->next = 0; else task->next = cpu_to_le32(next_addr); } return 0; } /* * If source sg buffers cannot be used as hardware buffer, it is needed to * allocate a hardware buffer and copy input data to the new buffer */ static int prepare_task_with_src_cpy_buf(struct aic_crypto_dev *ce, struct ahash_request *req) { struct aic_hash_tfm_ctx *ctx; struct aic_hash_reqctx *rctx; struct crypto_ahash *tfm; unsigned int blocksz, total, todo; unsigned char *p; int pages; pr_debug("%s\n", __func__); tfm = crypto_ahash_reqtfm(req); ctx = crypto_ahash_ctx(tfm); blocksz = crypto_ahash_blocksize(tfm); rctx = ahash_request_ctx(req); total = ctx->remain_len + req->nbytes; todo = rounddown(total, blocksz); if (total < blocksz && is_final(rctx->flags) == false) { /* Not enough data to start CE, backup data in remain buffer */ p = ctx->remain_buf; p += ctx->remain_len; aic_crypto_sg_copy(p, req->src, req->nbytes, 0); ctx->remain_len = total; return 0; } if (total > 0) { /* Final step or there is enough data to be processed */ pages = get_order(total); rctx->src_cpy_buf = (void *)__get_free_pages(GFP_ATOMIC, pages); if (!rctx->src_cpy_buf) { dev_err(ce->dev, "Failed to allocate pages for src.\n"); return -ENOMEM; } rctx->src_cpy_buf_len = total; p = rctx->src_cpy_buf; if (ctx->remain_len) { memcpy(p, ctx->remain_buf, ctx->remain_len); p += ctx->remain_len; } if (is_final(rctx->flags)) { /* If this is a final step, process all data */ ctx->remain_len = 0; todo = total; } else { aic_crypto_sg_copy(p, req->src, req->nbytes, 0); /* If this is not final step, backup the tail data */ ctx->remain_len = total % blocksz; if (ctx->remain_len) { p = rctx->src_cpy_buf; p += todo; memcpy(ctx->remain_buf, p, ctx->remain_len); } } rctx->src_phy_buf = dma_map_single(ce->dev, rctx->src_cpy_buf, total, DMA_TO_DEVICE); if (dma_mapping_error(ce->dev, rctx->src_phy_buf)) { dev_err(ce->dev, "Failed to dma map src_phy_buf\n"); return -EFAULT; } } else { rctx->src_cpy_buf = NULL; rctx->src_phy_buf = 0; } rctx->tasklen = sizeof(struct task_desc); rctx->task = dma_alloc_coherent(ce->dev, rctx->tasklen, &rctx->phy_task, GFP_KERNEL); if (!rctx->task) return -ENOMEM; memset(rctx->task, 0, rctx->tasklen); ctx->total_len += todo; aic_hash_task_cfg(rctx->task, rctx, rctx->src_phy_buf, rctx->phy_ivbuf, rctx->phy_ivbuf, todo, ctx->total_len, is_final(rctx->flags)); if (is_final(rctx->flags)) { ctx->total_len = 0; } return 0; } static inline bool is_hash_block_aligned(unsigned int val, unsigned long flg) { if (is_md5(flg)) { if (val % MD5_HMAC_BLOCK_SIZE) return false; return true; } if (is_sha1(flg)) { if (val % SHA1_BLOCK_SIZE) return false; return true; } if (is_sha224(flg)) { if (val % SHA224_BLOCK_SIZE) return false; return true; } if (is_sha256(flg)) { if (val % SHA256_BLOCK_SIZE) return false; return true; } if (is_sha384(flg)) { if (val % SHA384_BLOCK_SIZE) return false; return true; } if (is_sha512(flg)) { if (val % SHA512_BLOCK_SIZE) return false; return true; } return false; } static inline bool can_use_src_sg_buf(struct ahash_request *req) { struct aic_hash_reqctx *rctx; unsigned int sg_cnt, dlen; struct scatterlist *sg; if (!req->src) return false; rctx = ahash_request_ctx(req); sg_cnt = sg_nents_for_len(req->src, req->nbytes); if (sg_cnt <= 0) return false; sg = req->src; if (sg_cnt == 1) { if (!is_word_aligned(sg->offset)) return false; /* Only one sg buffer, but the data length is not block aligned, * cannot be used directly */ dlen = sg->length - sg->offset; if (!is_hash_block_aligned(dlen, rctx->flags)) return false; return true; } /* If there are more than 1 sg buffers, dont' care the last one, try * the best to reuse sg buffer */ while (sg_cnt > 0) { if (!is_word_aligned(sg->offset)) return false; dlen = sg->length - sg->offset; if (sg_cnt > 0 && !is_hash_block_aligned(dlen, rctx->flags)) return false; sg = sg_next(sg); sg_cnt--; } return true; } static void get_output_digest(struct aic_crypto_dev *ce, struct ahash_request *req) { struct aic_hash_reqctx *rctx; rctx = ahash_request_ctx(req); if (rctx->phy_ivbuf) { dma_unmap_single(ce->dev, rctx->phy_ivbuf, CE_MAX_DIGEST_SIZE, DMA_BIDIRECTIONAL); memcpy(rctx->digest, rctx->ivbuf, CE_MAX_DIGEST_SIZE); rctx->phy_ivbuf = 0; } if (is_final(rctx->flags) && req->result) memcpy(req->result, rctx->digest, rctx->digest_size); } static int aic_hash_unprepare_req(struct crypto_engine *engine, void *areq) { struct aic_hash_reqctx *rctx; struct aic_hash_tfm_ctx *ctx; struct ahash_request *req; struct crypto_ahash *tfm; struct device *dev; int pages, sg_cnt; req = container_of(areq, struct ahash_request, base); rctx = ahash_request_ctx(req); tfm = crypto_ahash_reqtfm(req); ctx = crypto_ahash_ctx(tfm); dev = ctx->ce->dev; if (rctx->src_map_sg) { sg_cnt = sg_nents_for_len(req->src, req->nbytes); dma_unmap_sg(dev, req->src, sg_cnt, DMA_TO_DEVICE); rctx->src_map_sg = false; } if (rctx->task) { dma_free_coherent(dev, rctx->tasklen, rctx->task, rctx->phy_task); rctx->task = NULL; rctx->phy_task = 0; rctx->tasklen = 0; } pages = get_order(rctx->src_cpy_buf_len); if (rctx->src_cpy_buf) { if (rctx->src_phy_buf) { dma_unmap_single(dev, rctx->src_phy_buf, rctx->src_cpy_buf_len, DMA_TO_DEVICE); rctx->src_phy_buf = 0; } free_pages((unsigned long)rctx->src_cpy_buf, pages); rctx->src_cpy_buf = NULL; } if (rctx->phy_ivbuf) { dma_unmap_single(dev, rctx->phy_ivbuf, CE_MAX_DIGEST_SIZE, DMA_BIDIRECTIONAL); rctx->phy_ivbuf = 0; } if (rctx->ivbuf) { kfree_sensitive(rctx->ivbuf); rctx->ivbuf = NULL; } return 0; } static int aic_hash_prepare_req(struct crypto_engine *engine, void *areq) { struct aic_hash_tfm_ctx *ctx; struct aic_hash_reqctx *rctx; struct ahash_request *req; struct crypto_ahash *tfm; struct aic_crypto_dev *ce; unsigned int ds; int ret; req = container_of(areq, struct ahash_request, base); tfm = crypto_ahash_reqtfm(req); ctx = crypto_ahash_ctx(tfm); rctx = ahash_request_ctx(req); ce = ctx->ce; ds = CE_MAX_DIGEST_SIZE; rctx->ivbuf = kmemdup(rctx->digest, ds, GFP_KERNEL | GFP_DMA); if (!rctx->ivbuf) { ret = -ENOMEM; dev_err(ce->dev, "No mem for ivbuf\n"); goto err; } rctx->phy_ivbuf = dma_map_single(ce->dev, rctx->ivbuf, ds, DMA_BIDIRECTIONAL); if (dma_mapping_error(ce->dev, rctx->phy_ivbuf)) { dev_err(ce->dev, "Failed to dma map ivbuf\n"); ret = -EFAULT; goto err; } if (ctx->remain_len == 0 && can_use_src_sg_buf(req)) ret = prepare_task_with_src_sg_buf(ce, req); else ret = prepare_task_with_src_cpy_buf(ce, req); if (ret) { dev_err(ce->dev, "Failed to prepare task\n"); goto err; } return 0; err: aic_hash_unprepare_req(engine, areq); return ret; } static int aic_hash_do_one_req(struct crypto_engine *engine, void *areq) { struct ahash_request *req; struct crypto_ahash *tfm; struct aic_hash_tfm_ctx *ctx; struct aic_hash_reqctx *rctx; struct aic_crypto_dev *ce; unsigned int ret; u32 algo; req = container_of(areq, struct ahash_request, base); tfm = crypto_ahash_reqtfm(req); ctx = crypto_ahash_ctx(tfm); rctx = ahash_request_ctx(req); ce = ctx->ce; if (!ce) { pr_err("Device is null.\n"); return -ENODEV; } if (!rctx->task) { /* Not enough data to start CE, just finalize current request */ crypto_finalize_hash_request(ce->hash_accel.engine, req, 0); return 0; } if (!aic_crypto_is_ce_avail(ce)) { dev_err(ce->dev, "Crypto engine is busy.\n"); return -EBUSY; } if (!aic_crypto_is_accel_avail(ce, HASH_ALG_ACCELERATOR)) { dev_err(ce->dev, "hash accelerator fifo is full.\n"); return -EBUSY; } mutex_lock(&ce->hash_accel.req_lock); ret = kfifo_put(&ce->hash_accel.req_fifo, areq); mutex_unlock(&ce->hash_accel.req_lock); if (ret != 1) { dev_err(ce->dev, "req fifo is full.\n"); return ret; } if (DEBUG_CE) aic_crypto_dump_task(rctx->task, rctx->tasklen); algo = rctx->task->alg.alg_tag; aic_crypto_irq_enable(ce, HASH_ALG_ACCELERATOR); if (DEBUG_CE) aic_crypto_dump_reg(ce); aic_crypto_enqueue_task(ce, algo, rctx->phy_task); if (DEBUG_CE) aic_crypto_dump_reg(ce); return 0; } void aic_hash_handle_irq(struct aic_crypto_dev *ce) { struct aic_hash_reqctx *rctx; struct ahash_request *req; void *areq; int err, ret; mutex_lock(&ce->hash_accel.req_lock); if (kfifo_len(&ce->hash_accel.req_fifo) < 1) { dev_err(ce->dev, "There is no req in fifo.\n"); goto err; } /* * TODO: Maybe more than one task is finished, should check here */ /* kfifo_peek(&ce->hash_accel.req_fifo, &areq); */ ret = kfifo_get(&ce->hash_accel.req_fifo, &areq); if (ret != 1) { dev_err(ce->dev, "Get req from fifo failed.\n"); goto err; } mutex_unlock(&ce->hash_accel.req_lock); req = container_of(areq, struct ahash_request, base); rctx = ahash_request_ctx(req); err = (ce->err_status >> 8 * HASH_ALG_ACCELERATOR) & 0xFF; if (!err) get_output_digest(ce, req); crypto_finalize_hash_request(ce->hash_accel.engine, req, err); return; err: mutex_unlock(&ce->hash_accel.req_lock); } static inline struct ahash_alg *crypto_ahash_alg(struct crypto_ahash *hash) { return container_of(crypto_hash_alg_common(hash), struct ahash_alg, halg); } static int aic_hash_alg_init(struct crypto_tfm *tfm) { struct aic_hash_tfm_ctx *ctx = crypto_tfm_ctx(tfm); struct crypto_ahash *hash = __crypto_ahash_cast(tfm); struct ahash_alg *halg = crypto_ahash_alg(hash); struct aic_hash_alg *aicalg; pr_debug("%s\n", __func__); crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm), sizeof(struct aic_hash_reqctx)); memset(ctx, 0, sizeof(*ctx)); aicalg = container_of(halg, struct aic_hash_alg, alg); ctx->ce = aicalg->ce; #ifdef CONFIG_PM if (ctx->ce->task_count == 0) pm_runtime_get_sync(ctx->ce->dev); ctx->ce->task_count++; #endif ctx->enginectx.op.do_one_request = aic_hash_do_one_req; ctx->enginectx.op.prepare_request = aic_hash_prepare_req; ctx->enginectx.op.unprepare_request = aic_hash_unprepare_req; ctx->remain_buf = kmalloc(crypto_ahash_blocksize(hash), GFP_KERNEL); if (!ctx->remain_buf) return -ENOMEM; return 0; } static int aic_hash_hmac_alg_init(struct crypto_tfm *tfm) { struct aic_hash_tfm_ctx *ctx = crypto_tfm_ctx(tfm); int ret; ret = aic_hash_alg_init(tfm); ctx->hmac = true; return ret; } static void aic_hash_alg_exit(struct crypto_tfm *tfm) { struct aic_hash_tfm_ctx *ctx = crypto_tfm_ctx(tfm); #ifdef CONFIG_PM ctx->ce->task_count--; if (ctx->ce->task_count == 0) { pm_runtime_mark_last_busy(ctx->ce->dev); pm_runtime_put_autosuspend(ctx->ce->dev); } #endif pr_debug("%s\n", __func__); kfree(ctx->remain_buf); memset(ctx, 0, sizeof(*ctx)); } static u32 md5_iv[] = { MD5_H0, MD5_H1, MD5_H2, MD5_H3, }; static u32 md5_iv_len = 16; static u32 sha1_iv[] = { 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), }; static u32 sha1_iv_len = 20; static u32 sha224_iv[] = { 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 u32 sha224_iv_len = 32; static u32 sha256_iv[] = { 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 u32 sha256_iv_len = 32; static u64 sha384_iv[] = { 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 u64 sha384_iv_len = 64; static u64 sha512_iv[] = { 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 u64 sha512_iv_len = 64; static int aic_hash_init(struct ahash_request *req) { struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); struct aic_hash_tfm_ctx *ctx = crypto_ahash_ctx(tfm); struct aic_hash_reqctx *rctx = ahash_request_ctx(req); pr_debug("%s\n", __func__); memset(rctx, 0, sizeof(*rctx)); rctx->flags |= FLG_FIRST; if (ctx->hmac) rctx->flags |= FLG_HMAC; rctx->digest_size = crypto_ahash_digestsize(tfm); switch (rctx->digest_size) { case MD5_DIGEST_SIZE: rctx->flags |= FLG_MD5; memcpy(rctx->digest, md5_iv, md5_iv_len); break; case SHA1_DIGEST_SIZE: rctx->flags |= FLG_SHA1; memcpy(rctx->digest, sha1_iv, sha1_iv_len); break; case SHA224_DIGEST_SIZE: rctx->flags |= FLG_SHA224; memcpy(rctx->digest, sha224_iv, sha224_iv_len); break; case SHA256_DIGEST_SIZE: rctx->flags |= FLG_SHA256; memcpy(rctx->digest, sha256_iv, sha256_iv_len); break; case SHA384_DIGEST_SIZE: rctx->flags |= FLG_SHA384; memcpy(rctx->digest, sha384_iv, sha384_iv_len); break; case SHA512_DIGEST_SIZE: rctx->flags |= FLG_SHA512; memcpy(rctx->digest, sha512_iv, sha512_iv_len); break; default: return -EINVAL; } return 0; } static int aic_hash_export(struct ahash_request *req, void *out) { /* * TODO: Export rctx and hardware context to user */ return 0; } static int aic_hash_import(struct ahash_request *req, const void *in) { /* * TODO: Import rctx and hardware context from user */ return 0; } static int aic_hash_enqueue(struct ahash_request *req, unsigned long flg) { struct aic_hash_tfm_ctx *ctx; struct aic_hash_reqctx *rctx; struct crypto_ahash *tfm; struct crypto_engine *eng; tfm = crypto_ahash_reqtfm(req); ctx = crypto_ahash_ctx(tfm); rctx = ahash_request_ctx(req); rctx->flags |= flg; eng = ctx->ce->hash_accel.engine; return crypto_transfer_hash_request_to_engine(eng, req); } /* * one data request is incoming, need to transfer to queue */ static int aic_hash_update(struct ahash_request *req) { return aic_hash_enqueue(req, FLG_UPDATE); } /* * These is no data, just want to get the result. */ static int aic_hash_final(struct ahash_request *req) { pr_debug("%s\n", __func__); return aic_hash_enqueue(req, FLG_FINAL); } /* * update and final */ static int aic_hash_finup(struct ahash_request *req) { pr_debug("%s\n", __func__); return aic_hash_enqueue(req, FLG_UPDATE | FLG_FINAL); } /* * init, and finup */ static int aic_hash_digest(struct ahash_request *req) { return 0; } static int aic_hash_setkey(struct crypto_ahash *tfm, const u8 *key, unsigned int keylen) { pr_debug("%s\n", __func__); return 0; } static struct aic_hash_alg hash_algs[] = { { .alg = { .init = aic_hash_init, .update = aic_hash_update, .final = aic_hash_final, .finup = aic_hash_finup, .digest = aic_hash_digest, .export = aic_hash_export, .import = aic_hash_import, .halg = { .digestsize = MD5_DIGEST_SIZE, .statesize = sizeof(struct aic_hash_reqctx), .base = { .cra_name = "md5", .cra_driver_name = "md5-aic", .cra_priority = 400, .cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_TYPE_AHASH, .cra_blocksize = MD5_HMAC_BLOCK_SIZE, .cra_ctxsize = sizeof(struct aic_hash_tfm_ctx), .cra_alignmask = 3, .cra_init = aic_hash_alg_init, .cra_exit = aic_hash_alg_exit, .cra_module = THIS_MODULE, } } } }, { .alg = { .init = aic_hash_init, .update = aic_hash_update, .final = aic_hash_final, .finup = aic_hash_finup, .digest = aic_hash_digest, .export = aic_hash_export, .import = aic_hash_import, .halg = { .digestsize = SHA1_DIGEST_SIZE, .statesize = sizeof(struct aic_hash_reqctx), .base = { .cra_name = "sha1", .cra_driver_name = "sha1-aic", .cra_priority = 400, .cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_TYPE_AHASH, .cra_blocksize = SHA1_BLOCK_SIZE, .cra_ctxsize = sizeof(struct aic_hash_tfm_ctx), .cra_alignmask = 3, .cra_init = aic_hash_alg_init, .cra_exit = aic_hash_alg_exit, .cra_module = THIS_MODULE, } } } }, { .alg = { .init = aic_hash_init, .update = aic_hash_update, .final = aic_hash_final, .finup = aic_hash_finup, .digest = aic_hash_digest, .export = aic_hash_export, .import = aic_hash_import, .halg = { .digestsize = SHA224_DIGEST_SIZE, .statesize = sizeof(struct aic_hash_reqctx), .base = { .cra_name = "sha224", .cra_driver_name = "sha224-aic", .cra_priority = 400, .cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_TYPE_AHASH, .cra_blocksize = SHA224_BLOCK_SIZE, .cra_ctxsize = sizeof(struct aic_hash_tfm_ctx), .cra_alignmask = 3, .cra_init = aic_hash_alg_init, .cra_exit = aic_hash_alg_exit, .cra_module = THIS_MODULE, } } } }, { .alg = { .init = aic_hash_init, .update = aic_hash_update, .final = aic_hash_final, .finup = aic_hash_finup, .digest = aic_hash_digest, .export = aic_hash_export, .import = aic_hash_import, .halg = { .digestsize = SHA256_DIGEST_SIZE, .statesize = sizeof(struct aic_hash_reqctx), .base = { .cra_name = "sha256", .cra_driver_name = "sha256-aic", .cra_priority = 400, .cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_TYPE_AHASH, .cra_blocksize = SHA256_BLOCK_SIZE, .cra_ctxsize = sizeof(struct aic_hash_tfm_ctx), .cra_alignmask = 3, .cra_init = aic_hash_alg_init, .cra_exit = aic_hash_alg_exit, .cra_module = THIS_MODULE, } } } }, { .alg = { .init = aic_hash_init, .update = aic_hash_update, .final = aic_hash_final, .finup = aic_hash_finup, .digest = aic_hash_digest, .export = aic_hash_export, .import = aic_hash_import, .halg = { .digestsize = SHA384_DIGEST_SIZE, .statesize = sizeof(struct aic_hash_reqctx), .base = { .cra_name = "sha384", .cra_driver_name = "sha384-aic", .cra_priority = 400, .cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_TYPE_AHASH, .cra_blocksize = SHA384_BLOCK_SIZE, .cra_ctxsize = sizeof(struct aic_hash_tfm_ctx), .cra_alignmask = 3, .cra_init = aic_hash_alg_init, .cra_exit = aic_hash_alg_exit, .cra_module = THIS_MODULE, } } } }, { .alg = { .init = aic_hash_init, .update = aic_hash_update, .final = aic_hash_final, .finup = aic_hash_finup, .digest = aic_hash_digest, .export = aic_hash_export, .import = aic_hash_import, .halg = { .digestsize = SHA512_DIGEST_SIZE, .statesize = sizeof(struct aic_hash_reqctx), .base = { .cra_name = "sha512", .cra_driver_name = "sha512-aic", .cra_priority = 400, .cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_TYPE_AHASH, .cra_blocksize = SHA512_BLOCK_SIZE, .cra_ctxsize = sizeof(struct aic_hash_tfm_ctx), .cra_alignmask = 3, .cra_init = aic_hash_alg_init, .cra_exit = aic_hash_alg_exit, .cra_module = THIS_MODULE, } } } }, { .alg = { .init = aic_hash_init, .update = aic_hash_update, .final = aic_hash_final, .finup = aic_hash_finup, .digest = aic_hash_digest, .export = aic_hash_export, .import = aic_hash_import, .setkey = aic_hash_setkey, .halg = { .digestsize = SHA1_DIGEST_SIZE, .statesize = sizeof(struct aic_hash_reqctx), .base = { .cra_name = "hmac(sha1)", .cra_driver_name = "hmac-sha1-aic", .cra_priority = 400, .cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_TYPE_AHASH, .cra_blocksize = SHA1_BLOCK_SIZE, .cra_ctxsize = sizeof(struct aic_hash_tfm_ctx), .cra_alignmask = 3, .cra_init = aic_hash_hmac_alg_init, .cra_exit = aic_hash_alg_exit, .cra_module = THIS_MODULE, } } } }, { .alg = { .init = aic_hash_init, .update = aic_hash_update, .final = aic_hash_final, .finup = aic_hash_finup, .digest = aic_hash_digest, .export = aic_hash_export, .import = aic_hash_import, .setkey = aic_hash_setkey, .halg = { .digestsize = SHA256_DIGEST_SIZE, .statesize = sizeof(struct aic_hash_reqctx), .base = { .cra_name = "hmac(sha256)", .cra_driver_name = "hmac-sha256-aic", .cra_priority = 400, .cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_TYPE_AHASH, .cra_blocksize = SHA256_BLOCK_SIZE, .cra_ctxsize = sizeof(struct aic_hash_tfm_ctx), .cra_alignmask = 3, .cra_init = aic_hash_hmac_alg_init, .cra_exit = aic_hash_alg_exit, .cra_module = THIS_MODULE, } } } }, }; int aic_crypto_hash_accelerator_init(struct aic_crypto_dev *ce) { struct crypto_engine *eng; int ret, i; eng = crypto_engine_alloc_init_and_set(ce->dev, true, NULL, true, ACCEL_QUEUE_MAX_SIZE); if (!eng) { dev_err(ce->dev, "Failed to init crypto engine for hash\n"); ret = -ENOMEM; return ret; } ce->hash_accel.engine = eng; ret = crypto_engine_start(ce->hash_accel.engine); if (ret) { dev_err(ce->dev, "Failed to start crypto engine for hash\n"); goto err; } mutex_init(&ce->hash_accel.req_lock); INIT_KFIFO(ce->hash_accel.req_fifo); ret = kfifo_alloc(&ce->hash_accel.req_fifo, ACCEL_QUEUE_MAX_SIZE, GFP_KERNEL); if (ret) { dev_err(ce->dev, "Failed to alloc kfifo for hash\n"); goto err2; } for (i = 0; i < ARRAY_SIZE(hash_algs); i++) { hash_algs[i].ce = ce; ret = crypto_register_ahash(&hash_algs[i].alg); if (ret) { dev_err(ce->dev, "Failed to register hash algs\n"); goto err3; } } return 0; err3: for (--i; i >= 0; --i) { hash_algs[i].ce = NULL; crypto_unregister_ahash(&hash_algs[i].alg); } err2: kfifo_free(&ce->hash_accel.req_fifo); err: crypto_engine_exit(ce->hash_accel.engine); return ret; } int aic_crypto_hash_accelerator_exit(struct aic_crypto_dev *ce) { int i; for (i = 0; i < ARRAY_SIZE(hash_algs); i++) { hash_algs[i].ce = NULL; crypto_unregister_ahash(&hash_algs[i].alg); } kfifo_free(&ce->hash_accel.req_fifo); crypto_engine_exit(ce->hash_accel.engine); return 0; }