162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Accelerated CRC32(C) using ARM CRC, NEON and Crypto Extensions instructions 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2016 Linaro Ltd <ard.biesheuvel@linaro.org> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/cpufeature.h> 962306a36Sopenharmony_ci#include <linux/crc32.h> 1062306a36Sopenharmony_ci#include <linux/init.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/string.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <crypto/internal/hash.h> 1662306a36Sopenharmony_ci#include <crypto/internal/simd.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <asm/hwcap.h> 1962306a36Sopenharmony_ci#include <asm/neon.h> 2062306a36Sopenharmony_ci#include <asm/simd.h> 2162306a36Sopenharmony_ci#include <asm/unaligned.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define PMULL_MIN_LEN 64L /* minimum size of buffer 2462306a36Sopenharmony_ci * for crc32_pmull_le_16 */ 2562306a36Sopenharmony_ci#define SCALE_F 16L /* size of NEON register */ 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ciasmlinkage u32 crc32_pmull_le(const u8 buf[], u32 len, u32 init_crc); 2862306a36Sopenharmony_ciasmlinkage u32 crc32_armv8_le(u32 init_crc, const u8 buf[], u32 len); 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ciasmlinkage u32 crc32c_pmull_le(const u8 buf[], u32 len, u32 init_crc); 3162306a36Sopenharmony_ciasmlinkage u32 crc32c_armv8_le(u32 init_crc, const u8 buf[], u32 len); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic u32 (*fallback_crc32)(u32 init_crc, const u8 buf[], u32 len); 3462306a36Sopenharmony_cistatic u32 (*fallback_crc32c)(u32 init_crc, const u8 buf[], u32 len); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic int crc32_cra_init(struct crypto_tfm *tfm) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci u32 *key = crypto_tfm_ctx(tfm); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci *key = 0; 4162306a36Sopenharmony_ci return 0; 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic int crc32c_cra_init(struct crypto_tfm *tfm) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci u32 *key = crypto_tfm_ctx(tfm); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci *key = ~0; 4962306a36Sopenharmony_ci return 0; 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic int crc32_setkey(struct crypto_shash *hash, const u8 *key, 5362306a36Sopenharmony_ci unsigned int keylen) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci u32 *mctx = crypto_shash_ctx(hash); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (keylen != sizeof(u32)) 5862306a36Sopenharmony_ci return -EINVAL; 5962306a36Sopenharmony_ci *mctx = le32_to_cpup((__le32 *)key); 6062306a36Sopenharmony_ci return 0; 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic int crc32_init(struct shash_desc *desc) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci u32 *mctx = crypto_shash_ctx(desc->tfm); 6662306a36Sopenharmony_ci u32 *crc = shash_desc_ctx(desc); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci *crc = *mctx; 6962306a36Sopenharmony_ci return 0; 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic int crc32_update(struct shash_desc *desc, const u8 *data, 7362306a36Sopenharmony_ci unsigned int length) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci u32 *crc = shash_desc_ctx(desc); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci *crc = crc32_armv8_le(*crc, data, length); 7862306a36Sopenharmony_ci return 0; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic int crc32c_update(struct shash_desc *desc, const u8 *data, 8262306a36Sopenharmony_ci unsigned int length) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci u32 *crc = shash_desc_ctx(desc); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci *crc = crc32c_armv8_le(*crc, data, length); 8762306a36Sopenharmony_ci return 0; 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic int crc32_final(struct shash_desc *desc, u8 *out) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci u32 *crc = shash_desc_ctx(desc); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci put_unaligned_le32(*crc, out); 9562306a36Sopenharmony_ci return 0; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic int crc32c_final(struct shash_desc *desc, u8 *out) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci u32 *crc = shash_desc_ctx(desc); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci put_unaligned_le32(~*crc, out); 10362306a36Sopenharmony_ci return 0; 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic int crc32_pmull_update(struct shash_desc *desc, const u8 *data, 10762306a36Sopenharmony_ci unsigned int length) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci u32 *crc = shash_desc_ctx(desc); 11062306a36Sopenharmony_ci unsigned int l; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci if (crypto_simd_usable()) { 11362306a36Sopenharmony_ci if ((u32)data % SCALE_F) { 11462306a36Sopenharmony_ci l = min_t(u32, length, SCALE_F - ((u32)data % SCALE_F)); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci *crc = fallback_crc32(*crc, data, l); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci data += l; 11962306a36Sopenharmony_ci length -= l; 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (length >= PMULL_MIN_LEN) { 12362306a36Sopenharmony_ci l = round_down(length, SCALE_F); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci kernel_neon_begin(); 12662306a36Sopenharmony_ci *crc = crc32_pmull_le(data, l, *crc); 12762306a36Sopenharmony_ci kernel_neon_end(); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci data += l; 13062306a36Sopenharmony_ci length -= l; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci if (length > 0) 13562306a36Sopenharmony_ci *crc = fallback_crc32(*crc, data, length); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci return 0; 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic int crc32c_pmull_update(struct shash_desc *desc, const u8 *data, 14162306a36Sopenharmony_ci unsigned int length) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci u32 *crc = shash_desc_ctx(desc); 14462306a36Sopenharmony_ci unsigned int l; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (crypto_simd_usable()) { 14762306a36Sopenharmony_ci if ((u32)data % SCALE_F) { 14862306a36Sopenharmony_ci l = min_t(u32, length, SCALE_F - ((u32)data % SCALE_F)); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci *crc = fallback_crc32c(*crc, data, l); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci data += l; 15362306a36Sopenharmony_ci length -= l; 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci if (length >= PMULL_MIN_LEN) { 15762306a36Sopenharmony_ci l = round_down(length, SCALE_F); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci kernel_neon_begin(); 16062306a36Sopenharmony_ci *crc = crc32c_pmull_le(data, l, *crc); 16162306a36Sopenharmony_ci kernel_neon_end(); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci data += l; 16462306a36Sopenharmony_ci length -= l; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (length > 0) 16962306a36Sopenharmony_ci *crc = fallback_crc32c(*crc, data, length); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci return 0; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic struct shash_alg crc32_pmull_algs[] = { { 17562306a36Sopenharmony_ci .setkey = crc32_setkey, 17662306a36Sopenharmony_ci .init = crc32_init, 17762306a36Sopenharmony_ci .update = crc32_update, 17862306a36Sopenharmony_ci .final = crc32_final, 17962306a36Sopenharmony_ci .descsize = sizeof(u32), 18062306a36Sopenharmony_ci .digestsize = sizeof(u32), 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci .base.cra_ctxsize = sizeof(u32), 18362306a36Sopenharmony_ci .base.cra_init = crc32_cra_init, 18462306a36Sopenharmony_ci .base.cra_name = "crc32", 18562306a36Sopenharmony_ci .base.cra_driver_name = "crc32-arm-ce", 18662306a36Sopenharmony_ci .base.cra_priority = 200, 18762306a36Sopenharmony_ci .base.cra_flags = CRYPTO_ALG_OPTIONAL_KEY, 18862306a36Sopenharmony_ci .base.cra_blocksize = 1, 18962306a36Sopenharmony_ci .base.cra_module = THIS_MODULE, 19062306a36Sopenharmony_ci}, { 19162306a36Sopenharmony_ci .setkey = crc32_setkey, 19262306a36Sopenharmony_ci .init = crc32_init, 19362306a36Sopenharmony_ci .update = crc32c_update, 19462306a36Sopenharmony_ci .final = crc32c_final, 19562306a36Sopenharmony_ci .descsize = sizeof(u32), 19662306a36Sopenharmony_ci .digestsize = sizeof(u32), 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci .base.cra_ctxsize = sizeof(u32), 19962306a36Sopenharmony_ci .base.cra_init = crc32c_cra_init, 20062306a36Sopenharmony_ci .base.cra_name = "crc32c", 20162306a36Sopenharmony_ci .base.cra_driver_name = "crc32c-arm-ce", 20262306a36Sopenharmony_ci .base.cra_priority = 200, 20362306a36Sopenharmony_ci .base.cra_flags = CRYPTO_ALG_OPTIONAL_KEY, 20462306a36Sopenharmony_ci .base.cra_blocksize = 1, 20562306a36Sopenharmony_ci .base.cra_module = THIS_MODULE, 20662306a36Sopenharmony_ci} }; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic int __init crc32_pmull_mod_init(void) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci if (elf_hwcap2 & HWCAP2_PMULL) { 21162306a36Sopenharmony_ci crc32_pmull_algs[0].update = crc32_pmull_update; 21262306a36Sopenharmony_ci crc32_pmull_algs[1].update = crc32c_pmull_update; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (elf_hwcap2 & HWCAP2_CRC32) { 21562306a36Sopenharmony_ci fallback_crc32 = crc32_armv8_le; 21662306a36Sopenharmony_ci fallback_crc32c = crc32c_armv8_le; 21762306a36Sopenharmony_ci } else { 21862306a36Sopenharmony_ci fallback_crc32 = crc32_le; 21962306a36Sopenharmony_ci fallback_crc32c = __crc32c_le; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci } else if (!(elf_hwcap2 & HWCAP2_CRC32)) { 22262306a36Sopenharmony_ci return -ENODEV; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci return crypto_register_shashes(crc32_pmull_algs, 22662306a36Sopenharmony_ci ARRAY_SIZE(crc32_pmull_algs)); 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic void __exit crc32_pmull_mod_exit(void) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci crypto_unregister_shashes(crc32_pmull_algs, 23262306a36Sopenharmony_ci ARRAY_SIZE(crc32_pmull_algs)); 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic const struct cpu_feature __maybe_unused crc32_cpu_feature[] = { 23662306a36Sopenharmony_ci { cpu_feature(CRC32) }, { cpu_feature(PMULL) }, { } 23762306a36Sopenharmony_ci}; 23862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(cpu, crc32_cpu_feature); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cimodule_init(crc32_pmull_mod_init); 24162306a36Sopenharmony_cimodule_exit(crc32_pmull_mod_exit); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ciMODULE_AUTHOR("Ard Biesheuvel <ard.biesheuvel@linaro.org>"); 24462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 24562306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("crc32"); 24662306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("crc32c"); 247