1 /* 2 * Calculate a CRC T10-DIF with vpmsum acceleration 3 * 4 * Copyright 2017, Daniel Axtens, IBM Corporation. 5 * [based on crc32c-vpmsum_glue.c] 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License 9 * as published by the Free Software Foundation; either version 2 10 * of the License, or (at your option) any later version. 11 */ 12 13 #include <linux/crc-t10dif.h> 14 #include <crypto/internal/hash.h> 15 #include <crypto/internal/simd.h> 16 #include <linux/init.h> 17 #include <linux/module.h> 18 #include <linux/string.h> 19 #include <linux/kernel.h> 20 #include <linux/cpufeature.h> 21 #include <asm/simd.h> 22 #include <asm/switch_to.h> 23 24 #define VMX_ALIGN 16 25 #define VMX_ALIGN_MASK (VMX_ALIGN-1) 26 27 #define VECTOR_BREAKPOINT 64 28 29 u32 __crct10dif_vpmsum(u32 crc, unsigned char const *p, size_t len); 30 31 static u16 crct10dif_vpmsum(u16 crci, unsigned char const *p, size_t len) 32 { 33 unsigned int prealign; 34 unsigned int tail; 35 u32 crc = crci; 36 37 if (len < (VECTOR_BREAKPOINT + VMX_ALIGN) || !crypto_simd_usable()) 38 return crc_t10dif_generic(crc, p, len); 39 40 if ((unsigned long)p & VMX_ALIGN_MASK) { 41 prealign = VMX_ALIGN - ((unsigned long)p & VMX_ALIGN_MASK); 42 crc = crc_t10dif_generic(crc, p, prealign); 43 len -= prealign; 44 p += prealign; 45 } 46 47 if (len & ~VMX_ALIGN_MASK) { 48 crc <<= 16; 49 preempt_disable(); 50 pagefault_disable(); 51 enable_kernel_altivec(); 52 crc = __crct10dif_vpmsum(crc, p, len & ~VMX_ALIGN_MASK); 53 disable_kernel_altivec(); 54 pagefault_enable(); 55 preempt_enable(); 56 crc >>= 16; 57 } 58 59 tail = len & VMX_ALIGN_MASK; 60 if (tail) { 61 p += len & ~VMX_ALIGN_MASK; 62 crc = crc_t10dif_generic(crc, p, tail); 63 } 64 65 return crc & 0xffff; 66 } 67 68 static int crct10dif_vpmsum_init(struct shash_desc *desc) 69 { 70 u16 *crc = shash_desc_ctx(desc); 71 72 *crc = 0; 73 return 0; 74 } 75 76 static int crct10dif_vpmsum_update(struct shash_desc *desc, const u8 *data, 77 unsigned int length) 78 { 79 u16 *crc = shash_desc_ctx(desc); 80 81 *crc = crct10dif_vpmsum(*crc, data, length); 82 83 return 0; 84 } 85 86 87 static int crct10dif_vpmsum_final(struct shash_desc *desc, u8 *out) 88 { 89 u16 *crcp = shash_desc_ctx(desc); 90 91 *(u16 *)out = *crcp; 92 return 0; 93 } 94 95 static struct shash_alg alg = { 96 .init = crct10dif_vpmsum_init, 97 .update = crct10dif_vpmsum_update, 98 .final = crct10dif_vpmsum_final, 99 .descsize = CRC_T10DIF_DIGEST_SIZE, 100 .digestsize = CRC_T10DIF_DIGEST_SIZE, 101 .base = { 102 .cra_name = "crct10dif", 103 .cra_driver_name = "crct10dif-vpmsum", 104 .cra_priority = 200, 105 .cra_blocksize = CRC_T10DIF_BLOCK_SIZE, 106 .cra_module = THIS_MODULE, 107 } 108 }; 109 110 static int __init crct10dif_vpmsum_mod_init(void) 111 { 112 if (!cpu_has_feature(CPU_FTR_ARCH_207S)) 113 return -ENODEV; 114 115 return crypto_register_shash(&alg); 116 } 117 118 static void __exit crct10dif_vpmsum_mod_fini(void) 119 { 120 crypto_unregister_shash(&alg); 121 } 122 123 module_cpu_feature_match(PPC_MODULE_FEATURE_VEC_CRYPTO, crct10dif_vpmsum_mod_init); 124 module_exit(crct10dif_vpmsum_mod_fini); 125 126 MODULE_AUTHOR("Daniel Axtens <dja@axtens.net>"); 127 MODULE_DESCRIPTION("CRCT10DIF using vector polynomial multiply-sum instructions"); 128 MODULE_LICENSE("GPL"); 129 MODULE_ALIAS_CRYPTO("crct10dif"); 130 MODULE_ALIAS_CRYPTO("crct10dif-vpmsum"); 131