xref: /linux/arch/arm/lib/crc32.c (revision 15d90a5e5524532b7456a24f4626cf28c1629c4c)
16cc25e4bSEric Biggers // SPDX-License-Identifier: GPL-2.0-only
26cc25e4bSEric Biggers /*
36cc25e4bSEric Biggers  * Accelerated CRC32(C) using ARM CRC, NEON and Crypto Extensions instructions
46cc25e4bSEric Biggers  *
56cc25e4bSEric Biggers  * Copyright (C) 2016 Linaro Ltd <ard.biesheuvel@linaro.org>
66cc25e4bSEric Biggers  */
76cc25e4bSEric Biggers 
86cc25e4bSEric Biggers #include <linux/cpufeature.h>
96cc25e4bSEric Biggers #include <linux/crc32.h>
106cc25e4bSEric Biggers #include <linux/init.h>
116cc25e4bSEric Biggers #include <linux/kernel.h>
126cc25e4bSEric Biggers #include <linux/module.h>
136cc25e4bSEric Biggers #include <linux/string.h>
146cc25e4bSEric Biggers 
156cc25e4bSEric Biggers #include <crypto/internal/simd.h>
166cc25e4bSEric Biggers 
176cc25e4bSEric Biggers #include <asm/hwcap.h>
186cc25e4bSEric Biggers #include <asm/neon.h>
196cc25e4bSEric Biggers #include <asm/simd.h>
206cc25e4bSEric Biggers 
216cc25e4bSEric Biggers static __ro_after_init DEFINE_STATIC_KEY_FALSE(have_crc32);
226cc25e4bSEric Biggers static __ro_after_init DEFINE_STATIC_KEY_FALSE(have_pmull);
236cc25e4bSEric Biggers 
246cc25e4bSEric Biggers #define PMULL_MIN_LEN	64	/* min size of buffer for pmull functions */
256cc25e4bSEric Biggers 
266cc25e4bSEric Biggers asmlinkage u32 crc32_pmull_le(const u8 buf[], u32 len, u32 init_crc);
276cc25e4bSEric Biggers asmlinkage u32 crc32_armv8_le(u32 init_crc, const u8 buf[], u32 len);
286cc25e4bSEric Biggers 
296cc25e4bSEric Biggers asmlinkage u32 crc32c_pmull_le(const u8 buf[], u32 len, u32 init_crc);
306cc25e4bSEric Biggers asmlinkage u32 crc32c_armv8_le(u32 init_crc, const u8 buf[], u32 len);
316cc25e4bSEric Biggers 
326cc25e4bSEric Biggers static u32 crc32_le_scalar(u32 crc, const u8 *p, size_t len)
336cc25e4bSEric Biggers {
346cc25e4bSEric Biggers 	if (static_branch_likely(&have_crc32))
356cc25e4bSEric Biggers 		return crc32_armv8_le(crc, p, len);
366cc25e4bSEric Biggers 	return crc32_le_base(crc, p, len);
376cc25e4bSEric Biggers }
386cc25e4bSEric Biggers 
396cc25e4bSEric Biggers u32 crc32_le_arch(u32 crc, const u8 *p, size_t len)
406cc25e4bSEric Biggers {
416cc25e4bSEric Biggers 	if (len >= PMULL_MIN_LEN + 15 &&
426cc25e4bSEric Biggers 	    static_branch_likely(&have_pmull) && crypto_simd_usable()) {
436cc25e4bSEric Biggers 		size_t n = -(uintptr_t)p & 15;
446cc25e4bSEric Biggers 
456cc25e4bSEric Biggers 		/* align p to 16-byte boundary */
466cc25e4bSEric Biggers 		if (n) {
476cc25e4bSEric Biggers 			crc = crc32_le_scalar(crc, p, n);
486cc25e4bSEric Biggers 			p += n;
496cc25e4bSEric Biggers 			len -= n;
506cc25e4bSEric Biggers 		}
516cc25e4bSEric Biggers 		n = round_down(len, 16);
526cc25e4bSEric Biggers 		kernel_neon_begin();
536cc25e4bSEric Biggers 		crc = crc32_pmull_le(p, n, crc);
546cc25e4bSEric Biggers 		kernel_neon_end();
556cc25e4bSEric Biggers 		p += n;
566cc25e4bSEric Biggers 		len -= n;
576cc25e4bSEric Biggers 	}
586cc25e4bSEric Biggers 	return crc32_le_scalar(crc, p, len);
596cc25e4bSEric Biggers }
606cc25e4bSEric Biggers EXPORT_SYMBOL(crc32_le_arch);
616cc25e4bSEric Biggers 
626cc25e4bSEric Biggers static u32 crc32c_scalar(u32 crc, const u8 *p, size_t len)
636cc25e4bSEric Biggers {
646cc25e4bSEric Biggers 	if (static_branch_likely(&have_crc32))
656cc25e4bSEric Biggers 		return crc32c_armv8_le(crc, p, len);
666cc25e4bSEric Biggers 	return crc32c_base(crc, p, len);
676cc25e4bSEric Biggers }
686cc25e4bSEric Biggers 
696cc25e4bSEric Biggers u32 crc32c_arch(u32 crc, const u8 *p, size_t len)
706cc25e4bSEric Biggers {
716cc25e4bSEric Biggers 	if (len >= PMULL_MIN_LEN + 15 &&
726cc25e4bSEric Biggers 	    static_branch_likely(&have_pmull) && crypto_simd_usable()) {
736cc25e4bSEric Biggers 		size_t n = -(uintptr_t)p & 15;
746cc25e4bSEric Biggers 
756cc25e4bSEric Biggers 		/* align p to 16-byte boundary */
766cc25e4bSEric Biggers 		if (n) {
776cc25e4bSEric Biggers 			crc = crc32c_scalar(crc, p, n);
786cc25e4bSEric Biggers 			p += n;
796cc25e4bSEric Biggers 			len -= n;
806cc25e4bSEric Biggers 		}
816cc25e4bSEric Biggers 		n = round_down(len, 16);
826cc25e4bSEric Biggers 		kernel_neon_begin();
836cc25e4bSEric Biggers 		crc = crc32c_pmull_le(p, n, crc);
846cc25e4bSEric Biggers 		kernel_neon_end();
856cc25e4bSEric Biggers 		p += n;
866cc25e4bSEric Biggers 		len -= n;
876cc25e4bSEric Biggers 	}
886cc25e4bSEric Biggers 	return crc32c_scalar(crc, p, len);
896cc25e4bSEric Biggers }
906cc25e4bSEric Biggers EXPORT_SYMBOL(crc32c_arch);
916cc25e4bSEric Biggers 
926cc25e4bSEric Biggers u32 crc32_be_arch(u32 crc, const u8 *p, size_t len)
936cc25e4bSEric Biggers {
946cc25e4bSEric Biggers 	return crc32_be_base(crc, p, len);
956cc25e4bSEric Biggers }
966cc25e4bSEric Biggers EXPORT_SYMBOL(crc32_be_arch);
976cc25e4bSEric Biggers 
986cc25e4bSEric Biggers static int __init crc32_arm_init(void)
996cc25e4bSEric Biggers {
1006cc25e4bSEric Biggers 	if (elf_hwcap2 & HWCAP2_CRC32)
1016cc25e4bSEric Biggers 		static_branch_enable(&have_crc32);
1026cc25e4bSEric Biggers 	if (elf_hwcap2 & HWCAP2_PMULL)
1036cc25e4bSEric Biggers 		static_branch_enable(&have_pmull);
1046cc25e4bSEric Biggers 	return 0;
1056cc25e4bSEric Biggers }
106*648c7fb1SEric Biggers subsys_initcall(crc32_arm_init);
1076cc25e4bSEric Biggers 
1086cc25e4bSEric Biggers static void __exit crc32_arm_exit(void)
1096cc25e4bSEric Biggers {
1106cc25e4bSEric Biggers }
1116cc25e4bSEric Biggers module_exit(crc32_arm_exit);
1126cc25e4bSEric Biggers 
1136cc25e4bSEric Biggers u32 crc32_optimizations(void)
1146cc25e4bSEric Biggers {
1156cc25e4bSEric Biggers 	if (elf_hwcap2 & (HWCAP2_CRC32 | HWCAP2_PMULL))
1166cc25e4bSEric Biggers 		return CRC32_LE_OPTIMIZATION | CRC32C_OPTIMIZATION;
1176cc25e4bSEric Biggers 	return 0;
1186cc25e4bSEric Biggers }
1196cc25e4bSEric Biggers EXPORT_SYMBOL(crc32_optimizations);
1206cc25e4bSEric Biggers 
1216cc25e4bSEric Biggers MODULE_AUTHOR("Ard Biesheuvel <ard.biesheuvel@linaro.org>");
1226cc25e4bSEric Biggers MODULE_DESCRIPTION("Accelerated CRC32(C) using ARM CRC, NEON and Crypto Extensions");
1236cc25e4bSEric Biggers MODULE_LICENSE("GPL v2");
124