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