Loading drivers/crypto/Kconfig +1 −0 Original line number Diff line number Diff line Loading @@ -383,6 +383,7 @@ config CRYPTO_DEV_ATMEL_AES tristate "Support for Atmel AES hw accelerator" depends on AT_XDMAC || AT_HDMAC || COMPILE_TEST select CRYPTO_AES select CRYPTO_AEAD select CRYPTO_BLKCIPHER help Some Atmel processors have AES hw accelerator. Loading drivers/crypto/atmel-aes-regs.h +10 −0 Original line number Diff line number Diff line Loading @@ -9,6 +9,7 @@ #define AES_MR 0x04 #define AES_MR_CYPHER_DEC (0 << 0) #define AES_MR_CYPHER_ENC (1 << 0) #define AES_MR_GTAGEN (1 << 1) #define AES_MR_DUALBUFF (1 << 3) #define AES_MR_PROCDLY_MASK (0xF << 4) #define AES_MR_PROCDLY_OFFSET 4 Loading @@ -26,6 +27,7 @@ #define AES_MR_OPMOD_OFB (0x2 << 12) #define AES_MR_OPMOD_CFB (0x3 << 12) #define AES_MR_OPMOD_CTR (0x4 << 12) #define AES_MR_OPMOD_GCM (0x5 << 12) #define AES_MR_LOD (0x1 << 15) #define AES_MR_CFBS_MASK (0x7 << 16) #define AES_MR_CFBS_128b (0x0 << 16) Loading @@ -44,6 +46,7 @@ #define AES_ISR 0x1C #define AES_INT_DATARDY (1 << 0) #define AES_INT_URAD (1 << 8) #define AES_INT_TAGRDY (1 << 16) #define AES_ISR_URAT_MASK (0xF << 12) #define AES_ISR_URAT_IDR_WR_PROC (0x0 << 12) #define AES_ISR_URAT_ODR_RD_PROC (0x1 << 12) Loading @@ -57,6 +60,13 @@ #define AES_ODATAR(x) (0x50 + ((x) * 0x04)) #define AES_IVR(x) (0x60 + ((x) * 0x04)) #define AES_AADLENR 0x70 #define AES_CLENR 0x74 #define AES_GHASHR(x) (0x78 + ((x) * 0x04)) #define AES_TAGR(x) (0x88 + ((x) * 0x04)) #define AES_CTRR 0x98 #define AES_GCMHR(x) (0x9c + ((x) * 0x04)) #define AES_HW_VERSION 0xFC #endif /* __ATMEL_AES_REGS_H__ */ drivers/crypto/atmel-aes.c +451 −2 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ #include <crypto/scatterwalk.h> #include <crypto/algapi.h> #include <crypto/aes.h> #include <crypto/internal/aead.h> #include <linux/platform_data/crypto-atmel.h> #include <dt-bindings/dma/at91.h> #include "atmel-aes-regs.h" Loading @@ -53,8 +54,9 @@ #define SIZE_IN_WORDS(x) ((x) >> 2) /* AES flags */ /* Reserve bits [18:16] [14:12] [0] for mode (same as for AES_MR) */ /* Reserve bits [18:16] [14:12] [1:0] for mode (same as for AES_MR) */ #define AES_FLAGS_ENCRYPT AES_MR_CYPHER_ENC #define AES_FLAGS_GTAGEN AES_MR_GTAGEN #define AES_FLAGS_OPMODE_MASK (AES_MR_OPMOD_MASK | AES_MR_CFBS_MASK) #define AES_FLAGS_ECB AES_MR_OPMOD_ECB #define AES_FLAGS_CBC AES_MR_OPMOD_CBC Loading @@ -65,9 +67,11 @@ #define AES_FLAGS_CFB16 (AES_MR_OPMOD_CFB | AES_MR_CFBS_16b) #define AES_FLAGS_CFB8 (AES_MR_OPMOD_CFB | AES_MR_CFBS_8b) #define AES_FLAGS_CTR AES_MR_OPMOD_CTR #define AES_FLAGS_GCM AES_MR_OPMOD_GCM #define AES_FLAGS_MODE_MASK (AES_FLAGS_OPMODE_MASK | \ AES_FLAGS_ENCRYPT) AES_FLAGS_ENCRYPT | \ AES_FLAGS_GTAGEN) #define AES_FLAGS_INIT BIT(2) #define AES_FLAGS_BUSY BIT(3) Loading @@ -83,6 +87,7 @@ struct atmel_aes_caps { bool has_dualbuff; bool has_cfb64; bool has_ctr32; bool has_gcm; u32 max_burst_size; }; Loading Loading @@ -113,6 +118,22 @@ struct atmel_aes_ctr_ctx { struct scatterlist dst[2]; }; struct atmel_aes_gcm_ctx { struct atmel_aes_base_ctx base; struct scatterlist src[2]; struct scatterlist dst[2]; u32 j0[AES_BLOCK_SIZE / sizeof(u32)]; u32 tag[AES_BLOCK_SIZE / sizeof(u32)]; u32 ghash[AES_BLOCK_SIZE / sizeof(u32)]; size_t textlen; const u32 *ghash_in; u32 *ghash_out; atmel_aes_fn_t ghash_resume; }; struct atmel_aes_reqctx { unsigned long mode; }; Loading Loading @@ -234,6 +255,12 @@ static inline size_t atmel_aes_padlen(size_t len, size_t block_size) return len ? block_size - len : 0; } static inline struct aead_request * aead_request_cast(struct crypto_async_request *req) { return container_of(req, struct aead_request, base); } static struct atmel_aes_dev *atmel_aes_find_dev(struct atmel_aes_base_ctx *ctx) { struct atmel_aes_dev *aes_dd = NULL; Loading Loading @@ -300,6 +327,11 @@ static inline void atmel_aes_set_mode(struct atmel_aes_dev *dd, dd->flags = (dd->flags & AES_FLAGS_PERSISTENT) | rctx->mode; } static inline bool atmel_aes_is_encrypt(const struct atmel_aes_dev *dd) { return (dd->flags & AES_FLAGS_ENCRYPT); } static inline int atmel_aes_complete(struct atmel_aes_dev *dd, int err) { clk_disable_unprepare(dd->iclk); Loading Loading @@ -1226,6 +1258,409 @@ static struct crypto_alg aes_cfb64_alg = { }; /* gcm aead functions */ static int atmel_aes_gcm_ghash(struct atmel_aes_dev *dd, const u32 *data, size_t datalen, const u32 *ghash_in, u32 *ghash_out, atmel_aes_fn_t resume); static int atmel_aes_gcm_ghash_init(struct atmel_aes_dev *dd); static int atmel_aes_gcm_ghash_finalize(struct atmel_aes_dev *dd); static int atmel_aes_gcm_start(struct atmel_aes_dev *dd); static int atmel_aes_gcm_process(struct atmel_aes_dev *dd); static int atmel_aes_gcm_length(struct atmel_aes_dev *dd); static int atmel_aes_gcm_data(struct atmel_aes_dev *dd); static int atmel_aes_gcm_tag_init(struct atmel_aes_dev *dd); static int atmel_aes_gcm_tag(struct atmel_aes_dev *dd); static int atmel_aes_gcm_finalize(struct atmel_aes_dev *dd); static inline struct atmel_aes_gcm_ctx * atmel_aes_gcm_ctx_cast(struct atmel_aes_base_ctx *ctx) { return container_of(ctx, struct atmel_aes_gcm_ctx, base); } static int atmel_aes_gcm_ghash(struct atmel_aes_dev *dd, const u32 *data, size_t datalen, const u32 *ghash_in, u32 *ghash_out, atmel_aes_fn_t resume) { struct atmel_aes_gcm_ctx *ctx = atmel_aes_gcm_ctx_cast(dd->ctx); dd->data = (u32 *)data; dd->datalen = datalen; ctx->ghash_in = ghash_in; ctx->ghash_out = ghash_out; ctx->ghash_resume = resume; atmel_aes_write_ctrl(dd, false, NULL); return atmel_aes_wait_for_data_ready(dd, atmel_aes_gcm_ghash_init); } static int atmel_aes_gcm_ghash_init(struct atmel_aes_dev *dd) { struct atmel_aes_gcm_ctx *ctx = atmel_aes_gcm_ctx_cast(dd->ctx); /* Set the data length. */ atmel_aes_write(dd, AES_AADLENR, dd->total); atmel_aes_write(dd, AES_CLENR, 0); /* If needed, overwrite the GCM Intermediate Hash Word Registers */ if (ctx->ghash_in) atmel_aes_write_block(dd, AES_GHASHR(0), ctx->ghash_in); return atmel_aes_gcm_ghash_finalize(dd); } static int atmel_aes_gcm_ghash_finalize(struct atmel_aes_dev *dd) { struct atmel_aes_gcm_ctx *ctx = atmel_aes_gcm_ctx_cast(dd->ctx); u32 isr; /* Write data into the Input Data Registers. */ while (dd->datalen > 0) { atmel_aes_write_block(dd, AES_IDATAR(0), dd->data); dd->data += 4; dd->datalen -= AES_BLOCK_SIZE; isr = atmel_aes_read(dd, AES_ISR); if (!(isr & AES_INT_DATARDY)) { dd->resume = atmel_aes_gcm_ghash_finalize; atmel_aes_write(dd, AES_IER, AES_INT_DATARDY); return -EINPROGRESS; } } /* Read the computed hash from GHASHRx. */ atmel_aes_read_block(dd, AES_GHASHR(0), ctx->ghash_out); return ctx->ghash_resume(dd); } static int atmel_aes_gcm_start(struct atmel_aes_dev *dd) { struct atmel_aes_gcm_ctx *ctx = atmel_aes_gcm_ctx_cast(dd->ctx); struct aead_request *req = aead_request_cast(dd->areq); struct crypto_aead *tfm = crypto_aead_reqtfm(req); struct atmel_aes_reqctx *rctx = aead_request_ctx(req); size_t ivsize = crypto_aead_ivsize(tfm); size_t datalen, padlen; const void *iv = req->iv; u8 *data = dd->buf; int err; atmel_aes_set_mode(dd, rctx); err = atmel_aes_hw_init(dd); if (err) return atmel_aes_complete(dd, err); if (likely(ivsize == 12)) { memcpy(ctx->j0, iv, ivsize); ctx->j0[3] = cpu_to_be32(1); return atmel_aes_gcm_process(dd); } padlen = atmel_aes_padlen(ivsize, AES_BLOCK_SIZE); datalen = ivsize + padlen + AES_BLOCK_SIZE; if (datalen > dd->buflen) return atmel_aes_complete(dd, -EINVAL); memcpy(data, iv, ivsize); memset(data + ivsize, 0, padlen + sizeof(u64)); ((u64 *)(data + datalen))[-1] = cpu_to_be64(ivsize * 8); return atmel_aes_gcm_ghash(dd, (const u32 *)data, datalen, NULL, ctx->j0, atmel_aes_gcm_process); } static int atmel_aes_gcm_process(struct atmel_aes_dev *dd) { struct atmel_aes_gcm_ctx *ctx = atmel_aes_gcm_ctx_cast(dd->ctx); struct aead_request *req = aead_request_cast(dd->areq); struct crypto_aead *tfm = crypto_aead_reqtfm(req); bool enc = atmel_aes_is_encrypt(dd); u32 authsize; /* Compute text length. */ authsize = crypto_aead_authsize(tfm); ctx->textlen = req->cryptlen - (enc ? 0 : authsize); /* * According to tcrypt test suite, the GCM Automatic Tag Generation * fails when both the message and its associated data are empty. */ if (likely(req->assoclen != 0 || ctx->textlen != 0)) dd->flags |= AES_FLAGS_GTAGEN; atmel_aes_write_ctrl(dd, false, NULL); return atmel_aes_wait_for_data_ready(dd, atmel_aes_gcm_length); } static int atmel_aes_gcm_length(struct atmel_aes_dev *dd) { struct atmel_aes_gcm_ctx *ctx = atmel_aes_gcm_ctx_cast(dd->ctx); struct aead_request *req = aead_request_cast(dd->areq); u32 j0_lsw, *j0 = ctx->j0; size_t padlen; /* Write incr32(J0) into IV. */ j0_lsw = j0[3]; j0[3] = cpu_to_be32(be32_to_cpu(j0[3]) + 1); atmel_aes_write_block(dd, AES_IVR(0), j0); j0[3] = j0_lsw; /* Set aad and text lengths. */ atmel_aes_write(dd, AES_AADLENR, req->assoclen); atmel_aes_write(dd, AES_CLENR, ctx->textlen); /* Check whether AAD are present. */ if (unlikely(req->assoclen == 0)) { dd->datalen = 0; return atmel_aes_gcm_data(dd); } /* Copy assoc data and add padding. */ padlen = atmel_aes_padlen(req->assoclen, AES_BLOCK_SIZE); if (unlikely(req->assoclen + padlen > dd->buflen)) return atmel_aes_complete(dd, -EINVAL); sg_copy_to_buffer(req->src, sg_nents(req->src), dd->buf, req->assoclen); /* Write assoc data into the Input Data register. */ dd->data = (u32 *)dd->buf; dd->datalen = req->assoclen + padlen; return atmel_aes_gcm_data(dd); } static int atmel_aes_gcm_data(struct atmel_aes_dev *dd) { struct atmel_aes_gcm_ctx *ctx = atmel_aes_gcm_ctx_cast(dd->ctx); struct aead_request *req = aead_request_cast(dd->areq); bool use_dma = (ctx->textlen >= ATMEL_AES_DMA_THRESHOLD); struct scatterlist *src, *dst; u32 isr, mr; /* Write AAD first. */ while (dd->datalen > 0) { atmel_aes_write_block(dd, AES_IDATAR(0), dd->data); dd->data += 4; dd->datalen -= AES_BLOCK_SIZE; isr = atmel_aes_read(dd, AES_ISR); if (!(isr & AES_INT_DATARDY)) { dd->resume = atmel_aes_gcm_data; atmel_aes_write(dd, AES_IER, AES_INT_DATARDY); return -EINPROGRESS; } } /* GMAC only. */ if (unlikely(ctx->textlen == 0)) return atmel_aes_gcm_tag_init(dd); /* Prepare src and dst scatter lists to transfer cipher/plain texts */ src = scatterwalk_ffwd(ctx->src, req->src, req->assoclen); dst = ((req->src == req->dst) ? src : scatterwalk_ffwd(ctx->dst, req->dst, req->assoclen)); if (use_dma) { /* Update the Mode Register for DMA transfers. */ mr = atmel_aes_read(dd, AES_MR); mr &= ~(AES_MR_SMOD_MASK | AES_MR_DUALBUFF); mr |= AES_MR_SMOD_IDATAR0; if (dd->caps.has_dualbuff) mr |= AES_MR_DUALBUFF; atmel_aes_write(dd, AES_MR, mr); return atmel_aes_dma_start(dd, src, dst, ctx->textlen, atmel_aes_gcm_tag_init); } return atmel_aes_cpu_start(dd, src, dst, ctx->textlen, atmel_aes_gcm_tag_init); } static int atmel_aes_gcm_tag_init(struct atmel_aes_dev *dd) { struct atmel_aes_gcm_ctx *ctx = atmel_aes_gcm_ctx_cast(dd->ctx); struct aead_request *req = aead_request_cast(dd->areq); u64 *data = dd->buf; if (likely(dd->flags & AES_FLAGS_GTAGEN)) { if (!(atmel_aes_read(dd, AES_ISR) & AES_INT_TAGRDY)) { dd->resume = atmel_aes_gcm_tag_init; atmel_aes_write(dd, AES_IER, AES_INT_TAGRDY); return -EINPROGRESS; } return atmel_aes_gcm_finalize(dd); } /* Read the GCM Intermediate Hash Word Registers. */ atmel_aes_read_block(dd, AES_GHASHR(0), ctx->ghash); data[0] = cpu_to_be64(req->assoclen * 8); data[1] = cpu_to_be64(ctx->textlen * 8); return atmel_aes_gcm_ghash(dd, (const u32 *)data, AES_BLOCK_SIZE, ctx->ghash, ctx->ghash, atmel_aes_gcm_tag); } static int atmel_aes_gcm_tag(struct atmel_aes_dev *dd) { struct atmel_aes_gcm_ctx *ctx = atmel_aes_gcm_ctx_cast(dd->ctx); unsigned long flags; /* * Change mode to CTR to complete the tag generation. * Use J0 as Initialization Vector. */ flags = dd->flags; dd->flags &= ~(AES_FLAGS_OPMODE_MASK | AES_FLAGS_GTAGEN); dd->flags |= AES_FLAGS_CTR; atmel_aes_write_ctrl(dd, false, ctx->j0); dd->flags = flags; atmel_aes_write_block(dd, AES_IDATAR(0), ctx->ghash); return atmel_aes_wait_for_data_ready(dd, atmel_aes_gcm_finalize); } static int atmel_aes_gcm_finalize(struct atmel_aes_dev *dd) { struct atmel_aes_gcm_ctx *ctx = atmel_aes_gcm_ctx_cast(dd->ctx); struct aead_request *req = aead_request_cast(dd->areq); struct crypto_aead *tfm = crypto_aead_reqtfm(req); bool enc = atmel_aes_is_encrypt(dd); u32 offset, authsize, itag[4], *otag = ctx->tag; int err; /* Read the computed tag. */ if (likely(dd->flags & AES_FLAGS_GTAGEN)) atmel_aes_read_block(dd, AES_TAGR(0), ctx->tag); else atmel_aes_read_block(dd, AES_ODATAR(0), ctx->tag); offset = req->assoclen + ctx->textlen; authsize = crypto_aead_authsize(tfm); if (enc) { scatterwalk_map_and_copy(otag, req->dst, offset, authsize, 1); err = 0; } else { scatterwalk_map_and_copy(itag, req->src, offset, authsize, 0); err = crypto_memneq(itag, otag, authsize) ? -EBADMSG : 0; } return atmel_aes_complete(dd, err); } static int atmel_aes_gcm_crypt(struct aead_request *req, unsigned long mode) { struct atmel_aes_base_ctx *ctx; struct atmel_aes_reqctx *rctx; struct atmel_aes_dev *dd; ctx = crypto_aead_ctx(crypto_aead_reqtfm(req)); ctx->block_size = AES_BLOCK_SIZE; dd = atmel_aes_find_dev(ctx); if (!dd) return -ENODEV; rctx = aead_request_ctx(req); rctx->mode = AES_FLAGS_GCM | mode; return atmel_aes_handle_queue(dd, &req->base); } static int atmel_aes_gcm_setkey(struct crypto_aead *tfm, const u8 *key, unsigned int keylen) { struct atmel_aes_base_ctx *ctx = crypto_aead_ctx(tfm); if (keylen != AES_KEYSIZE_256 && keylen != AES_KEYSIZE_192 && keylen != AES_KEYSIZE_128) { crypto_aead_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN); return -EINVAL; } memcpy(ctx->key, key, keylen); ctx->keylen = keylen; return 0; } static int atmel_aes_gcm_setauthsize(struct crypto_aead *tfm, unsigned int authsize) { /* Same as crypto_gcm_authsize() from crypto/gcm.c */ switch (authsize) { case 4: case 8: case 12: case 13: case 14: case 15: case 16: break; default: return -EINVAL; } return 0; } static int atmel_aes_gcm_encrypt(struct aead_request *req) { return atmel_aes_gcm_crypt(req, AES_FLAGS_ENCRYPT); } static int atmel_aes_gcm_decrypt(struct aead_request *req) { return atmel_aes_gcm_crypt(req, 0); } static int atmel_aes_gcm_init(struct crypto_aead *tfm) { struct atmel_aes_gcm_ctx *ctx = crypto_aead_ctx(tfm); crypto_aead_set_reqsize(tfm, sizeof(struct atmel_aes_reqctx)); ctx->base.start = atmel_aes_gcm_start; return 0; } static void atmel_aes_gcm_exit(struct crypto_aead *tfm) { } static struct aead_alg aes_gcm_alg = { .setkey = atmel_aes_gcm_setkey, .setauthsize = atmel_aes_gcm_setauthsize, .encrypt = atmel_aes_gcm_encrypt, .decrypt = atmel_aes_gcm_decrypt, .init = atmel_aes_gcm_init, .exit = atmel_aes_gcm_exit, .ivsize = 12, .maxauthsize = AES_BLOCK_SIZE, .base = { .cra_name = "gcm(aes)", .cra_driver_name = "atmel-gcm-aes", .cra_priority = ATMEL_AES_PRIORITY, .cra_flags = CRYPTO_ALG_ASYNC, .cra_blocksize = 1, .cra_ctxsize = sizeof(struct atmel_aes_gcm_ctx), .cra_alignmask = 0xf, .cra_module = THIS_MODULE, }, }; /* Probe functions */ static int atmel_aes_buff_init(struct atmel_aes_dev *dd) Loading Loading @@ -1334,6 +1769,9 @@ static void atmel_aes_unregister_algs(struct atmel_aes_dev *dd) { int i; if (dd->caps.has_gcm) crypto_unregister_aead(&aes_gcm_alg); if (dd->caps.has_cfb64) crypto_unregister_alg(&aes_cfb64_alg); Loading @@ -1357,8 +1795,16 @@ static int atmel_aes_register_algs(struct atmel_aes_dev *dd) goto err_aes_cfb64_alg; } if (dd->caps.has_gcm) { err = crypto_register_aead(&aes_gcm_alg); if (err) goto err_aes_gcm_alg; } return 0; err_aes_gcm_alg: crypto_unregister_alg(&aes_cfb64_alg); err_aes_cfb64_alg: i = ARRAY_SIZE(aes_algs); err_aes_algs: Loading @@ -1373,6 +1819,7 @@ static void atmel_aes_get_cap(struct atmel_aes_dev *dd) dd->caps.has_dualbuff = 0; dd->caps.has_cfb64 = 0; dd->caps.has_ctr32 = 0; dd->caps.has_gcm = 0; dd->caps.max_burst_size = 1; /* keep only major version number */ Loading @@ -1381,12 +1828,14 @@ static void atmel_aes_get_cap(struct atmel_aes_dev *dd) dd->caps.has_dualbuff = 1; dd->caps.has_cfb64 = 1; dd->caps.has_ctr32 = 1; dd->caps.has_gcm = 1; dd->caps.max_burst_size = 4; break; case 0x200: dd->caps.has_dualbuff = 1; dd->caps.has_cfb64 = 1; dd->caps.has_ctr32 = 1; dd->caps.has_gcm = 1; dd->caps.max_burst_size = 4; break; case 0x130: Loading Loading
drivers/crypto/Kconfig +1 −0 Original line number Diff line number Diff line Loading @@ -383,6 +383,7 @@ config CRYPTO_DEV_ATMEL_AES tristate "Support for Atmel AES hw accelerator" depends on AT_XDMAC || AT_HDMAC || COMPILE_TEST select CRYPTO_AES select CRYPTO_AEAD select CRYPTO_BLKCIPHER help Some Atmel processors have AES hw accelerator. Loading
drivers/crypto/atmel-aes-regs.h +10 −0 Original line number Diff line number Diff line Loading @@ -9,6 +9,7 @@ #define AES_MR 0x04 #define AES_MR_CYPHER_DEC (0 << 0) #define AES_MR_CYPHER_ENC (1 << 0) #define AES_MR_GTAGEN (1 << 1) #define AES_MR_DUALBUFF (1 << 3) #define AES_MR_PROCDLY_MASK (0xF << 4) #define AES_MR_PROCDLY_OFFSET 4 Loading @@ -26,6 +27,7 @@ #define AES_MR_OPMOD_OFB (0x2 << 12) #define AES_MR_OPMOD_CFB (0x3 << 12) #define AES_MR_OPMOD_CTR (0x4 << 12) #define AES_MR_OPMOD_GCM (0x5 << 12) #define AES_MR_LOD (0x1 << 15) #define AES_MR_CFBS_MASK (0x7 << 16) #define AES_MR_CFBS_128b (0x0 << 16) Loading @@ -44,6 +46,7 @@ #define AES_ISR 0x1C #define AES_INT_DATARDY (1 << 0) #define AES_INT_URAD (1 << 8) #define AES_INT_TAGRDY (1 << 16) #define AES_ISR_URAT_MASK (0xF << 12) #define AES_ISR_URAT_IDR_WR_PROC (0x0 << 12) #define AES_ISR_URAT_ODR_RD_PROC (0x1 << 12) Loading @@ -57,6 +60,13 @@ #define AES_ODATAR(x) (0x50 + ((x) * 0x04)) #define AES_IVR(x) (0x60 + ((x) * 0x04)) #define AES_AADLENR 0x70 #define AES_CLENR 0x74 #define AES_GHASHR(x) (0x78 + ((x) * 0x04)) #define AES_TAGR(x) (0x88 + ((x) * 0x04)) #define AES_CTRR 0x98 #define AES_GCMHR(x) (0x9c + ((x) * 0x04)) #define AES_HW_VERSION 0xFC #endif /* __ATMEL_AES_REGS_H__ */
drivers/crypto/atmel-aes.c +451 −2 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ #include <crypto/scatterwalk.h> #include <crypto/algapi.h> #include <crypto/aes.h> #include <crypto/internal/aead.h> #include <linux/platform_data/crypto-atmel.h> #include <dt-bindings/dma/at91.h> #include "atmel-aes-regs.h" Loading @@ -53,8 +54,9 @@ #define SIZE_IN_WORDS(x) ((x) >> 2) /* AES flags */ /* Reserve bits [18:16] [14:12] [0] for mode (same as for AES_MR) */ /* Reserve bits [18:16] [14:12] [1:0] for mode (same as for AES_MR) */ #define AES_FLAGS_ENCRYPT AES_MR_CYPHER_ENC #define AES_FLAGS_GTAGEN AES_MR_GTAGEN #define AES_FLAGS_OPMODE_MASK (AES_MR_OPMOD_MASK | AES_MR_CFBS_MASK) #define AES_FLAGS_ECB AES_MR_OPMOD_ECB #define AES_FLAGS_CBC AES_MR_OPMOD_CBC Loading @@ -65,9 +67,11 @@ #define AES_FLAGS_CFB16 (AES_MR_OPMOD_CFB | AES_MR_CFBS_16b) #define AES_FLAGS_CFB8 (AES_MR_OPMOD_CFB | AES_MR_CFBS_8b) #define AES_FLAGS_CTR AES_MR_OPMOD_CTR #define AES_FLAGS_GCM AES_MR_OPMOD_GCM #define AES_FLAGS_MODE_MASK (AES_FLAGS_OPMODE_MASK | \ AES_FLAGS_ENCRYPT) AES_FLAGS_ENCRYPT | \ AES_FLAGS_GTAGEN) #define AES_FLAGS_INIT BIT(2) #define AES_FLAGS_BUSY BIT(3) Loading @@ -83,6 +87,7 @@ struct atmel_aes_caps { bool has_dualbuff; bool has_cfb64; bool has_ctr32; bool has_gcm; u32 max_burst_size; }; Loading Loading @@ -113,6 +118,22 @@ struct atmel_aes_ctr_ctx { struct scatterlist dst[2]; }; struct atmel_aes_gcm_ctx { struct atmel_aes_base_ctx base; struct scatterlist src[2]; struct scatterlist dst[2]; u32 j0[AES_BLOCK_SIZE / sizeof(u32)]; u32 tag[AES_BLOCK_SIZE / sizeof(u32)]; u32 ghash[AES_BLOCK_SIZE / sizeof(u32)]; size_t textlen; const u32 *ghash_in; u32 *ghash_out; atmel_aes_fn_t ghash_resume; }; struct atmel_aes_reqctx { unsigned long mode; }; Loading Loading @@ -234,6 +255,12 @@ static inline size_t atmel_aes_padlen(size_t len, size_t block_size) return len ? block_size - len : 0; } static inline struct aead_request * aead_request_cast(struct crypto_async_request *req) { return container_of(req, struct aead_request, base); } static struct atmel_aes_dev *atmel_aes_find_dev(struct atmel_aes_base_ctx *ctx) { struct atmel_aes_dev *aes_dd = NULL; Loading Loading @@ -300,6 +327,11 @@ static inline void atmel_aes_set_mode(struct atmel_aes_dev *dd, dd->flags = (dd->flags & AES_FLAGS_PERSISTENT) | rctx->mode; } static inline bool atmel_aes_is_encrypt(const struct atmel_aes_dev *dd) { return (dd->flags & AES_FLAGS_ENCRYPT); } static inline int atmel_aes_complete(struct atmel_aes_dev *dd, int err) { clk_disable_unprepare(dd->iclk); Loading Loading @@ -1226,6 +1258,409 @@ static struct crypto_alg aes_cfb64_alg = { }; /* gcm aead functions */ static int atmel_aes_gcm_ghash(struct atmel_aes_dev *dd, const u32 *data, size_t datalen, const u32 *ghash_in, u32 *ghash_out, atmel_aes_fn_t resume); static int atmel_aes_gcm_ghash_init(struct atmel_aes_dev *dd); static int atmel_aes_gcm_ghash_finalize(struct atmel_aes_dev *dd); static int atmel_aes_gcm_start(struct atmel_aes_dev *dd); static int atmel_aes_gcm_process(struct atmel_aes_dev *dd); static int atmel_aes_gcm_length(struct atmel_aes_dev *dd); static int atmel_aes_gcm_data(struct atmel_aes_dev *dd); static int atmel_aes_gcm_tag_init(struct atmel_aes_dev *dd); static int atmel_aes_gcm_tag(struct atmel_aes_dev *dd); static int atmel_aes_gcm_finalize(struct atmel_aes_dev *dd); static inline struct atmel_aes_gcm_ctx * atmel_aes_gcm_ctx_cast(struct atmel_aes_base_ctx *ctx) { return container_of(ctx, struct atmel_aes_gcm_ctx, base); } static int atmel_aes_gcm_ghash(struct atmel_aes_dev *dd, const u32 *data, size_t datalen, const u32 *ghash_in, u32 *ghash_out, atmel_aes_fn_t resume) { struct atmel_aes_gcm_ctx *ctx = atmel_aes_gcm_ctx_cast(dd->ctx); dd->data = (u32 *)data; dd->datalen = datalen; ctx->ghash_in = ghash_in; ctx->ghash_out = ghash_out; ctx->ghash_resume = resume; atmel_aes_write_ctrl(dd, false, NULL); return atmel_aes_wait_for_data_ready(dd, atmel_aes_gcm_ghash_init); } static int atmel_aes_gcm_ghash_init(struct atmel_aes_dev *dd) { struct atmel_aes_gcm_ctx *ctx = atmel_aes_gcm_ctx_cast(dd->ctx); /* Set the data length. */ atmel_aes_write(dd, AES_AADLENR, dd->total); atmel_aes_write(dd, AES_CLENR, 0); /* If needed, overwrite the GCM Intermediate Hash Word Registers */ if (ctx->ghash_in) atmel_aes_write_block(dd, AES_GHASHR(0), ctx->ghash_in); return atmel_aes_gcm_ghash_finalize(dd); } static int atmel_aes_gcm_ghash_finalize(struct atmel_aes_dev *dd) { struct atmel_aes_gcm_ctx *ctx = atmel_aes_gcm_ctx_cast(dd->ctx); u32 isr; /* Write data into the Input Data Registers. */ while (dd->datalen > 0) { atmel_aes_write_block(dd, AES_IDATAR(0), dd->data); dd->data += 4; dd->datalen -= AES_BLOCK_SIZE; isr = atmel_aes_read(dd, AES_ISR); if (!(isr & AES_INT_DATARDY)) { dd->resume = atmel_aes_gcm_ghash_finalize; atmel_aes_write(dd, AES_IER, AES_INT_DATARDY); return -EINPROGRESS; } } /* Read the computed hash from GHASHRx. */ atmel_aes_read_block(dd, AES_GHASHR(0), ctx->ghash_out); return ctx->ghash_resume(dd); } static int atmel_aes_gcm_start(struct atmel_aes_dev *dd) { struct atmel_aes_gcm_ctx *ctx = atmel_aes_gcm_ctx_cast(dd->ctx); struct aead_request *req = aead_request_cast(dd->areq); struct crypto_aead *tfm = crypto_aead_reqtfm(req); struct atmel_aes_reqctx *rctx = aead_request_ctx(req); size_t ivsize = crypto_aead_ivsize(tfm); size_t datalen, padlen; const void *iv = req->iv; u8 *data = dd->buf; int err; atmel_aes_set_mode(dd, rctx); err = atmel_aes_hw_init(dd); if (err) return atmel_aes_complete(dd, err); if (likely(ivsize == 12)) { memcpy(ctx->j0, iv, ivsize); ctx->j0[3] = cpu_to_be32(1); return atmel_aes_gcm_process(dd); } padlen = atmel_aes_padlen(ivsize, AES_BLOCK_SIZE); datalen = ivsize + padlen + AES_BLOCK_SIZE; if (datalen > dd->buflen) return atmel_aes_complete(dd, -EINVAL); memcpy(data, iv, ivsize); memset(data + ivsize, 0, padlen + sizeof(u64)); ((u64 *)(data + datalen))[-1] = cpu_to_be64(ivsize * 8); return atmel_aes_gcm_ghash(dd, (const u32 *)data, datalen, NULL, ctx->j0, atmel_aes_gcm_process); } static int atmel_aes_gcm_process(struct atmel_aes_dev *dd) { struct atmel_aes_gcm_ctx *ctx = atmel_aes_gcm_ctx_cast(dd->ctx); struct aead_request *req = aead_request_cast(dd->areq); struct crypto_aead *tfm = crypto_aead_reqtfm(req); bool enc = atmel_aes_is_encrypt(dd); u32 authsize; /* Compute text length. */ authsize = crypto_aead_authsize(tfm); ctx->textlen = req->cryptlen - (enc ? 0 : authsize); /* * According to tcrypt test suite, the GCM Automatic Tag Generation * fails when both the message and its associated data are empty. */ if (likely(req->assoclen != 0 || ctx->textlen != 0)) dd->flags |= AES_FLAGS_GTAGEN; atmel_aes_write_ctrl(dd, false, NULL); return atmel_aes_wait_for_data_ready(dd, atmel_aes_gcm_length); } static int atmel_aes_gcm_length(struct atmel_aes_dev *dd) { struct atmel_aes_gcm_ctx *ctx = atmel_aes_gcm_ctx_cast(dd->ctx); struct aead_request *req = aead_request_cast(dd->areq); u32 j0_lsw, *j0 = ctx->j0; size_t padlen; /* Write incr32(J0) into IV. */ j0_lsw = j0[3]; j0[3] = cpu_to_be32(be32_to_cpu(j0[3]) + 1); atmel_aes_write_block(dd, AES_IVR(0), j0); j0[3] = j0_lsw; /* Set aad and text lengths. */ atmel_aes_write(dd, AES_AADLENR, req->assoclen); atmel_aes_write(dd, AES_CLENR, ctx->textlen); /* Check whether AAD are present. */ if (unlikely(req->assoclen == 0)) { dd->datalen = 0; return atmel_aes_gcm_data(dd); } /* Copy assoc data and add padding. */ padlen = atmel_aes_padlen(req->assoclen, AES_BLOCK_SIZE); if (unlikely(req->assoclen + padlen > dd->buflen)) return atmel_aes_complete(dd, -EINVAL); sg_copy_to_buffer(req->src, sg_nents(req->src), dd->buf, req->assoclen); /* Write assoc data into the Input Data register. */ dd->data = (u32 *)dd->buf; dd->datalen = req->assoclen + padlen; return atmel_aes_gcm_data(dd); } static int atmel_aes_gcm_data(struct atmel_aes_dev *dd) { struct atmel_aes_gcm_ctx *ctx = atmel_aes_gcm_ctx_cast(dd->ctx); struct aead_request *req = aead_request_cast(dd->areq); bool use_dma = (ctx->textlen >= ATMEL_AES_DMA_THRESHOLD); struct scatterlist *src, *dst; u32 isr, mr; /* Write AAD first. */ while (dd->datalen > 0) { atmel_aes_write_block(dd, AES_IDATAR(0), dd->data); dd->data += 4; dd->datalen -= AES_BLOCK_SIZE; isr = atmel_aes_read(dd, AES_ISR); if (!(isr & AES_INT_DATARDY)) { dd->resume = atmel_aes_gcm_data; atmel_aes_write(dd, AES_IER, AES_INT_DATARDY); return -EINPROGRESS; } } /* GMAC only. */ if (unlikely(ctx->textlen == 0)) return atmel_aes_gcm_tag_init(dd); /* Prepare src and dst scatter lists to transfer cipher/plain texts */ src = scatterwalk_ffwd(ctx->src, req->src, req->assoclen); dst = ((req->src == req->dst) ? src : scatterwalk_ffwd(ctx->dst, req->dst, req->assoclen)); if (use_dma) { /* Update the Mode Register for DMA transfers. */ mr = atmel_aes_read(dd, AES_MR); mr &= ~(AES_MR_SMOD_MASK | AES_MR_DUALBUFF); mr |= AES_MR_SMOD_IDATAR0; if (dd->caps.has_dualbuff) mr |= AES_MR_DUALBUFF; atmel_aes_write(dd, AES_MR, mr); return atmel_aes_dma_start(dd, src, dst, ctx->textlen, atmel_aes_gcm_tag_init); } return atmel_aes_cpu_start(dd, src, dst, ctx->textlen, atmel_aes_gcm_tag_init); } static int atmel_aes_gcm_tag_init(struct atmel_aes_dev *dd) { struct atmel_aes_gcm_ctx *ctx = atmel_aes_gcm_ctx_cast(dd->ctx); struct aead_request *req = aead_request_cast(dd->areq); u64 *data = dd->buf; if (likely(dd->flags & AES_FLAGS_GTAGEN)) { if (!(atmel_aes_read(dd, AES_ISR) & AES_INT_TAGRDY)) { dd->resume = atmel_aes_gcm_tag_init; atmel_aes_write(dd, AES_IER, AES_INT_TAGRDY); return -EINPROGRESS; } return atmel_aes_gcm_finalize(dd); } /* Read the GCM Intermediate Hash Word Registers. */ atmel_aes_read_block(dd, AES_GHASHR(0), ctx->ghash); data[0] = cpu_to_be64(req->assoclen * 8); data[1] = cpu_to_be64(ctx->textlen * 8); return atmel_aes_gcm_ghash(dd, (const u32 *)data, AES_BLOCK_SIZE, ctx->ghash, ctx->ghash, atmel_aes_gcm_tag); } static int atmel_aes_gcm_tag(struct atmel_aes_dev *dd) { struct atmel_aes_gcm_ctx *ctx = atmel_aes_gcm_ctx_cast(dd->ctx); unsigned long flags; /* * Change mode to CTR to complete the tag generation. * Use J0 as Initialization Vector. */ flags = dd->flags; dd->flags &= ~(AES_FLAGS_OPMODE_MASK | AES_FLAGS_GTAGEN); dd->flags |= AES_FLAGS_CTR; atmel_aes_write_ctrl(dd, false, ctx->j0); dd->flags = flags; atmel_aes_write_block(dd, AES_IDATAR(0), ctx->ghash); return atmel_aes_wait_for_data_ready(dd, atmel_aes_gcm_finalize); } static int atmel_aes_gcm_finalize(struct atmel_aes_dev *dd) { struct atmel_aes_gcm_ctx *ctx = atmel_aes_gcm_ctx_cast(dd->ctx); struct aead_request *req = aead_request_cast(dd->areq); struct crypto_aead *tfm = crypto_aead_reqtfm(req); bool enc = atmel_aes_is_encrypt(dd); u32 offset, authsize, itag[4], *otag = ctx->tag; int err; /* Read the computed tag. */ if (likely(dd->flags & AES_FLAGS_GTAGEN)) atmel_aes_read_block(dd, AES_TAGR(0), ctx->tag); else atmel_aes_read_block(dd, AES_ODATAR(0), ctx->tag); offset = req->assoclen + ctx->textlen; authsize = crypto_aead_authsize(tfm); if (enc) { scatterwalk_map_and_copy(otag, req->dst, offset, authsize, 1); err = 0; } else { scatterwalk_map_and_copy(itag, req->src, offset, authsize, 0); err = crypto_memneq(itag, otag, authsize) ? -EBADMSG : 0; } return atmel_aes_complete(dd, err); } static int atmel_aes_gcm_crypt(struct aead_request *req, unsigned long mode) { struct atmel_aes_base_ctx *ctx; struct atmel_aes_reqctx *rctx; struct atmel_aes_dev *dd; ctx = crypto_aead_ctx(crypto_aead_reqtfm(req)); ctx->block_size = AES_BLOCK_SIZE; dd = atmel_aes_find_dev(ctx); if (!dd) return -ENODEV; rctx = aead_request_ctx(req); rctx->mode = AES_FLAGS_GCM | mode; return atmel_aes_handle_queue(dd, &req->base); } static int atmel_aes_gcm_setkey(struct crypto_aead *tfm, const u8 *key, unsigned int keylen) { struct atmel_aes_base_ctx *ctx = crypto_aead_ctx(tfm); if (keylen != AES_KEYSIZE_256 && keylen != AES_KEYSIZE_192 && keylen != AES_KEYSIZE_128) { crypto_aead_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN); return -EINVAL; } memcpy(ctx->key, key, keylen); ctx->keylen = keylen; return 0; } static int atmel_aes_gcm_setauthsize(struct crypto_aead *tfm, unsigned int authsize) { /* Same as crypto_gcm_authsize() from crypto/gcm.c */ switch (authsize) { case 4: case 8: case 12: case 13: case 14: case 15: case 16: break; default: return -EINVAL; } return 0; } static int atmel_aes_gcm_encrypt(struct aead_request *req) { return atmel_aes_gcm_crypt(req, AES_FLAGS_ENCRYPT); } static int atmel_aes_gcm_decrypt(struct aead_request *req) { return atmel_aes_gcm_crypt(req, 0); } static int atmel_aes_gcm_init(struct crypto_aead *tfm) { struct atmel_aes_gcm_ctx *ctx = crypto_aead_ctx(tfm); crypto_aead_set_reqsize(tfm, sizeof(struct atmel_aes_reqctx)); ctx->base.start = atmel_aes_gcm_start; return 0; } static void atmel_aes_gcm_exit(struct crypto_aead *tfm) { } static struct aead_alg aes_gcm_alg = { .setkey = atmel_aes_gcm_setkey, .setauthsize = atmel_aes_gcm_setauthsize, .encrypt = atmel_aes_gcm_encrypt, .decrypt = atmel_aes_gcm_decrypt, .init = atmel_aes_gcm_init, .exit = atmel_aes_gcm_exit, .ivsize = 12, .maxauthsize = AES_BLOCK_SIZE, .base = { .cra_name = "gcm(aes)", .cra_driver_name = "atmel-gcm-aes", .cra_priority = ATMEL_AES_PRIORITY, .cra_flags = CRYPTO_ALG_ASYNC, .cra_blocksize = 1, .cra_ctxsize = sizeof(struct atmel_aes_gcm_ctx), .cra_alignmask = 0xf, .cra_module = THIS_MODULE, }, }; /* Probe functions */ static int atmel_aes_buff_init(struct atmel_aes_dev *dd) Loading Loading @@ -1334,6 +1769,9 @@ static void atmel_aes_unregister_algs(struct atmel_aes_dev *dd) { int i; if (dd->caps.has_gcm) crypto_unregister_aead(&aes_gcm_alg); if (dd->caps.has_cfb64) crypto_unregister_alg(&aes_cfb64_alg); Loading @@ -1357,8 +1795,16 @@ static int atmel_aes_register_algs(struct atmel_aes_dev *dd) goto err_aes_cfb64_alg; } if (dd->caps.has_gcm) { err = crypto_register_aead(&aes_gcm_alg); if (err) goto err_aes_gcm_alg; } return 0; err_aes_gcm_alg: crypto_unregister_alg(&aes_cfb64_alg); err_aes_cfb64_alg: i = ARRAY_SIZE(aes_algs); err_aes_algs: Loading @@ -1373,6 +1819,7 @@ static void atmel_aes_get_cap(struct atmel_aes_dev *dd) dd->caps.has_dualbuff = 0; dd->caps.has_cfb64 = 0; dd->caps.has_ctr32 = 0; dd->caps.has_gcm = 0; dd->caps.max_burst_size = 1; /* keep only major version number */ Loading @@ -1381,12 +1828,14 @@ static void atmel_aes_get_cap(struct atmel_aes_dev *dd) dd->caps.has_dualbuff = 1; dd->caps.has_cfb64 = 1; dd->caps.has_ctr32 = 1; dd->caps.has_gcm = 1; dd->caps.max_burst_size = 4; break; case 0x200: dd->caps.has_dualbuff = 1; dd->caps.has_cfb64 = 1; dd->caps.has_ctr32 = 1; dd->caps.has_gcm = 1; dd->caps.max_burst_size = 4; break; case 0x130: Loading