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