162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Cryptographic API. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * T10 Data Integrity Field CRC16 Crypto Transform using PCLMULQDQ Instructions 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 2013 Intel Corporation 762306a36Sopenharmony_ci * Author: Tim Chen <tim.c.chen@linux.intel.com> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or modify it 1062306a36Sopenharmony_ci * under the terms of the GNU General Public License as published by the Free 1162306a36Sopenharmony_ci * Software Foundation; either version 2 of the License, or (at your option) 1262306a36Sopenharmony_ci * any later version. 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 1562306a36Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 1662306a36Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 1762306a36Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 1862306a36Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 1962306a36Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 2062306a36Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 2162306a36Sopenharmony_ci * SOFTWARE. 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include <linux/types.h> 2662306a36Sopenharmony_ci#include <linux/module.h> 2762306a36Sopenharmony_ci#include <linux/crc-t10dif.h> 2862306a36Sopenharmony_ci#include <crypto/internal/hash.h> 2962306a36Sopenharmony_ci#include <crypto/internal/simd.h> 3062306a36Sopenharmony_ci#include <linux/init.h> 3162306a36Sopenharmony_ci#include <linux/string.h> 3262306a36Sopenharmony_ci#include <linux/kernel.h> 3362306a36Sopenharmony_ci#include <asm/cpufeatures.h> 3462306a36Sopenharmony_ci#include <asm/cpu_device_id.h> 3562306a36Sopenharmony_ci#include <asm/simd.h> 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ciasmlinkage u16 crc_t10dif_pcl(u16 init_crc, const u8 *buf, size_t len); 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistruct chksum_desc_ctx { 4062306a36Sopenharmony_ci __u16 crc; 4162306a36Sopenharmony_ci}; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic int chksum_init(struct shash_desc *desc) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci struct chksum_desc_ctx *ctx = shash_desc_ctx(desc); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci ctx->crc = 0; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci return 0; 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic int chksum_update(struct shash_desc *desc, const u8 *data, 5362306a36Sopenharmony_ci unsigned int length) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci struct chksum_desc_ctx *ctx = shash_desc_ctx(desc); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (length >= 16 && crypto_simd_usable()) { 5862306a36Sopenharmony_ci kernel_fpu_begin(); 5962306a36Sopenharmony_ci ctx->crc = crc_t10dif_pcl(ctx->crc, data, length); 6062306a36Sopenharmony_ci kernel_fpu_end(); 6162306a36Sopenharmony_ci } else 6262306a36Sopenharmony_ci ctx->crc = crc_t10dif_generic(ctx->crc, data, length); 6362306a36Sopenharmony_ci return 0; 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic int chksum_final(struct shash_desc *desc, u8 *out) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci struct chksum_desc_ctx *ctx = shash_desc_ctx(desc); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci *(__u16 *)out = ctx->crc; 7162306a36Sopenharmony_ci return 0; 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic int __chksum_finup(__u16 crc, const u8 *data, unsigned int len, u8 *out) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci if (len >= 16 && crypto_simd_usable()) { 7762306a36Sopenharmony_ci kernel_fpu_begin(); 7862306a36Sopenharmony_ci *(__u16 *)out = crc_t10dif_pcl(crc, data, len); 7962306a36Sopenharmony_ci kernel_fpu_end(); 8062306a36Sopenharmony_ci } else 8162306a36Sopenharmony_ci *(__u16 *)out = crc_t10dif_generic(crc, data, len); 8262306a36Sopenharmony_ci return 0; 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic int chksum_finup(struct shash_desc *desc, const u8 *data, 8662306a36Sopenharmony_ci unsigned int len, u8 *out) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci struct chksum_desc_ctx *ctx = shash_desc_ctx(desc); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci return __chksum_finup(ctx->crc, data, len, out); 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic int chksum_digest(struct shash_desc *desc, const u8 *data, 9462306a36Sopenharmony_ci unsigned int length, u8 *out) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci return __chksum_finup(0, data, length, out); 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic struct shash_alg alg = { 10062306a36Sopenharmony_ci .digestsize = CRC_T10DIF_DIGEST_SIZE, 10162306a36Sopenharmony_ci .init = chksum_init, 10262306a36Sopenharmony_ci .update = chksum_update, 10362306a36Sopenharmony_ci .final = chksum_final, 10462306a36Sopenharmony_ci .finup = chksum_finup, 10562306a36Sopenharmony_ci .digest = chksum_digest, 10662306a36Sopenharmony_ci .descsize = sizeof(struct chksum_desc_ctx), 10762306a36Sopenharmony_ci .base = { 10862306a36Sopenharmony_ci .cra_name = "crct10dif", 10962306a36Sopenharmony_ci .cra_driver_name = "crct10dif-pclmul", 11062306a36Sopenharmony_ci .cra_priority = 200, 11162306a36Sopenharmony_ci .cra_blocksize = CRC_T10DIF_BLOCK_SIZE, 11262306a36Sopenharmony_ci .cra_module = THIS_MODULE, 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci}; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic const struct x86_cpu_id crct10dif_cpu_id[] = { 11762306a36Sopenharmony_ci X86_MATCH_FEATURE(X86_FEATURE_PCLMULQDQ, NULL), 11862306a36Sopenharmony_ci {} 11962306a36Sopenharmony_ci}; 12062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(x86cpu, crct10dif_cpu_id); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic int __init crct10dif_intel_mod_init(void) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci if (!x86_match_cpu(crct10dif_cpu_id)) 12562306a36Sopenharmony_ci return -ENODEV; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci return crypto_register_shash(&alg); 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic void __exit crct10dif_intel_mod_fini(void) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci crypto_unregister_shash(&alg); 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cimodule_init(crct10dif_intel_mod_init); 13662306a36Sopenharmony_cimodule_exit(crct10dif_intel_mod_fini); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ciMODULE_AUTHOR("Tim Chen <tim.c.chen@linux.intel.com>"); 13962306a36Sopenharmony_ciMODULE_DESCRIPTION("T10 DIF CRC calculation accelerated with PCLMULQDQ."); 14062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("crct10dif"); 14362306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("crct10dif-pclmul"); 144