xref: /openbmc/linux/arch/x86/crypto/crc32c-intel_glue.c (revision cbecf716ca618fd44feda6bd9a64a8179d031fc5)
1a61127c2SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
235b80920STim Chen /*
335b80920STim Chen  * Using hardware provided CRC32 instruction to accelerate the CRC32 disposal.
435b80920STim Chen  * CRC32C polynomial:0x1EDC6F41(BE)/0x82F63B78(LE)
535b80920STim Chen  * CRC32 is a new instruction in Intel SSE4.2, the reference can be found at:
635b80920STim Chen  * http://www.intel.com/products/processor/manuals/
735b80920STim Chen  * Intel(R) 64 and IA-32 Architectures Software Developer's Manual
835b80920STim Chen  * Volume 2A: Instruction Set Reference, A-M
935b80920STim Chen  *
1035b80920STim Chen  * Copyright (C) 2008 Intel Corporation
1135b80920STim Chen  * Authors: Austin Zhang <austin_zhang@linux.intel.com>
1235b80920STim Chen  *          Kent Liu <kent.liu@intel.com>
1335b80920STim Chen  */
1435b80920STim Chen #include <linux/init.h>
1535b80920STim Chen #include <linux/module.h>
1635b80920STim Chen #include <linux/string.h>
1735b80920STim Chen #include <linux/kernel.h>
1835b80920STim Chen #include <crypto/internal/hash.h>
19f2abe0d7SEric Biggers #include <crypto/internal/simd.h>
2035b80920STim Chen 
21cd4d09ecSBorislav Petkov #include <asm/cpufeatures.h>
2235b80920STim Chen #include <asm/cpu_device_id.h>
23f2abe0d7SEric Biggers #include <asm/simd.h>
2435b80920STim Chen 
2535b80920STim Chen #define CHKSUM_BLOCK_SIZE	1
2635b80920STim Chen #define CHKSUM_DIGEST_SIZE	4
2735b80920STim Chen 
2835b80920STim Chen #define SCALE_F	sizeof(unsigned long)
2935b80920STim Chen 
3035b80920STim Chen #ifdef CONFIG_X86_64
31*3a95887eSUros Bizjak #define CRC32_INST "crc32q %1, %q0"
3235b80920STim Chen #else
33*3a95887eSUros Bizjak #define CRC32_INST "crc32l %1, %0"
3435b80920STim Chen #endif
3535b80920STim Chen 
366a8ce1efSTim Chen #ifdef CONFIG_X86_64
376a8ce1efSTim Chen /*
386a8ce1efSTim Chen  * use carryless multiply version of crc32c when buffer
39c592b573SAndy Lutomirski  * size is >= 512 to account
406a8ce1efSTim Chen  * for fpu state save/restore overhead.
416a8ce1efSTim Chen  */
42c592b573SAndy Lutomirski #define CRC32C_PCL_BREAKEVEN	512
436a8ce1efSTim Chen 
446a8ce1efSTim Chen asmlinkage unsigned int crc_pcl(const u8 *buffer, int len,
456a8ce1efSTim Chen 				unsigned int crc_init);
466a8ce1efSTim Chen #endif /* CONFIG_X86_64 */
476a8ce1efSTim Chen 
crc32c_intel_le_hw_byte(u32 crc,unsigned char const * data,size_t length)4835b80920STim Chen static u32 crc32c_intel_le_hw_byte(u32 crc, unsigned char const *data, size_t length)
4935b80920STim Chen {
5035b80920STim Chen 	while (length--) {
51*3a95887eSUros Bizjak 		asm("crc32b %1, %0"
52*3a95887eSUros Bizjak 		    : "+r" (crc) : "rm" (*data));
5335b80920STim Chen 		data++;
5435b80920STim Chen 	}
5535b80920STim Chen 
5635b80920STim Chen 	return crc;
5735b80920STim Chen }
5835b80920STim Chen 
crc32c_intel_le_hw(u32 crc,unsigned char const * p,size_t len)5935b80920STim Chen static u32 __pure crc32c_intel_le_hw(u32 crc, unsigned char const *p, size_t len)
6035b80920STim Chen {
6135b80920STim Chen 	unsigned int iquotient = len / SCALE_F;
6235b80920STim Chen 	unsigned int iremainder = len % SCALE_F;
6335b80920STim Chen 	unsigned long *ptmp = (unsigned long *)p;
6435b80920STim Chen 
6535b80920STim Chen 	while (iquotient--) {
66*3a95887eSUros Bizjak 		asm(CRC32_INST
67*3a95887eSUros Bizjak 		    : "+r" (crc) : "rm" (*ptmp));
6835b80920STim Chen 		ptmp++;
6935b80920STim Chen 	}
7035b80920STim Chen 
7135b80920STim Chen 	if (iremainder)
7235b80920STim Chen 		crc = crc32c_intel_le_hw_byte(crc, (unsigned char *)ptmp,
7335b80920STim Chen 				 iremainder);
7435b80920STim Chen 
7535b80920STim Chen 	return crc;
7635b80920STim Chen }
7735b80920STim Chen 
7835b80920STim Chen /*
7935b80920STim Chen  * Setting the seed allows arbitrary accumulators and flexible XOR policy
8035b80920STim Chen  * If your algorithm starts with ~0, then XOR with ~0 before you set
8135b80920STim Chen  * the seed.
8235b80920STim Chen  */
crc32c_intel_setkey(struct crypto_shash * hash,const u8 * key,unsigned int keylen)8335b80920STim Chen static int crc32c_intel_setkey(struct crypto_shash *hash, const u8 *key,
8435b80920STim Chen 			unsigned int keylen)
8535b80920STim Chen {
8635b80920STim Chen 	u32 *mctx = crypto_shash_ctx(hash);
8735b80920STim Chen 
88674f368aSEric Biggers 	if (keylen != sizeof(u32))
8935b80920STim Chen 		return -EINVAL;
9035b80920STim Chen 	*mctx = le32_to_cpup((__le32 *)key);
9135b80920STim Chen 	return 0;
9235b80920STim Chen }
9335b80920STim Chen 
crc32c_intel_init(struct shash_desc * desc)9435b80920STim Chen static int crc32c_intel_init(struct shash_desc *desc)
9535b80920STim Chen {
9635b80920STim Chen 	u32 *mctx = crypto_shash_ctx(desc->tfm);
9735b80920STim Chen 	u32 *crcp = shash_desc_ctx(desc);
9835b80920STim Chen 
9935b80920STim Chen 	*crcp = *mctx;
10035b80920STim Chen 
10135b80920STim Chen 	return 0;
10235b80920STim Chen }
10335b80920STim Chen 
crc32c_intel_update(struct shash_desc * desc,const u8 * data,unsigned int len)10435b80920STim Chen static int crc32c_intel_update(struct shash_desc *desc, const u8 *data,
10535b80920STim Chen 			       unsigned int len)
10635b80920STim Chen {
10735b80920STim Chen 	u32 *crcp = shash_desc_ctx(desc);
10835b80920STim Chen 
10935b80920STim Chen 	*crcp = crc32c_intel_le_hw(*crcp, data, len);
11035b80920STim Chen 	return 0;
11135b80920STim Chen }
11235b80920STim Chen 
__crc32c_intel_finup(u32 * crcp,const u8 * data,unsigned int len,u8 * out)11335b80920STim Chen static int __crc32c_intel_finup(u32 *crcp, const u8 *data, unsigned int len,
11435b80920STim Chen 				u8 *out)
11535b80920STim Chen {
11635b80920STim Chen 	*(__le32 *)out = ~cpu_to_le32(crc32c_intel_le_hw(*crcp, data, len));
11735b80920STim Chen 	return 0;
11835b80920STim Chen }
11935b80920STim Chen 
crc32c_intel_finup(struct shash_desc * desc,const u8 * data,unsigned int len,u8 * out)12035b80920STim Chen static int crc32c_intel_finup(struct shash_desc *desc, const u8 *data,
12135b80920STim Chen 			      unsigned int len, u8 *out)
12235b80920STim Chen {
12335b80920STim Chen 	return __crc32c_intel_finup(shash_desc_ctx(desc), data, len, out);
12435b80920STim Chen }
12535b80920STim Chen 
crc32c_intel_final(struct shash_desc * desc,u8 * out)12635b80920STim Chen static int crc32c_intel_final(struct shash_desc *desc, u8 *out)
12735b80920STim Chen {
12835b80920STim Chen 	u32 *crcp = shash_desc_ctx(desc);
12935b80920STim Chen 
13035b80920STim Chen 	*(__le32 *)out = ~cpu_to_le32p(crcp);
13135b80920STim Chen 	return 0;
13235b80920STim Chen }
13335b80920STim Chen 
crc32c_intel_digest(struct shash_desc * desc,const u8 * data,unsigned int len,u8 * out)13435b80920STim Chen static int crc32c_intel_digest(struct shash_desc *desc, const u8 *data,
13535b80920STim Chen 			       unsigned int len, u8 *out)
13635b80920STim Chen {
13735b80920STim Chen 	return __crc32c_intel_finup(crypto_shash_ctx(desc->tfm), data, len,
13835b80920STim Chen 				    out);
13935b80920STim Chen }
14035b80920STim Chen 
crc32c_intel_cra_init(struct crypto_tfm * tfm)14135b80920STim Chen static int crc32c_intel_cra_init(struct crypto_tfm *tfm)
14235b80920STim Chen {
14335b80920STim Chen 	u32 *key = crypto_tfm_ctx(tfm);
14435b80920STim Chen 
14535b80920STim Chen 	*key = ~0;
14635b80920STim Chen 
14735b80920STim Chen 	return 0;
14835b80920STim Chen }
14935b80920STim Chen 
1506a8ce1efSTim Chen #ifdef CONFIG_X86_64
crc32c_pcl_intel_update(struct shash_desc * desc,const u8 * data,unsigned int len)1516a8ce1efSTim Chen static int crc32c_pcl_intel_update(struct shash_desc *desc, const u8 *data,
1526a8ce1efSTim Chen 			       unsigned int len)
1536a8ce1efSTim Chen {
1546a8ce1efSTim Chen 	u32 *crcp = shash_desc_ctx(desc);
1556a8ce1efSTim Chen 
1566a8ce1efSTim Chen 	/*
1576a8ce1efSTim Chen 	 * use faster PCL version if datasize is large enough to
1586a8ce1efSTim Chen 	 * overcome kernel fpu state save/restore overhead
1596a8ce1efSTim Chen 	 */
160f2abe0d7SEric Biggers 	if (len >= CRC32C_PCL_BREAKEVEN && crypto_simd_usable()) {
1616a8ce1efSTim Chen 		kernel_fpu_begin();
1626a8ce1efSTim Chen 		*crcp = crc_pcl(data, len, *crcp);
1636a8ce1efSTim Chen 		kernel_fpu_end();
1646a8ce1efSTim Chen 	} else
1656a8ce1efSTim Chen 		*crcp = crc32c_intel_le_hw(*crcp, data, len);
1666a8ce1efSTim Chen 	return 0;
1676a8ce1efSTim Chen }
1686a8ce1efSTim Chen 
__crc32c_pcl_intel_finup(u32 * crcp,const u8 * data,unsigned int len,u8 * out)1696a8ce1efSTim Chen static int __crc32c_pcl_intel_finup(u32 *crcp, const u8 *data, unsigned int len,
1706a8ce1efSTim Chen 				u8 *out)
1716a8ce1efSTim Chen {
172f2abe0d7SEric Biggers 	if (len >= CRC32C_PCL_BREAKEVEN && crypto_simd_usable()) {
1736a8ce1efSTim Chen 		kernel_fpu_begin();
1746a8ce1efSTim Chen 		*(__le32 *)out = ~cpu_to_le32(crc_pcl(data, len, *crcp));
1756a8ce1efSTim Chen 		kernel_fpu_end();
1766a8ce1efSTim Chen 	} else
1776a8ce1efSTim Chen 		*(__le32 *)out =
1786a8ce1efSTim Chen 			~cpu_to_le32(crc32c_intel_le_hw(*crcp, data, len));
1796a8ce1efSTim Chen 	return 0;
1806a8ce1efSTim Chen }
1816a8ce1efSTim Chen 
crc32c_pcl_intel_finup(struct shash_desc * desc,const u8 * data,unsigned int len,u8 * out)1826a8ce1efSTim Chen static int crc32c_pcl_intel_finup(struct shash_desc *desc, const u8 *data,
1836a8ce1efSTim Chen 			      unsigned int len, u8 *out)
1846a8ce1efSTim Chen {
1856a8ce1efSTim Chen 	return __crc32c_pcl_intel_finup(shash_desc_ctx(desc), data, len, out);
1866a8ce1efSTim Chen }
1876a8ce1efSTim Chen 
crc32c_pcl_intel_digest(struct shash_desc * desc,const u8 * data,unsigned int len,u8 * out)1886a8ce1efSTim Chen static int crc32c_pcl_intel_digest(struct shash_desc *desc, const u8 *data,
1896a8ce1efSTim Chen 			       unsigned int len, u8 *out)
1906a8ce1efSTim Chen {
1916a8ce1efSTim Chen 	return __crc32c_pcl_intel_finup(crypto_shash_ctx(desc->tfm), data, len,
1926a8ce1efSTim Chen 				    out);
1936a8ce1efSTim Chen }
1946a8ce1efSTim Chen #endif /* CONFIG_X86_64 */
1956a8ce1efSTim Chen 
19635b80920STim Chen static struct shash_alg alg = {
19735b80920STim Chen 	.setkey			=	crc32c_intel_setkey,
19835b80920STim Chen 	.init			=	crc32c_intel_init,
19935b80920STim Chen 	.update			=	crc32c_intel_update,
20035b80920STim Chen 	.final			=	crc32c_intel_final,
20135b80920STim Chen 	.finup			=	crc32c_intel_finup,
20235b80920STim Chen 	.digest			=	crc32c_intel_digest,
20335b80920STim Chen 	.descsize		=	sizeof(u32),
20435b80920STim Chen 	.digestsize		=	CHKSUM_DIGEST_SIZE,
20535b80920STim Chen 	.base			=	{
20635b80920STim Chen 		.cra_name		=	"crc32c",
20735b80920STim Chen 		.cra_driver_name	=	"crc32c-intel",
20835b80920STim Chen 		.cra_priority		=	200,
209a208fa8fSEric Biggers 		.cra_flags		=	CRYPTO_ALG_OPTIONAL_KEY,
21035b80920STim Chen 		.cra_blocksize		=	CHKSUM_BLOCK_SIZE,
21135b80920STim Chen 		.cra_ctxsize		=	sizeof(u32),
21235b80920STim Chen 		.cra_module		=	THIS_MODULE,
21335b80920STim Chen 		.cra_init		=	crc32c_intel_cra_init,
21435b80920STim Chen 	}
21535b80920STim Chen };
21635b80920STim Chen 
21735b80920STim Chen static const struct x86_cpu_id crc32c_cpu_id[] = {
218f30cfacaSThomas Gleixner 	X86_MATCH_FEATURE(X86_FEATURE_XMM4_2, NULL),
21935b80920STim Chen 	{}
22035b80920STim Chen };
22135b80920STim Chen MODULE_DEVICE_TABLE(x86cpu, crc32c_cpu_id);
22235b80920STim Chen 
crc32c_intel_mod_init(void)22335b80920STim Chen static int __init crc32c_intel_mod_init(void)
22435b80920STim Chen {
22535b80920STim Chen 	if (!x86_match_cpu(crc32c_cpu_id))
22635b80920STim Chen 		return -ENODEV;
2276a8ce1efSTim Chen #ifdef CONFIG_X86_64
228362f924bSBorislav Petkov 	if (boot_cpu_has(X86_FEATURE_PCLMULQDQ)) {
2296a8ce1efSTim Chen 		alg.update = crc32c_pcl_intel_update;
2306a8ce1efSTim Chen 		alg.finup = crc32c_pcl_intel_finup;
2316a8ce1efSTim Chen 		alg.digest = crc32c_pcl_intel_digest;
2326a8ce1efSTim Chen 	}
2336a8ce1efSTim Chen #endif
23435b80920STim Chen 	return crypto_register_shash(&alg);
23535b80920STim Chen }
23635b80920STim Chen 
crc32c_intel_mod_fini(void)23735b80920STim Chen static void __exit crc32c_intel_mod_fini(void)
23835b80920STim Chen {
23935b80920STim Chen 	crypto_unregister_shash(&alg);
24035b80920STim Chen }
24135b80920STim Chen 
24235b80920STim Chen module_init(crc32c_intel_mod_init);
24335b80920STim Chen module_exit(crc32c_intel_mod_fini);
24435b80920STim Chen 
24535b80920STim Chen MODULE_AUTHOR("Austin Zhang <austin.zhang@intel.com>, Kent Liu <kent.liu@intel.com>");
24635b80920STim Chen MODULE_DESCRIPTION("CRC32c (Castagnoli) optimization using Intel Hardware.");
24735b80920STim Chen MODULE_LICENSE("GPL");
24835b80920STim Chen 
2495d26a105SKees Cook MODULE_ALIAS_CRYPTO("crc32c");
2505d26a105SKees Cook MODULE_ALIAS_CRYPTO("crc32c-intel");
251