Path: blob/master/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-cipher.c
29537 views
// SPDX-License-Identifier: GPL-2.01/*2* sun8i-ce-cipher.c - hardware cryptographic offloader for3* Allwinner H3/A64/H5/H2+/H6/R40 SoC4*5* Copyright (C) 2016-2019 Corentin LABBE <[email protected]>6*7* This file add support for AES cipher with 128,192,256 bits keysize in8* CBC and ECB mode.9*10* You could find a link for the datasheet in Documentation/arch/arm/sunxi.rst11*/1213#include <linux/bottom_half.h>14#include <linux/crypto.h>15#include <linux/dma-mapping.h>16#include <linux/io.h>17#include <linux/pm_runtime.h>18#include <crypto/scatterwalk.h>19#include <crypto/internal/des.h>20#include <crypto/internal/skcipher.h>21#include "sun8i-ce.h"2223static int sun8i_ce_cipher_need_fallback(struct skcipher_request *areq)24{25struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(areq);26struct scatterlist *sg;27struct skcipher_alg *alg = crypto_skcipher_alg(tfm);28struct sun8i_ce_alg_template *algt;29unsigned int todo, len;3031algt = container_of(alg, struct sun8i_ce_alg_template, alg.skcipher.base);3233if (sg_nents_for_len(areq->src, areq->cryptlen) > MAX_SG ||34sg_nents_for_len(areq->dst, areq->cryptlen) > MAX_SG) {35if (IS_ENABLED(CONFIG_CRYPTO_DEV_SUN8I_CE_DEBUG))36algt->stat_fb_maxsg++;3738return true;39}4041if (areq->cryptlen < crypto_skcipher_ivsize(tfm)) {42if (IS_ENABLED(CONFIG_CRYPTO_DEV_SUN8I_CE_DEBUG))43algt->stat_fb_leniv++;4445return true;46}4748if (areq->cryptlen == 0) {49if (IS_ENABLED(CONFIG_CRYPTO_DEV_SUN8I_CE_DEBUG))50algt->stat_fb_len0++;5152return true;53}5455if (areq->cryptlen % 16) {56if (IS_ENABLED(CONFIG_CRYPTO_DEV_SUN8I_CE_DEBUG))57algt->stat_fb_mod16++;5859return true;60}6162len = areq->cryptlen;63sg = areq->src;64while (sg) {65if (!IS_ALIGNED(sg->offset, sizeof(u32))) {66if (IS_ENABLED(CONFIG_CRYPTO_DEV_SUN8I_CE_DEBUG))67algt->stat_fb_srcali++;6869return true;70}71todo = min(len, sg->length);72if (todo % 4) {73if (IS_ENABLED(CONFIG_CRYPTO_DEV_SUN8I_CE_DEBUG))74algt->stat_fb_srclen++;7576return true;77}78len -= todo;79sg = sg_next(sg);80}8182len = areq->cryptlen;83sg = areq->dst;84while (sg) {85if (!IS_ALIGNED(sg->offset, sizeof(u32))) {86if (IS_ENABLED(CONFIG_CRYPTO_DEV_SUN8I_CE_DEBUG))87algt->stat_fb_dstali++;8889return true;90}91todo = min(len, sg->length);92if (todo % 4) {93if (IS_ENABLED(CONFIG_CRYPTO_DEV_SUN8I_CE_DEBUG))94algt->stat_fb_dstlen++;9596return true;97}98len -= todo;99sg = sg_next(sg);100}101return false;102}103104static int sun8i_ce_cipher_fallback(struct skcipher_request *areq)105{106struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(areq);107struct sun8i_cipher_tfm_ctx *op = crypto_skcipher_ctx(tfm);108struct sun8i_cipher_req_ctx *rctx = skcipher_request_ctx(areq);109int err;110111if (IS_ENABLED(CONFIG_CRYPTO_DEV_SUN8I_CE_DEBUG)) {112struct skcipher_alg *alg = crypto_skcipher_alg(tfm);113struct sun8i_ce_alg_template *algt;114115algt = container_of(alg, struct sun8i_ce_alg_template,116alg.skcipher.base);117118algt->stat_fb++;119}120121skcipher_request_set_tfm(&rctx->fallback_req, op->fallback_tfm);122skcipher_request_set_callback(&rctx->fallback_req, areq->base.flags,123areq->base.complete, areq->base.data);124skcipher_request_set_crypt(&rctx->fallback_req, areq->src, areq->dst,125areq->cryptlen, areq->iv);126if (rctx->op_dir & CE_DECRYPTION)127err = crypto_skcipher_decrypt(&rctx->fallback_req);128else129err = crypto_skcipher_encrypt(&rctx->fallback_req);130return err;131}132133static int sun8i_ce_cipher_prepare(struct skcipher_request *areq,134struct ce_task *cet)135{136struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(areq);137struct sun8i_cipher_tfm_ctx *op = crypto_skcipher_ctx(tfm);138struct sun8i_ce_dev *ce = op->ce;139struct sun8i_cipher_req_ctx *rctx = skcipher_request_ctx(areq);140struct skcipher_alg *alg = crypto_skcipher_alg(tfm);141struct sun8i_ce_alg_template *algt;142struct scatterlist *sg;143unsigned int todo, len, offset, ivsize;144u32 common, sym;145int i;146int nr_sgs = 0;147int nr_sgd = 0;148int err = 0;149int ns = sg_nents_for_len(areq->src, areq->cryptlen);150int nd = sg_nents_for_len(areq->dst, areq->cryptlen);151152algt = container_of(alg, struct sun8i_ce_alg_template, alg.skcipher.base);153154dev_dbg(ce->dev, "%s %s %u %x IV(%p %u) key=%u\n", __func__,155crypto_tfm_alg_name(areq->base.tfm),156areq->cryptlen,157rctx->op_dir, areq->iv, crypto_skcipher_ivsize(tfm),158op->keylen);159160if (IS_ENABLED(CONFIG_CRYPTO_DEV_SUN8I_CE_DEBUG))161algt->stat_req++;162163memset(cet, 0, sizeof(struct ce_task));164165cet->t_id = cpu_to_le32(rctx->flow);166common = ce->variant->alg_cipher[algt->ce_algo_id];167common |= rctx->op_dir | CE_COMM_INT;168cet->t_common_ctl = cpu_to_le32(common);169/* CTS and recent CE (H6) need length in bytes, in word otherwise */170if (ce->variant->cipher_t_dlen_in_bytes)171cet->t_dlen = cpu_to_le32(areq->cryptlen);172else173cet->t_dlen = cpu_to_le32(areq->cryptlen / 4);174175sym = ce->variant->op_mode[algt->ce_blockmode];176len = op->keylen;177switch (len) {178case 128 / 8:179sym |= CE_AES_128BITS;180break;181case 192 / 8:182sym |= CE_AES_192BITS;183break;184case 256 / 8:185sym |= CE_AES_256BITS;186break;187}188189cet->t_sym_ctl = cpu_to_le32(sym);190cet->t_asym_ctl = 0;191192rctx->addr_key = dma_map_single(ce->dev, op->key, op->keylen, DMA_TO_DEVICE);193if (dma_mapping_error(ce->dev, rctx->addr_key)) {194dev_err(ce->dev, "Cannot DMA MAP KEY\n");195err = -EFAULT;196goto theend;197}198cet->t_key = desc_addr_val_le32(ce, rctx->addr_key);199200ivsize = crypto_skcipher_ivsize(tfm);201if (areq->iv && ivsize > 0) {202if (rctx->op_dir & CE_DECRYPTION) {203offset = areq->cryptlen - ivsize;204scatterwalk_map_and_copy(rctx->backup_iv, areq->src,205offset, ivsize, 0);206}207memcpy(rctx->bounce_iv, areq->iv, ivsize);208rctx->addr_iv = dma_map_single(ce->dev, rctx->bounce_iv, ivsize,209DMA_TO_DEVICE);210if (dma_mapping_error(ce->dev, rctx->addr_iv)) {211dev_err(ce->dev, "Cannot DMA MAP IV\n");212err = -ENOMEM;213goto theend_iv;214}215cet->t_iv = desc_addr_val_le32(ce, rctx->addr_iv);216}217218if (areq->src == areq->dst) {219nr_sgs = dma_map_sg(ce->dev, areq->src, ns, DMA_BIDIRECTIONAL);220if (nr_sgs <= 0 || nr_sgs > MAX_SG) {221dev_err(ce->dev, "Invalid sg number %d\n", nr_sgs);222err = -EINVAL;223goto theend_iv;224}225nr_sgd = nr_sgs;226} else {227nr_sgs = dma_map_sg(ce->dev, areq->src, ns, DMA_TO_DEVICE);228if (nr_sgs <= 0 || nr_sgs > MAX_SG) {229dev_err(ce->dev, "Invalid sg number %d\n", nr_sgs);230err = -EINVAL;231goto theend_iv;232}233nr_sgd = dma_map_sg(ce->dev, areq->dst, nd, DMA_FROM_DEVICE);234if (nr_sgd <= 0 || nr_sgd > MAX_SG) {235dev_err(ce->dev, "Invalid sg number %d\n", nr_sgd);236err = -EINVAL;237goto theend_sgs;238}239}240241len = areq->cryptlen;242for_each_sg(areq->src, sg, nr_sgs, i) {243cet->t_src[i].addr = desc_addr_val_le32(ce, sg_dma_address(sg));244todo = min(len, sg_dma_len(sg));245cet->t_src[i].len = cpu_to_le32(todo / 4);246dev_dbg(ce->dev, "%s total=%u SG(%d %u off=%d) todo=%u\n", __func__,247areq->cryptlen, i, cet->t_src[i].len, sg->offset, todo);248len -= todo;249}250if (len > 0) {251dev_err(ce->dev, "remaining len %d\n", len);252err = -EINVAL;253goto theend_sgs;254}255256len = areq->cryptlen;257for_each_sg(areq->dst, sg, nr_sgd, i) {258cet->t_dst[i].addr = desc_addr_val_le32(ce, sg_dma_address(sg));259todo = min(len, sg_dma_len(sg));260cet->t_dst[i].len = cpu_to_le32(todo / 4);261dev_dbg(ce->dev, "%s total=%u SG(%d %u off=%d) todo=%u\n", __func__,262areq->cryptlen, i, cet->t_dst[i].len, sg->offset, todo);263len -= todo;264}265if (len > 0) {266dev_err(ce->dev, "remaining len %d\n", len);267err = -EINVAL;268goto theend_sgs;269}270271rctx->nr_sgs = ns;272rctx->nr_sgd = nd;273return 0;274275theend_sgs:276if (areq->src == areq->dst) {277dma_unmap_sg(ce->dev, areq->src, ns, DMA_BIDIRECTIONAL);278} else {279if (nr_sgs > 0)280dma_unmap_sg(ce->dev, areq->src, ns, DMA_TO_DEVICE);281282if (nr_sgd > 0)283dma_unmap_sg(ce->dev, areq->dst, nd, DMA_FROM_DEVICE);284}285286theend_iv:287if (areq->iv && ivsize > 0) {288if (!dma_mapping_error(ce->dev, rctx->addr_iv))289dma_unmap_single(ce->dev, rctx->addr_iv, ivsize,290DMA_TO_DEVICE);291292offset = areq->cryptlen - ivsize;293if (rctx->op_dir & CE_DECRYPTION) {294memcpy(areq->iv, rctx->backup_iv, ivsize);295memzero_explicit(rctx->backup_iv, ivsize);296} else {297scatterwalk_map_and_copy(areq->iv, areq->dst, offset,298ivsize, 0);299}300memzero_explicit(rctx->bounce_iv, ivsize);301}302303dma_unmap_single(ce->dev, rctx->addr_key, op->keylen, DMA_TO_DEVICE);304305theend:306return err;307}308309static void sun8i_ce_cipher_unprepare(struct skcipher_request *areq,310struct ce_task *cet)311{312struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(areq);313struct sun8i_cipher_tfm_ctx *op = crypto_skcipher_ctx(tfm);314struct sun8i_ce_dev *ce = op->ce;315struct sun8i_cipher_req_ctx *rctx = skcipher_request_ctx(areq);316unsigned int ivsize, offset;317int nr_sgs = rctx->nr_sgs;318int nr_sgd = rctx->nr_sgd;319320ivsize = crypto_skcipher_ivsize(tfm);321322if (areq->src == areq->dst) {323dma_unmap_sg(ce->dev, areq->src, nr_sgs, DMA_BIDIRECTIONAL);324} else {325if (nr_sgs > 0)326dma_unmap_sg(ce->dev, areq->src, nr_sgs, DMA_TO_DEVICE);327dma_unmap_sg(ce->dev, areq->dst, nr_sgd, DMA_FROM_DEVICE);328}329330if (areq->iv && ivsize > 0) {331if (cet->t_iv)332dma_unmap_single(ce->dev, rctx->addr_iv, ivsize,333DMA_TO_DEVICE);334offset = areq->cryptlen - ivsize;335if (rctx->op_dir & CE_DECRYPTION) {336memcpy(areq->iv, rctx->backup_iv, ivsize);337memzero_explicit(rctx->backup_iv, ivsize);338} else {339scatterwalk_map_and_copy(areq->iv, areq->dst, offset,340ivsize, 0);341}342memzero_explicit(rctx->bounce_iv, ivsize);343}344345dma_unmap_single(ce->dev, rctx->addr_key, op->keylen, DMA_TO_DEVICE);346}347348int sun8i_ce_cipher_do_one(struct crypto_engine *engine, void *areq)349{350struct skcipher_request *req = skcipher_request_cast(areq);351struct sun8i_cipher_req_ctx *rctx = skcipher_request_ctx(req);352struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);353struct sun8i_cipher_tfm_ctx *ctx = crypto_skcipher_ctx(tfm);354struct sun8i_ce_dev *ce = ctx->ce;355struct sun8i_ce_flow *chan;356int err;357358chan = &ce->chanlist[rctx->flow];359360err = sun8i_ce_cipher_prepare(req, chan->tl);361if (err)362return err;363364err = sun8i_ce_run_task(ce, rctx->flow,365crypto_tfm_alg_name(req->base.tfm));366367sun8i_ce_cipher_unprepare(req, chan->tl);368369local_bh_disable();370crypto_finalize_skcipher_request(engine, req, err);371local_bh_enable();372373return 0;374}375376int sun8i_ce_skdecrypt(struct skcipher_request *areq)377{378struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(areq);379struct sun8i_cipher_tfm_ctx *op = crypto_skcipher_ctx(tfm);380struct sun8i_cipher_req_ctx *rctx = skcipher_request_ctx(areq);381struct crypto_engine *engine;382int e;383384rctx->op_dir = CE_DECRYPTION;385if (sun8i_ce_cipher_need_fallback(areq))386return sun8i_ce_cipher_fallback(areq);387388e = sun8i_ce_get_engine_number(op->ce);389rctx->flow = e;390engine = op->ce->chanlist[e].engine;391392return crypto_transfer_skcipher_request_to_engine(engine, areq);393}394395int sun8i_ce_skencrypt(struct skcipher_request *areq)396{397struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(areq);398struct sun8i_cipher_tfm_ctx *op = crypto_skcipher_ctx(tfm);399struct sun8i_cipher_req_ctx *rctx = skcipher_request_ctx(areq);400struct crypto_engine *engine;401int e;402403rctx->op_dir = CE_ENCRYPTION;404if (sun8i_ce_cipher_need_fallback(areq))405return sun8i_ce_cipher_fallback(areq);406407e = sun8i_ce_get_engine_number(op->ce);408rctx->flow = e;409engine = op->ce->chanlist[e].engine;410411return crypto_transfer_skcipher_request_to_engine(engine, areq);412}413414int sun8i_ce_cipher_init(struct crypto_tfm *tfm)415{416struct sun8i_cipher_tfm_ctx *op = crypto_tfm_ctx(tfm);417struct sun8i_ce_alg_template *algt;418const char *name = crypto_tfm_alg_name(tfm);419struct crypto_skcipher *sktfm = __crypto_skcipher_cast(tfm);420struct skcipher_alg *alg = crypto_skcipher_alg(sktfm);421int err;422423memset(op, 0, sizeof(struct sun8i_cipher_tfm_ctx));424425algt = container_of(alg, struct sun8i_ce_alg_template, alg.skcipher.base);426op->ce = algt->ce;427428op->fallback_tfm = crypto_alloc_skcipher(name, 0, CRYPTO_ALG_NEED_FALLBACK);429if (IS_ERR(op->fallback_tfm)) {430dev_err(op->ce->dev, "ERROR: Cannot allocate fallback for %s %ld\n",431name, PTR_ERR(op->fallback_tfm));432return PTR_ERR(op->fallback_tfm);433}434435crypto_skcipher_set_reqsize(sktfm, sizeof(struct sun8i_cipher_req_ctx) +436crypto_skcipher_reqsize(op->fallback_tfm));437438if (IS_ENABLED(CONFIG_CRYPTO_DEV_SUN8I_CE_DEBUG))439memcpy(algt->fbname,440crypto_skcipher_driver_name(op->fallback_tfm),441CRYPTO_MAX_ALG_NAME);442443err = pm_runtime_resume_and_get(op->ce->dev);444if (err < 0)445goto error_pm;446447return 0;448error_pm:449crypto_free_skcipher(op->fallback_tfm);450return err;451}452453void sun8i_ce_cipher_exit(struct crypto_tfm *tfm)454{455struct sun8i_cipher_tfm_ctx *op = crypto_tfm_ctx(tfm);456457kfree_sensitive(op->key);458crypto_free_skcipher(op->fallback_tfm);459pm_runtime_put_sync_suspend(op->ce->dev);460}461462int sun8i_ce_aes_setkey(struct crypto_skcipher *tfm, const u8 *key,463unsigned int keylen)464{465struct sun8i_cipher_tfm_ctx *op = crypto_skcipher_ctx(tfm);466struct sun8i_ce_dev *ce = op->ce;467468switch (keylen) {469case 128 / 8:470break;471case 192 / 8:472break;473case 256 / 8:474break;475default:476dev_dbg(ce->dev, "ERROR: Invalid keylen %u\n", keylen);477return -EINVAL;478}479kfree_sensitive(op->key);480op->keylen = keylen;481op->key = kmemdup(key, keylen, GFP_KERNEL | GFP_DMA);482if (!op->key)483return -ENOMEM;484485crypto_skcipher_clear_flags(op->fallback_tfm, CRYPTO_TFM_REQ_MASK);486crypto_skcipher_set_flags(op->fallback_tfm, tfm->base.crt_flags & CRYPTO_TFM_REQ_MASK);487488return crypto_skcipher_setkey(op->fallback_tfm, key, keylen);489}490491int sun8i_ce_des3_setkey(struct crypto_skcipher *tfm, const u8 *key,492unsigned int keylen)493{494struct sun8i_cipher_tfm_ctx *op = crypto_skcipher_ctx(tfm);495int err;496497err = verify_skcipher_des3_key(tfm, key);498if (err)499return err;500501kfree_sensitive(op->key);502op->keylen = keylen;503op->key = kmemdup(key, keylen, GFP_KERNEL | GFP_DMA);504if (!op->key)505return -ENOMEM;506507crypto_skcipher_clear_flags(op->fallback_tfm, CRYPTO_TFM_REQ_MASK);508crypto_skcipher_set_flags(op->fallback_tfm, tfm->base.crt_flags & CRYPTO_TFM_REQ_MASK);509510return crypto_skcipher_setkey(op->fallback_tfm, key, keylen);511}512513514