Path: blob/master/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-hash.c
26296 views
// SPDX-License-Identifier: GPL-2.01/*2* sun8i-ce-hash.c - hardware cryptographic offloader for3* Allwinner H3/A64/H5/H2+/H6/R40 SoC4*5* Copyright (C) 2015-2020 Corentin Labbe <[email protected]>6*7* This file add support for MD5 and SHA1/SHA224/SHA256/SHA384/SHA512.8*9* You could find the datasheet in Documentation/arch/arm/sunxi.rst10*/1112#include <crypto/internal/hash.h>13#include <crypto/md5.h>14#include <crypto/sha1.h>15#include <crypto/sha2.h>16#include <linux/bottom_half.h>17#include <linux/dma-mapping.h>18#include <linux/kernel.h>19#include <linux/pm_runtime.h>20#include <linux/scatterlist.h>21#include <linux/slab.h>22#include <linux/string.h>23#include "sun8i-ce.h"2425static void sun8i_ce_hash_stat_fb_inc(struct crypto_ahash *tfm)26{27if (IS_ENABLED(CONFIG_CRYPTO_DEV_SUN8I_CE_DEBUG)) {28struct sun8i_ce_alg_template *algt __maybe_unused;29struct ahash_alg *alg = crypto_ahash_alg(tfm);3031algt = container_of(alg, struct sun8i_ce_alg_template,32alg.hash.base);33algt->stat_fb++;34}35}3637int sun8i_ce_hash_init_tfm(struct crypto_ahash *tfm)38{39struct sun8i_ce_hash_tfm_ctx *op = crypto_ahash_ctx(tfm);40struct ahash_alg *alg = crypto_ahash_alg(tfm);41struct sun8i_ce_alg_template *algt;42int err;4344algt = container_of(alg, struct sun8i_ce_alg_template, alg.hash.base);45op->ce = algt->ce;4647/* FALLBACK */48op->fallback_tfm = crypto_alloc_ahash(crypto_ahash_alg_name(tfm), 0,49CRYPTO_ALG_NEED_FALLBACK);50if (IS_ERR(op->fallback_tfm)) {51dev_err(algt->ce->dev, "Fallback driver could no be loaded\n");52return PTR_ERR(op->fallback_tfm);53}5455crypto_ahash_set_statesize(tfm,56crypto_ahash_statesize(op->fallback_tfm));5758crypto_ahash_set_reqsize(tfm,59sizeof(struct sun8i_ce_hash_reqctx) +60crypto_ahash_reqsize(op->fallback_tfm));6162if (IS_ENABLED(CONFIG_CRYPTO_DEV_SUN8I_CE_DEBUG))63memcpy(algt->fbname,64crypto_ahash_driver_name(op->fallback_tfm),65CRYPTO_MAX_ALG_NAME);6667err = pm_runtime_resume_and_get(op->ce->dev);68if (err < 0)69goto error_pm;70return 0;71error_pm:72crypto_free_ahash(op->fallback_tfm);73return err;74}7576void sun8i_ce_hash_exit_tfm(struct crypto_ahash *tfm)77{78struct sun8i_ce_hash_tfm_ctx *tfmctx = crypto_ahash_ctx(tfm);7980crypto_free_ahash(tfmctx->fallback_tfm);81pm_runtime_put_sync_suspend(tfmctx->ce->dev);82}8384int sun8i_ce_hash_init(struct ahash_request *areq)85{86struct sun8i_ce_hash_reqctx *rctx = ahash_request_ctx(areq);87struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq);88struct sun8i_ce_hash_tfm_ctx *tfmctx = crypto_ahash_ctx(tfm);8990memset(rctx, 0, sizeof(struct sun8i_ce_hash_reqctx));9192ahash_request_set_tfm(&rctx->fallback_req, tfmctx->fallback_tfm);93ahash_request_set_callback(&rctx->fallback_req,94areq->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP,95areq->base.complete, areq->base.data);9697return crypto_ahash_init(&rctx->fallback_req);98}99100int sun8i_ce_hash_export(struct ahash_request *areq, void *out)101{102struct sun8i_ce_hash_reqctx *rctx = ahash_request_ctx(areq);103struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq);104struct sun8i_ce_hash_tfm_ctx *tfmctx = crypto_ahash_ctx(tfm);105106ahash_request_set_tfm(&rctx->fallback_req, tfmctx->fallback_tfm);107ahash_request_set_callback(&rctx->fallback_req,108areq->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP,109areq->base.complete, areq->base.data);110111return crypto_ahash_export(&rctx->fallback_req, out);112}113114int sun8i_ce_hash_import(struct ahash_request *areq, const void *in)115{116struct sun8i_ce_hash_reqctx *rctx = ahash_request_ctx(areq);117struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq);118struct sun8i_ce_hash_tfm_ctx *tfmctx = crypto_ahash_ctx(tfm);119120ahash_request_set_tfm(&rctx->fallback_req, tfmctx->fallback_tfm);121ahash_request_set_callback(&rctx->fallback_req,122areq->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP,123areq->base.complete, areq->base.data);124125return crypto_ahash_import(&rctx->fallback_req, in);126}127128int sun8i_ce_hash_final(struct ahash_request *areq)129{130struct sun8i_ce_hash_reqctx *rctx = ahash_request_ctx(areq);131struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq);132struct sun8i_ce_hash_tfm_ctx *tfmctx = crypto_ahash_ctx(tfm);133134sun8i_ce_hash_stat_fb_inc(tfm);135136ahash_request_set_tfm(&rctx->fallback_req, tfmctx->fallback_tfm);137ahash_request_set_callback(&rctx->fallback_req,138areq->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP,139areq->base.complete, areq->base.data);140ahash_request_set_crypt(&rctx->fallback_req, NULL, areq->result, 0);141142return crypto_ahash_final(&rctx->fallback_req);143}144145int sun8i_ce_hash_update(struct ahash_request *areq)146{147struct sun8i_ce_hash_reqctx *rctx = ahash_request_ctx(areq);148struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq);149struct sun8i_ce_hash_tfm_ctx *tfmctx = crypto_ahash_ctx(tfm);150151ahash_request_set_tfm(&rctx->fallback_req, tfmctx->fallback_tfm);152ahash_request_set_callback(&rctx->fallback_req,153areq->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP,154areq->base.complete, areq->base.data);155ahash_request_set_crypt(&rctx->fallback_req, areq->src, NULL, areq->nbytes);156157return crypto_ahash_update(&rctx->fallback_req);158}159160int sun8i_ce_hash_finup(struct ahash_request *areq)161{162struct sun8i_ce_hash_reqctx *rctx = ahash_request_ctx(areq);163struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq);164struct sun8i_ce_hash_tfm_ctx *tfmctx = crypto_ahash_ctx(tfm);165166sun8i_ce_hash_stat_fb_inc(tfm);167168ahash_request_set_tfm(&rctx->fallback_req, tfmctx->fallback_tfm);169ahash_request_set_callback(&rctx->fallback_req,170areq->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP,171areq->base.complete, areq->base.data);172ahash_request_set_crypt(&rctx->fallback_req, areq->src, areq->result,173areq->nbytes);174175return crypto_ahash_finup(&rctx->fallback_req);176}177178static int sun8i_ce_hash_digest_fb(struct ahash_request *areq)179{180struct sun8i_ce_hash_reqctx *rctx = ahash_request_ctx(areq);181struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq);182struct sun8i_ce_hash_tfm_ctx *tfmctx = crypto_ahash_ctx(tfm);183184sun8i_ce_hash_stat_fb_inc(tfm);185186ahash_request_set_tfm(&rctx->fallback_req, tfmctx->fallback_tfm);187ahash_request_set_callback(&rctx->fallback_req,188areq->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP,189areq->base.complete, areq->base.data);190ahash_request_set_crypt(&rctx->fallback_req, areq->src, areq->result,191areq->nbytes);192193return crypto_ahash_digest(&rctx->fallback_req);194}195196static bool sun8i_ce_hash_need_fallback(struct ahash_request *areq)197{198struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq);199struct ahash_alg *alg = __crypto_ahash_alg(tfm->base.__crt_alg);200struct sun8i_ce_alg_template *algt;201struct scatterlist *sg;202203algt = container_of(alg, struct sun8i_ce_alg_template, alg.hash.base);204205if (areq->nbytes == 0) {206if (IS_ENABLED(CONFIG_CRYPTO_DEV_SUN8I_CE_DEBUG))207algt->stat_fb_len0++;208209return true;210}211/* we need to reserve one SG for padding one */212if (sg_nents_for_len(areq->src, areq->nbytes) > MAX_SG - 1) {213if (IS_ENABLED(CONFIG_CRYPTO_DEV_SUN8I_CE_DEBUG))214algt->stat_fb_maxsg++;215216return true;217}218sg = areq->src;219while (sg) {220if (sg->length % 4) {221if (IS_ENABLED(CONFIG_CRYPTO_DEV_SUN8I_CE_DEBUG))222algt->stat_fb_srclen++;223224return true;225}226if (!IS_ALIGNED(sg->offset, sizeof(u32))) {227if (IS_ENABLED(CONFIG_CRYPTO_DEV_SUN8I_CE_DEBUG))228algt->stat_fb_srcali++;229230return true;231}232sg = sg_next(sg);233}234return false;235}236237int sun8i_ce_hash_digest(struct ahash_request *areq)238{239struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq);240struct ahash_alg *alg = __crypto_ahash_alg(tfm->base.__crt_alg);241struct sun8i_ce_hash_reqctx *rctx = ahash_request_ctx(areq);242struct sun8i_ce_alg_template *algt;243struct sun8i_ce_dev *ce;244struct crypto_engine *engine;245int e;246247if (sun8i_ce_hash_need_fallback(areq))248return sun8i_ce_hash_digest_fb(areq);249250algt = container_of(alg, struct sun8i_ce_alg_template, alg.hash.base);251ce = algt->ce;252253e = sun8i_ce_get_engine_number(ce);254rctx->flow = e;255engine = ce->chanlist[e].engine;256257return crypto_transfer_hash_request_to_engine(engine, areq);258}259260static u64 hash_pad(__le32 *buf, unsigned int bufsize, u64 padi, u64 byte_count, bool le, int bs)261{262u64 fill, min_fill, j, k;263__be64 *bebits;264__le64 *lebits;265266j = padi;267buf[j++] = cpu_to_le32(0x80);268269if (bs == 64) {270fill = 64 - (byte_count % 64);271min_fill = 2 * sizeof(u32) + sizeof(u32);272} else {273fill = 128 - (byte_count % 128);274min_fill = 4 * sizeof(u32) + sizeof(u32);275}276277if (fill < min_fill)278fill += bs;279280k = j;281j += (fill - min_fill) / sizeof(u32);282if (j * 4 > bufsize) {283pr_err("%s OVERFLOW %llu\n", __func__, j);284return 0;285}286for (; k < j; k++)287buf[k] = 0;288289if (le) {290/* MD5 */291lebits = (__le64 *)&buf[j];292*lebits = cpu_to_le64(byte_count << 3);293j += 2;294} else {295if (bs == 64) {296/* sha1 sha224 sha256 */297bebits = (__be64 *)&buf[j];298*bebits = cpu_to_be64(byte_count << 3);299j += 2;300} else {301/* sha384 sha512*/302bebits = (__be64 *)&buf[j];303*bebits = cpu_to_be64(byte_count >> 61);304j += 2;305bebits = (__be64 *)&buf[j];306*bebits = cpu_to_be64(byte_count << 3);307j += 2;308}309}310if (j * 4 > bufsize) {311pr_err("%s OVERFLOW %llu\n", __func__, j);312return 0;313}314315return j;316}317318int sun8i_ce_hash_run(struct crypto_engine *engine, void *breq)319{320struct ahash_request *areq = container_of(breq, struct ahash_request, base);321struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq);322struct ahash_alg *alg = __crypto_ahash_alg(tfm->base.__crt_alg);323struct sun8i_ce_hash_reqctx *rctx = ahash_request_ctx(areq);324struct sun8i_ce_alg_template *algt;325struct sun8i_ce_dev *ce;326struct sun8i_ce_flow *chan;327struct ce_task *cet;328struct scatterlist *sg;329int nr_sgs, flow, err;330unsigned int len;331u32 common;332u64 byte_count;333__le32 *bf;334void *buf, *result;335int j, i, todo;336u64 bs;337int digestsize;338dma_addr_t addr_res, addr_pad;339int ns = sg_nents_for_len(areq->src, areq->nbytes);340341algt = container_of(alg, struct sun8i_ce_alg_template, alg.hash.base);342ce = algt->ce;343344bs = crypto_ahash_blocksize(tfm);345digestsize = crypto_ahash_digestsize(tfm);346if (digestsize == SHA224_DIGEST_SIZE)347digestsize = SHA256_DIGEST_SIZE;348if (digestsize == SHA384_DIGEST_SIZE)349digestsize = SHA512_DIGEST_SIZE;350351/* the padding could be up to two block. */352buf = kcalloc(2, bs, GFP_KERNEL | GFP_DMA);353if (!buf) {354err = -ENOMEM;355goto err_out;356}357bf = (__le32 *)buf;358359result = kzalloc(digestsize, GFP_KERNEL | GFP_DMA);360if (!result) {361err = -ENOMEM;362goto err_free_buf;363}364365flow = rctx->flow;366chan = &ce->chanlist[flow];367368if (IS_ENABLED(CONFIG_CRYPTO_DEV_SUN8I_CE_DEBUG))369algt->stat_req++;370371dev_dbg(ce->dev, "%s %s len=%d\n", __func__, crypto_tfm_alg_name(areq->base.tfm), areq->nbytes);372373cet = chan->tl;374memset(cet, 0, sizeof(struct ce_task));375376cet->t_id = cpu_to_le32(flow);377common = ce->variant->alg_hash[algt->ce_algo_id];378common |= CE_COMM_INT;379cet->t_common_ctl = cpu_to_le32(common);380381cet->t_sym_ctl = 0;382cet->t_asym_ctl = 0;383384nr_sgs = dma_map_sg(ce->dev, areq->src, ns, DMA_TO_DEVICE);385if (nr_sgs <= 0 || nr_sgs > MAX_SG) {386dev_err(ce->dev, "Invalid sg number %d\n", nr_sgs);387err = -EINVAL;388goto err_free_result;389}390391len = areq->nbytes;392for_each_sg(areq->src, sg, nr_sgs, i) {393cet->t_src[i].addr = desc_addr_val_le32(ce, sg_dma_address(sg));394todo = min(len, sg_dma_len(sg));395cet->t_src[i].len = cpu_to_le32(todo / 4);396len -= todo;397}398if (len > 0) {399dev_err(ce->dev, "remaining len %d\n", len);400err = -EINVAL;401goto err_unmap_src;402}403addr_res = dma_map_single(ce->dev, result, digestsize, DMA_FROM_DEVICE);404cet->t_dst[0].addr = desc_addr_val_le32(ce, addr_res);405cet->t_dst[0].len = cpu_to_le32(digestsize / 4);406if (dma_mapping_error(ce->dev, addr_res)) {407dev_err(ce->dev, "DMA map dest\n");408err = -EINVAL;409goto err_unmap_src;410}411412byte_count = areq->nbytes;413j = 0;414415switch (algt->ce_algo_id) {416case CE_ID_HASH_MD5:417j = hash_pad(bf, 2 * bs, j, byte_count, true, bs);418break;419case CE_ID_HASH_SHA1:420case CE_ID_HASH_SHA224:421case CE_ID_HASH_SHA256:422j = hash_pad(bf, 2 * bs, j, byte_count, false, bs);423break;424case CE_ID_HASH_SHA384:425case CE_ID_HASH_SHA512:426j = hash_pad(bf, 2 * bs, j, byte_count, false, bs);427break;428}429if (!j) {430err = -EINVAL;431goto err_unmap_result;432}433434addr_pad = dma_map_single(ce->dev, buf, j * 4, DMA_TO_DEVICE);435cet->t_src[i].addr = desc_addr_val_le32(ce, addr_pad);436cet->t_src[i].len = cpu_to_le32(j);437if (dma_mapping_error(ce->dev, addr_pad)) {438dev_err(ce->dev, "DMA error on padding SG\n");439err = -EINVAL;440goto err_unmap_result;441}442443if (ce->variant->hash_t_dlen_in_bits)444cet->t_dlen = cpu_to_le32((areq->nbytes + j * 4) * 8);445else446cet->t_dlen = cpu_to_le32(areq->nbytes / 4 + j);447448chan->timeout = areq->nbytes;449450err = sun8i_ce_run_task(ce, flow, crypto_ahash_alg_name(tfm));451452dma_unmap_single(ce->dev, addr_pad, j * 4, DMA_TO_DEVICE);453454err_unmap_result:455dma_unmap_single(ce->dev, addr_res, digestsize, DMA_FROM_DEVICE);456if (!err)457memcpy(areq->result, result, crypto_ahash_digestsize(tfm));458459err_unmap_src:460dma_unmap_sg(ce->dev, areq->src, ns, DMA_TO_DEVICE);461462err_free_result:463kfree(result);464465err_free_buf:466kfree(buf);467468err_out:469local_bh_disable();470crypto_finalize_hash_request(engine, breq, err);471local_bh_enable();472473return 0;474}475476477