xref: /linux/arch/x86/lib/crc32.c (revision 15d90a5e5524532b7456a24f4626cf28c1629c4c)
135984c73SEric Biggers // SPDX-License-Identifier: GPL-2.0-only
235984c73SEric Biggers /*
335984c73SEric Biggers  * x86-optimized CRC32 functions
435984c73SEric Biggers  *
535984c73SEric Biggers  * Copyright (C) 2008 Intel Corporation
635984c73SEric Biggers  * Copyright 2012 Xyratex Technology Limited
735984c73SEric Biggers  * Copyright 2024 Google LLC
835984c73SEric Biggers  */
935984c73SEric Biggers 
1035984c73SEric Biggers #include <linux/crc32.h>
1135984c73SEric Biggers #include <linux/module.h>
1235984c73SEric Biggers #include "crc-pclmul-template.h"
1335984c73SEric Biggers 
1435984c73SEric Biggers static __ro_after_init DEFINE_STATIC_KEY_FALSE(have_crc32);
1535984c73SEric Biggers static __ro_after_init DEFINE_STATIC_KEY_FALSE(have_pclmulqdq);
1635984c73SEric Biggers 
1735984c73SEric Biggers DECLARE_CRC_PCLMUL_FUNCS(crc32_lsb, u32);
1835984c73SEric Biggers 
1935984c73SEric Biggers u32 crc32_le_arch(u32 crc, const u8 *p, size_t len)
2035984c73SEric Biggers {
2135984c73SEric Biggers 	CRC_PCLMUL(crc, p, len, crc32_lsb, crc32_lsb_0xedb88320_consts,
2235984c73SEric Biggers 		   have_pclmulqdq);
2335984c73SEric Biggers 	return crc32_le_base(crc, p, len);
2435984c73SEric Biggers }
2535984c73SEric Biggers EXPORT_SYMBOL(crc32_le_arch);
2635984c73SEric Biggers 
2735984c73SEric Biggers #ifdef CONFIG_X86_64
2835984c73SEric Biggers #define CRC32_INST "crc32q %1, %q0"
2935984c73SEric Biggers #else
3035984c73SEric Biggers #define CRC32_INST "crc32l %1, %0"
3135984c73SEric Biggers #endif
3235984c73SEric Biggers 
3335984c73SEric Biggers /*
3435984c73SEric Biggers  * Use carryless multiply version of crc32c when buffer size is >= 512 to
3535984c73SEric Biggers  * account for FPU state save/restore overhead.
3635984c73SEric Biggers  */
3735984c73SEric Biggers #define CRC32C_PCLMUL_BREAKEVEN	512
3835984c73SEric Biggers 
3935984c73SEric Biggers asmlinkage u32 crc32c_x86_3way(u32 crc, const u8 *buffer, size_t len);
4035984c73SEric Biggers 
4135984c73SEric Biggers u32 crc32c_arch(u32 crc, const u8 *p, size_t len)
4235984c73SEric Biggers {
4335984c73SEric Biggers 	size_t num_longs;
4435984c73SEric Biggers 
4535984c73SEric Biggers 	if (!static_branch_likely(&have_crc32))
4635984c73SEric Biggers 		return crc32c_base(crc, p, len);
4735984c73SEric Biggers 
4835984c73SEric Biggers 	if (IS_ENABLED(CONFIG_X86_64) && len >= CRC32C_PCLMUL_BREAKEVEN &&
4935984c73SEric Biggers 	    static_branch_likely(&have_pclmulqdq) && crypto_simd_usable()) {
5035984c73SEric Biggers 		kernel_fpu_begin();
5135984c73SEric Biggers 		crc = crc32c_x86_3way(crc, p, len);
5235984c73SEric Biggers 		kernel_fpu_end();
5335984c73SEric Biggers 		return crc;
5435984c73SEric Biggers 	}
5535984c73SEric Biggers 
5635984c73SEric Biggers 	for (num_longs = len / sizeof(unsigned long);
5735984c73SEric Biggers 	     num_longs != 0; num_longs--, p += sizeof(unsigned long))
5835984c73SEric Biggers 		asm(CRC32_INST : "+r" (crc) : ASM_INPUT_RM (*(unsigned long *)p));
5935984c73SEric Biggers 
6035984c73SEric Biggers 	if (sizeof(unsigned long) > 4 && (len & 4)) {
6135984c73SEric Biggers 		asm("crc32l %1, %0" : "+r" (crc) : ASM_INPUT_RM (*(u32 *)p));
6235984c73SEric Biggers 		p += 4;
6335984c73SEric Biggers 	}
6435984c73SEric Biggers 	if (len & 2) {
6535984c73SEric Biggers 		asm("crc32w %1, %0" : "+r" (crc) : ASM_INPUT_RM (*(u16 *)p));
6635984c73SEric Biggers 		p += 2;
6735984c73SEric Biggers 	}
6835984c73SEric Biggers 	if (len & 1)
6935984c73SEric Biggers 		asm("crc32b %1, %0" : "+r" (crc) : ASM_INPUT_RM (*p));
7035984c73SEric Biggers 
7135984c73SEric Biggers 	return crc;
7235984c73SEric Biggers }
7335984c73SEric Biggers EXPORT_SYMBOL(crc32c_arch);
7435984c73SEric Biggers 
7535984c73SEric Biggers u32 crc32_be_arch(u32 crc, const u8 *p, size_t len)
7635984c73SEric Biggers {
7735984c73SEric Biggers 	return crc32_be_base(crc, p, len);
7835984c73SEric Biggers }
7935984c73SEric Biggers EXPORT_SYMBOL(crc32_be_arch);
8035984c73SEric Biggers 
8135984c73SEric Biggers static int __init crc32_x86_init(void)
8235984c73SEric Biggers {
8335984c73SEric Biggers 	if (boot_cpu_has(X86_FEATURE_XMM4_2))
8435984c73SEric Biggers 		static_branch_enable(&have_crc32);
8535984c73SEric Biggers 	if (boot_cpu_has(X86_FEATURE_PCLMULQDQ)) {
8635984c73SEric Biggers 		static_branch_enable(&have_pclmulqdq);
8735984c73SEric Biggers 		INIT_CRC_PCLMUL(crc32_lsb);
8835984c73SEric Biggers 	}
8935984c73SEric Biggers 	return 0;
9035984c73SEric Biggers }
91*648c7fb1SEric Biggers subsys_initcall(crc32_x86_init);
9235984c73SEric Biggers 
9335984c73SEric Biggers static void __exit crc32_x86_exit(void)
9435984c73SEric Biggers {
9535984c73SEric Biggers }
9635984c73SEric Biggers module_exit(crc32_x86_exit);
9735984c73SEric Biggers 
9835984c73SEric Biggers u32 crc32_optimizations(void)
9935984c73SEric Biggers {
10035984c73SEric Biggers 	u32 optimizations = 0;
10135984c73SEric Biggers 
10235984c73SEric Biggers 	if (static_key_enabled(&have_crc32))
10335984c73SEric Biggers 		optimizations |= CRC32C_OPTIMIZATION;
10435984c73SEric Biggers 	if (static_key_enabled(&have_pclmulqdq))
10535984c73SEric Biggers 		optimizations |= CRC32_LE_OPTIMIZATION;
10635984c73SEric Biggers 	return optimizations;
10735984c73SEric Biggers }
10835984c73SEric Biggers EXPORT_SYMBOL(crc32_optimizations);
10935984c73SEric Biggers 
11035984c73SEric Biggers MODULE_DESCRIPTION("x86-optimized CRC32 functions");
11135984c73SEric Biggers MODULE_LICENSE("GPL");
112