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