xref: /openbmc/linux/drivers/crypto/vmx/aes_cbc.c (revision 5f76eea8)
18c755aceSMarcelo H. Cerri /**
28c755aceSMarcelo H. Cerri  * AES CBC routines supporting VMX instructions on the Power 8
38c755aceSMarcelo H. Cerri  *
48c755aceSMarcelo H. Cerri  * Copyright (C) 2015 International Business Machines Inc.
58c755aceSMarcelo H. Cerri  *
68c755aceSMarcelo H. Cerri  * This program is free software; you can redistribute it and/or modify
78c755aceSMarcelo H. Cerri  * it under the terms of the GNU General Public License as published by
88c755aceSMarcelo H. Cerri  * the Free Software Foundation; version 2 only.
98c755aceSMarcelo H. Cerri  *
108c755aceSMarcelo H. Cerri  * This program is distributed in the hope that it will be useful,
118c755aceSMarcelo H. Cerri  * but WITHOUT ANY WARRANTY; without even the implied warranty of
128c755aceSMarcelo H. Cerri  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
138c755aceSMarcelo H. Cerri  * GNU General Public License for more details.
148c755aceSMarcelo H. Cerri  *
158c755aceSMarcelo H. Cerri  * You should have received a copy of the GNU General Public License
168c755aceSMarcelo H. Cerri  * along with this program; if not, write to the Free Software
178c755aceSMarcelo H. Cerri  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
188c755aceSMarcelo H. Cerri  *
198c755aceSMarcelo H. Cerri  * Author: Marcelo Henrique Cerri <mhcerri@br.ibm.com>
208c755aceSMarcelo H. Cerri  */
218c755aceSMarcelo H. Cerri 
228c755aceSMarcelo H. Cerri #include <linux/types.h>
238c755aceSMarcelo H. Cerri #include <linux/err.h>
248c755aceSMarcelo H. Cerri #include <linux/crypto.h>
258c755aceSMarcelo H. Cerri #include <linux/delay.h>
268c755aceSMarcelo H. Cerri #include <linux/hardirq.h>
278c755aceSMarcelo H. Cerri #include <asm/switch_to.h>
288c755aceSMarcelo H. Cerri #include <crypto/aes.h>
298c755aceSMarcelo H. Cerri #include <crypto/scatterwalk.h>
308c755aceSMarcelo H. Cerri 
318c755aceSMarcelo H. Cerri #include "aesp8-ppc.h"
328c755aceSMarcelo H. Cerri 
338c755aceSMarcelo H. Cerri struct p8_aes_cbc_ctx {
348c755aceSMarcelo H. Cerri     struct crypto_blkcipher *fallback;
358c755aceSMarcelo H. Cerri     struct aes_key enc_key;
368c755aceSMarcelo H. Cerri     struct aes_key dec_key;
378c755aceSMarcelo H. Cerri };
388c755aceSMarcelo H. Cerri 
398c755aceSMarcelo H. Cerri static int p8_aes_cbc_init(struct crypto_tfm *tfm)
408c755aceSMarcelo H. Cerri {
418c755aceSMarcelo H. Cerri     const char *alg;
428c755aceSMarcelo H. Cerri     struct crypto_blkcipher *fallback;
438c755aceSMarcelo H. Cerri     struct p8_aes_cbc_ctx *ctx = crypto_tfm_ctx(tfm);
448c755aceSMarcelo H. Cerri 
458c755aceSMarcelo H. Cerri     if (!(alg = crypto_tfm_alg_name(tfm))) {
468c755aceSMarcelo H. Cerri         printk(KERN_ERR "Failed to get algorithm name.\n");
478c755aceSMarcelo H. Cerri         return -ENOENT;
488c755aceSMarcelo H. Cerri     }
498c755aceSMarcelo H. Cerri 
508c755aceSMarcelo H. Cerri     fallback = crypto_alloc_blkcipher(alg, 0 ,CRYPTO_ALG_NEED_FALLBACK);
518c755aceSMarcelo H. Cerri     if (IS_ERR(fallback)) {
528c755aceSMarcelo H. Cerri         printk(KERN_ERR "Failed to allocate transformation for '%s': %ld\n",
538c755aceSMarcelo H. Cerri                 alg, PTR_ERR(fallback));
548c755aceSMarcelo H. Cerri         return PTR_ERR(fallback);
558c755aceSMarcelo H. Cerri     }
568c755aceSMarcelo H. Cerri     printk(KERN_INFO "Using '%s' as fallback implementation.\n",
578c755aceSMarcelo H. Cerri             crypto_tfm_alg_driver_name((struct crypto_tfm *) fallback));
588c755aceSMarcelo H. Cerri 
598c755aceSMarcelo H. Cerri     crypto_blkcipher_set_flags(fallback,
608c755aceSMarcelo H. Cerri             crypto_blkcipher_get_flags((struct crypto_blkcipher *) tfm));
618c755aceSMarcelo H. Cerri     ctx->fallback = fallback;
628c755aceSMarcelo H. Cerri 
638c755aceSMarcelo H. Cerri     return 0;
648c755aceSMarcelo H. Cerri }
658c755aceSMarcelo H. Cerri 
668c755aceSMarcelo H. Cerri static void p8_aes_cbc_exit(struct crypto_tfm *tfm)
678c755aceSMarcelo H. Cerri {
688c755aceSMarcelo H. Cerri     struct p8_aes_cbc_ctx *ctx = crypto_tfm_ctx(tfm);
698c755aceSMarcelo H. Cerri 
708c755aceSMarcelo H. Cerri     if (ctx->fallback) {
718c755aceSMarcelo H. Cerri         crypto_free_blkcipher(ctx->fallback);
728c755aceSMarcelo H. Cerri         ctx->fallback = NULL;
738c755aceSMarcelo H. Cerri     }
748c755aceSMarcelo H. Cerri }
758c755aceSMarcelo H. Cerri 
768c755aceSMarcelo H. Cerri static int p8_aes_cbc_setkey(struct crypto_tfm *tfm, const u8 *key,
778c755aceSMarcelo H. Cerri     unsigned int keylen)
788c755aceSMarcelo H. Cerri {
798c755aceSMarcelo H. Cerri     int ret;
808c755aceSMarcelo H. Cerri     struct p8_aes_cbc_ctx *ctx = crypto_tfm_ctx(tfm);
818c755aceSMarcelo H. Cerri 
825f76eea8SDavid Hildenbrand     preempt_disable();
838c755aceSMarcelo H. Cerri     pagefault_disable();
848c755aceSMarcelo H. Cerri     enable_kernel_altivec();
858c755aceSMarcelo H. Cerri     ret = aes_p8_set_encrypt_key(key, keylen * 8, &ctx->enc_key);
868c755aceSMarcelo H. Cerri     ret += aes_p8_set_decrypt_key(key, keylen * 8, &ctx->dec_key);
878c755aceSMarcelo H. Cerri     pagefault_enable();
885f76eea8SDavid Hildenbrand     preempt_enable();
898c755aceSMarcelo H. Cerri 
908c755aceSMarcelo H. Cerri     ret += crypto_blkcipher_setkey(ctx->fallback, key, keylen);
918c755aceSMarcelo H. Cerri     return ret;
928c755aceSMarcelo H. Cerri }
938c755aceSMarcelo H. Cerri 
948c755aceSMarcelo H. Cerri static int p8_aes_cbc_encrypt(struct blkcipher_desc *desc,
958c755aceSMarcelo H. Cerri     struct scatterlist *dst, struct scatterlist *src,
968c755aceSMarcelo H. Cerri     unsigned int nbytes)
978c755aceSMarcelo H. Cerri {
988c755aceSMarcelo H. Cerri     int ret;
998c755aceSMarcelo H. Cerri     struct blkcipher_walk walk;
1008c755aceSMarcelo H. Cerri     struct p8_aes_cbc_ctx *ctx = crypto_tfm_ctx(
1018c755aceSMarcelo H. Cerri             crypto_blkcipher_tfm(desc->tfm));
1028c755aceSMarcelo H. Cerri     struct blkcipher_desc fallback_desc = {
1038c755aceSMarcelo H. Cerri         .tfm = ctx->fallback,
1048c755aceSMarcelo H. Cerri         .info = desc->info,
1058c755aceSMarcelo H. Cerri         .flags = desc->flags
1068c755aceSMarcelo H. Cerri     };
1078c755aceSMarcelo H. Cerri 
1088c755aceSMarcelo H. Cerri     if (in_interrupt()) {
1098c755aceSMarcelo H. Cerri         ret = crypto_blkcipher_encrypt(&fallback_desc, dst, src, nbytes);
1108c755aceSMarcelo H. Cerri     } else {
1115f76eea8SDavid Hildenbrand 	preempt_disable();
1128c755aceSMarcelo H. Cerri         pagefault_disable();
1138c755aceSMarcelo H. Cerri         enable_kernel_altivec();
1148c755aceSMarcelo H. Cerri 
1158c755aceSMarcelo H. Cerri 	blkcipher_walk_init(&walk, dst, src, nbytes);
1168c755aceSMarcelo H. Cerri         ret = blkcipher_walk_virt(desc, &walk);
1178c755aceSMarcelo H. Cerri         while ((nbytes = walk.nbytes)) {
1188c755aceSMarcelo H. Cerri 			aes_p8_cbc_encrypt(walk.src.virt.addr, walk.dst.virt.addr,
1198c755aceSMarcelo H. Cerri 				nbytes & AES_BLOCK_MASK, &ctx->enc_key, walk.iv, 1);
1208c755aceSMarcelo H. Cerri 			nbytes &= AES_BLOCK_SIZE - 1;
1218c755aceSMarcelo H. Cerri 			ret = blkcipher_walk_done(desc, &walk, nbytes);
1228c755aceSMarcelo H. Cerri 	}
1238c755aceSMarcelo H. Cerri 
1248c755aceSMarcelo H. Cerri         pagefault_enable();
1255f76eea8SDavid Hildenbrand 	preempt_enable();
1268c755aceSMarcelo H. Cerri     }
1278c755aceSMarcelo H. Cerri 
1288c755aceSMarcelo H. Cerri     return ret;
1298c755aceSMarcelo H. Cerri }
1308c755aceSMarcelo H. Cerri 
1318c755aceSMarcelo H. Cerri static int p8_aes_cbc_decrypt(struct blkcipher_desc *desc,
1328c755aceSMarcelo H. Cerri     struct scatterlist *dst, struct scatterlist *src,
1338c755aceSMarcelo H. Cerri     unsigned int nbytes)
1348c755aceSMarcelo H. Cerri {
1358c755aceSMarcelo H. Cerri     int ret;
1368c755aceSMarcelo H. Cerri     struct blkcipher_walk walk;
1378c755aceSMarcelo H. Cerri     struct p8_aes_cbc_ctx *ctx = crypto_tfm_ctx(
1388c755aceSMarcelo H. Cerri             crypto_blkcipher_tfm(desc->tfm));
1398c755aceSMarcelo H. Cerri     struct blkcipher_desc fallback_desc = {
1408c755aceSMarcelo H. Cerri         .tfm = ctx->fallback,
1418c755aceSMarcelo H. Cerri         .info = desc->info,
1428c755aceSMarcelo H. Cerri         .flags = desc->flags
1438c755aceSMarcelo H. Cerri     };
1448c755aceSMarcelo H. Cerri 
1458c755aceSMarcelo H. Cerri     if (in_interrupt()) {
1468c755aceSMarcelo H. Cerri         ret = crypto_blkcipher_decrypt(&fallback_desc, dst, src, nbytes);
1478c755aceSMarcelo H. Cerri     } else {
1485f76eea8SDavid Hildenbrand 	preempt_disable();
1498c755aceSMarcelo H. Cerri         pagefault_disable();
1508c755aceSMarcelo H. Cerri         enable_kernel_altivec();
1518c755aceSMarcelo H. Cerri 
1528c755aceSMarcelo H. Cerri 	blkcipher_walk_init(&walk, dst, src, nbytes);
1538c755aceSMarcelo H. Cerri         ret = blkcipher_walk_virt(desc, &walk);
1548c755aceSMarcelo H. Cerri         while ((nbytes = walk.nbytes)) {
1558c755aceSMarcelo H. Cerri 			aes_p8_cbc_encrypt(walk.src.virt.addr, walk.dst.virt.addr,
1568c755aceSMarcelo H. Cerri 				nbytes & AES_BLOCK_MASK, &ctx->dec_key, walk.iv, 0);
1578c755aceSMarcelo H. Cerri 			nbytes &= AES_BLOCK_SIZE - 1;
1588c755aceSMarcelo H. Cerri 			ret = blkcipher_walk_done(desc, &walk, nbytes);
1598c755aceSMarcelo H. Cerri 		}
1608c755aceSMarcelo H. Cerri 
1618c755aceSMarcelo H. Cerri         pagefault_enable();
1625f76eea8SDavid Hildenbrand 	preempt_enable();
1638c755aceSMarcelo H. Cerri     }
1648c755aceSMarcelo H. Cerri 
1658c755aceSMarcelo H. Cerri     return ret;
1668c755aceSMarcelo H. Cerri }
1678c755aceSMarcelo H. Cerri 
1688c755aceSMarcelo H. Cerri 
1698c755aceSMarcelo H. Cerri struct crypto_alg p8_aes_cbc_alg = {
1708c755aceSMarcelo H. Cerri     .cra_name = "cbc(aes)",
1718c755aceSMarcelo H. Cerri     .cra_driver_name = "p8_aes_cbc",
1728c755aceSMarcelo H. Cerri     .cra_module = THIS_MODULE,
1738c755aceSMarcelo H. Cerri     .cra_priority = 1000,
1748c755aceSMarcelo H. Cerri     .cra_type = &crypto_blkcipher_type,
1758c755aceSMarcelo H. Cerri     .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | CRYPTO_ALG_NEED_FALLBACK,
1768c755aceSMarcelo H. Cerri     .cra_alignmask = 0,
1778c755aceSMarcelo H. Cerri     .cra_blocksize = AES_BLOCK_SIZE,
1788c755aceSMarcelo H. Cerri     .cra_ctxsize = sizeof(struct p8_aes_cbc_ctx),
1798c755aceSMarcelo H. Cerri     .cra_init = p8_aes_cbc_init,
1808c755aceSMarcelo H. Cerri     .cra_exit = p8_aes_cbc_exit,
1818c755aceSMarcelo H. Cerri     .cra_blkcipher = {
1828c755aceSMarcelo H. Cerri         .ivsize = 0,
1838c755aceSMarcelo H. Cerri         .min_keysize = AES_MIN_KEY_SIZE,
1848c755aceSMarcelo H. Cerri         .max_keysize = AES_MAX_KEY_SIZE,
1858c755aceSMarcelo H. Cerri         .setkey = p8_aes_cbc_setkey,
1868c755aceSMarcelo H. Cerri         .encrypt = p8_aes_cbc_encrypt,
1878c755aceSMarcelo H. Cerri         .decrypt = p8_aes_cbc_decrypt,
1888c755aceSMarcelo H. Cerri     },
1898c755aceSMarcelo H. Cerri };
1908c755aceSMarcelo H. Cerri 
191