162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Calculate a CRC T10-DIF with vpmsum acceleration 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2017, Daniel Axtens, IBM Corporation. 662306a36Sopenharmony_ci * [based on crc32c-vpmsum_glue.c] 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/crc-t10dif.h> 1062306a36Sopenharmony_ci#include <crypto/internal/hash.h> 1162306a36Sopenharmony_ci#include <crypto/internal/simd.h> 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/string.h> 1562306a36Sopenharmony_ci#include <linux/kernel.h> 1662306a36Sopenharmony_ci#include <linux/cpufeature.h> 1762306a36Sopenharmony_ci#include <asm/simd.h> 1862306a36Sopenharmony_ci#include <asm/switch_to.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define VMX_ALIGN 16 2162306a36Sopenharmony_ci#define VMX_ALIGN_MASK (VMX_ALIGN-1) 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define VECTOR_BREAKPOINT 64 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ciu32 __crct10dif_vpmsum(u32 crc, unsigned char const *p, size_t len); 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic u16 crct10dif_vpmsum(u16 crci, unsigned char const *p, size_t len) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci unsigned int prealign; 3062306a36Sopenharmony_ci unsigned int tail; 3162306a36Sopenharmony_ci u32 crc = crci; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci if (len < (VECTOR_BREAKPOINT + VMX_ALIGN) || !crypto_simd_usable()) 3462306a36Sopenharmony_ci return crc_t10dif_generic(crc, p, len); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci if ((unsigned long)p & VMX_ALIGN_MASK) { 3762306a36Sopenharmony_ci prealign = VMX_ALIGN - ((unsigned long)p & VMX_ALIGN_MASK); 3862306a36Sopenharmony_ci crc = crc_t10dif_generic(crc, p, prealign); 3962306a36Sopenharmony_ci len -= prealign; 4062306a36Sopenharmony_ci p += prealign; 4162306a36Sopenharmony_ci } 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci if (len & ~VMX_ALIGN_MASK) { 4462306a36Sopenharmony_ci crc <<= 16; 4562306a36Sopenharmony_ci preempt_disable(); 4662306a36Sopenharmony_ci pagefault_disable(); 4762306a36Sopenharmony_ci enable_kernel_altivec(); 4862306a36Sopenharmony_ci crc = __crct10dif_vpmsum(crc, p, len & ~VMX_ALIGN_MASK); 4962306a36Sopenharmony_ci disable_kernel_altivec(); 5062306a36Sopenharmony_ci pagefault_enable(); 5162306a36Sopenharmony_ci preempt_enable(); 5262306a36Sopenharmony_ci crc >>= 16; 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci tail = len & VMX_ALIGN_MASK; 5662306a36Sopenharmony_ci if (tail) { 5762306a36Sopenharmony_ci p += len & ~VMX_ALIGN_MASK; 5862306a36Sopenharmony_ci crc = crc_t10dif_generic(crc, p, tail); 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci return crc & 0xffff; 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic int crct10dif_vpmsum_init(struct shash_desc *desc) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci u16 *crc = shash_desc_ctx(desc); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci *crc = 0; 6962306a36Sopenharmony_ci return 0; 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic int crct10dif_vpmsum_update(struct shash_desc *desc, const u8 *data, 7362306a36Sopenharmony_ci unsigned int length) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci u16 *crc = shash_desc_ctx(desc); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci *crc = crct10dif_vpmsum(*crc, data, length); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci return 0; 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic int crct10dif_vpmsum_final(struct shash_desc *desc, u8 *out) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci u16 *crcp = shash_desc_ctx(desc); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci *(u16 *)out = *crcp; 8862306a36Sopenharmony_ci return 0; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic struct shash_alg alg = { 9262306a36Sopenharmony_ci .init = crct10dif_vpmsum_init, 9362306a36Sopenharmony_ci .update = crct10dif_vpmsum_update, 9462306a36Sopenharmony_ci .final = crct10dif_vpmsum_final, 9562306a36Sopenharmony_ci .descsize = CRC_T10DIF_DIGEST_SIZE, 9662306a36Sopenharmony_ci .digestsize = CRC_T10DIF_DIGEST_SIZE, 9762306a36Sopenharmony_ci .base = { 9862306a36Sopenharmony_ci .cra_name = "crct10dif", 9962306a36Sopenharmony_ci .cra_driver_name = "crct10dif-vpmsum", 10062306a36Sopenharmony_ci .cra_priority = 200, 10162306a36Sopenharmony_ci .cra_blocksize = CRC_T10DIF_BLOCK_SIZE, 10262306a36Sopenharmony_ci .cra_module = THIS_MODULE, 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci}; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic int __init crct10dif_vpmsum_mod_init(void) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_ARCH_207S)) 10962306a36Sopenharmony_ci return -ENODEV; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci return crypto_register_shash(&alg); 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic void __exit crct10dif_vpmsum_mod_fini(void) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci crypto_unregister_shash(&alg); 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cimodule_cpu_feature_match(PPC_MODULE_FEATURE_VEC_CRYPTO, crct10dif_vpmsum_mod_init); 12062306a36Sopenharmony_cimodule_exit(crct10dif_vpmsum_mod_fini); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ciMODULE_AUTHOR("Daniel Axtens <dja@axtens.net>"); 12362306a36Sopenharmony_ciMODULE_DESCRIPTION("CRCT10DIF using vector polynomial multiply-sum instructions"); 12462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 12562306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("crct10dif"); 12662306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("crct10dif-vpmsum"); 127