162306a36Sopenharmony_ci/* GPL HEADER START 262306a36Sopenharmony_ci * 362306a36Sopenharmony_ci * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 662306a36Sopenharmony_ci * it under the terms of the GNU General Public License version 2 only, 762306a36Sopenharmony_ci * as published by the Free Software Foundation. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * This program is distributed in the hope that it will be useful, but 1062306a36Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of 1162306a36Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1262306a36Sopenharmony_ci * General Public License version 2 for more details (a copy is included 1362306a36Sopenharmony_ci * in the LICENSE file that accompanied this code). 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * You should have received a copy of the GNU General Public License 1662306a36Sopenharmony_ci * version 2 along with this program; If not, see http://www.gnu.org/licenses 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * Please visit http://www.xyratex.com/contact if you need additional 1962306a36Sopenharmony_ci * information or have any questions. 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * GPL HEADER END 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* 2562306a36Sopenharmony_ci * Copyright 2012 Xyratex Technology Limited 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * Wrappers for kernel crypto shash api to pclmulqdq crc32 implementation. 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_ci#include <linux/init.h> 3062306a36Sopenharmony_ci#include <linux/module.h> 3162306a36Sopenharmony_ci#include <linux/string.h> 3262306a36Sopenharmony_ci#include <linux/kernel.h> 3362306a36Sopenharmony_ci#include <linux/crc32.h> 3462306a36Sopenharmony_ci#include <crypto/internal/hash.h> 3562306a36Sopenharmony_ci#include <crypto/internal/simd.h> 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#include <asm/cpufeatures.h> 3862306a36Sopenharmony_ci#include <asm/cpu_device_id.h> 3962306a36Sopenharmony_ci#include <asm/simd.h> 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#define CHKSUM_BLOCK_SIZE 1 4262306a36Sopenharmony_ci#define CHKSUM_DIGEST_SIZE 4 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define PCLMUL_MIN_LEN 64L /* minimum size of buffer 4562306a36Sopenharmony_ci * for crc32_pclmul_le_16 */ 4662306a36Sopenharmony_ci#define SCALE_F 16L /* size of xmm register */ 4762306a36Sopenharmony_ci#define SCALE_F_MASK (SCALE_F - 1) 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ciu32 crc32_pclmul_le_16(unsigned char const *buffer, size_t len, u32 crc32); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic u32 __attribute__((pure)) 5262306a36Sopenharmony_ci crc32_pclmul_le(u32 crc, unsigned char const *p, size_t len) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci unsigned int iquotient; 5562306a36Sopenharmony_ci unsigned int iremainder; 5662306a36Sopenharmony_ci unsigned int prealign; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci if (len < PCLMUL_MIN_LEN + SCALE_F_MASK || !crypto_simd_usable()) 5962306a36Sopenharmony_ci return crc32_le(crc, p, len); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci if ((long)p & SCALE_F_MASK) { 6262306a36Sopenharmony_ci /* align p to 16 byte */ 6362306a36Sopenharmony_ci prealign = SCALE_F - ((long)p & SCALE_F_MASK); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci crc = crc32_le(crc, p, prealign); 6662306a36Sopenharmony_ci len -= prealign; 6762306a36Sopenharmony_ci p = (unsigned char *)(((unsigned long)p + SCALE_F_MASK) & 6862306a36Sopenharmony_ci ~SCALE_F_MASK); 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci iquotient = len & (~SCALE_F_MASK); 7162306a36Sopenharmony_ci iremainder = len & SCALE_F_MASK; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci kernel_fpu_begin(); 7462306a36Sopenharmony_ci crc = crc32_pclmul_le_16(p, iquotient, crc); 7562306a36Sopenharmony_ci kernel_fpu_end(); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci if (iremainder) 7862306a36Sopenharmony_ci crc = crc32_le(crc, p + iquotient, iremainder); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci return crc; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic int crc32_pclmul_cra_init(struct crypto_tfm *tfm) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci u32 *key = crypto_tfm_ctx(tfm); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci *key = 0; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci return 0; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic int crc32_pclmul_setkey(struct crypto_shash *hash, const u8 *key, 9362306a36Sopenharmony_ci unsigned int keylen) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci u32 *mctx = crypto_shash_ctx(hash); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci if (keylen != sizeof(u32)) 9862306a36Sopenharmony_ci return -EINVAL; 9962306a36Sopenharmony_ci *mctx = le32_to_cpup((__le32 *)key); 10062306a36Sopenharmony_ci return 0; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic int crc32_pclmul_init(struct shash_desc *desc) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci u32 *mctx = crypto_shash_ctx(desc->tfm); 10662306a36Sopenharmony_ci u32 *crcp = shash_desc_ctx(desc); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci *crcp = *mctx; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci return 0; 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic int crc32_pclmul_update(struct shash_desc *desc, const u8 *data, 11462306a36Sopenharmony_ci unsigned int len) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci u32 *crcp = shash_desc_ctx(desc); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci *crcp = crc32_pclmul_le(*crcp, data, len); 11962306a36Sopenharmony_ci return 0; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci/* No final XOR 0xFFFFFFFF, like crc32_le */ 12362306a36Sopenharmony_cistatic int __crc32_pclmul_finup(u32 *crcp, const u8 *data, unsigned int len, 12462306a36Sopenharmony_ci u8 *out) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci *(__le32 *)out = cpu_to_le32(crc32_pclmul_le(*crcp, data, len)); 12762306a36Sopenharmony_ci return 0; 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic int crc32_pclmul_finup(struct shash_desc *desc, const u8 *data, 13162306a36Sopenharmony_ci unsigned int len, u8 *out) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci return __crc32_pclmul_finup(shash_desc_ctx(desc), data, len, out); 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic int crc32_pclmul_final(struct shash_desc *desc, u8 *out) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci u32 *crcp = shash_desc_ctx(desc); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci *(__le32 *)out = cpu_to_le32p(crcp); 14162306a36Sopenharmony_ci return 0; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic int crc32_pclmul_digest(struct shash_desc *desc, const u8 *data, 14562306a36Sopenharmony_ci unsigned int len, u8 *out) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci return __crc32_pclmul_finup(crypto_shash_ctx(desc->tfm), data, len, 14862306a36Sopenharmony_ci out); 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic struct shash_alg alg = { 15262306a36Sopenharmony_ci .setkey = crc32_pclmul_setkey, 15362306a36Sopenharmony_ci .init = crc32_pclmul_init, 15462306a36Sopenharmony_ci .update = crc32_pclmul_update, 15562306a36Sopenharmony_ci .final = crc32_pclmul_final, 15662306a36Sopenharmony_ci .finup = crc32_pclmul_finup, 15762306a36Sopenharmony_ci .digest = crc32_pclmul_digest, 15862306a36Sopenharmony_ci .descsize = sizeof(u32), 15962306a36Sopenharmony_ci .digestsize = CHKSUM_DIGEST_SIZE, 16062306a36Sopenharmony_ci .base = { 16162306a36Sopenharmony_ci .cra_name = "crc32", 16262306a36Sopenharmony_ci .cra_driver_name = "crc32-pclmul", 16362306a36Sopenharmony_ci .cra_priority = 200, 16462306a36Sopenharmony_ci .cra_flags = CRYPTO_ALG_OPTIONAL_KEY, 16562306a36Sopenharmony_ci .cra_blocksize = CHKSUM_BLOCK_SIZE, 16662306a36Sopenharmony_ci .cra_ctxsize = sizeof(u32), 16762306a36Sopenharmony_ci .cra_module = THIS_MODULE, 16862306a36Sopenharmony_ci .cra_init = crc32_pclmul_cra_init, 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci}; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic const struct x86_cpu_id crc32pclmul_cpu_id[] = { 17362306a36Sopenharmony_ci X86_MATCH_FEATURE(X86_FEATURE_PCLMULQDQ, NULL), 17462306a36Sopenharmony_ci {} 17562306a36Sopenharmony_ci}; 17662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(x86cpu, crc32pclmul_cpu_id); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic int __init crc32_pclmul_mod_init(void) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci if (!x86_match_cpu(crc32pclmul_cpu_id)) { 18362306a36Sopenharmony_ci pr_info("PCLMULQDQ-NI instructions are not detected.\n"); 18462306a36Sopenharmony_ci return -ENODEV; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci return crypto_register_shash(&alg); 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic void __exit crc32_pclmul_mod_fini(void) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci crypto_unregister_shash(&alg); 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cimodule_init(crc32_pclmul_mod_init); 19562306a36Sopenharmony_cimodule_exit(crc32_pclmul_mod_fini); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ciMODULE_AUTHOR("Alexander Boyko <alexander_boyko@xyratex.com>"); 19862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("crc32"); 20162306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("crc32-pclmul"); 202