162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Crypto-API module for CRC-32 algorithms implemented with the 462306a36Sopenharmony_ci * z/Architecture Vector Extension Facility. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright IBM Corp. 2015 762306a36Sopenharmony_ci * Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#define KMSG_COMPONENT "crc32-vx" 1062306a36Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/cpufeature.h> 1462306a36Sopenharmony_ci#include <linux/crc32.h> 1562306a36Sopenharmony_ci#include <crypto/internal/hash.h> 1662306a36Sopenharmony_ci#include <asm/fpu/api.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define CRC32_BLOCK_SIZE 1 2062306a36Sopenharmony_ci#define CRC32_DIGEST_SIZE 4 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define VX_MIN_LEN 64 2362306a36Sopenharmony_ci#define VX_ALIGNMENT 16L 2462306a36Sopenharmony_ci#define VX_ALIGN_MASK (VX_ALIGNMENT - 1) 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistruct crc_ctx { 2762306a36Sopenharmony_ci u32 key; 2862306a36Sopenharmony_ci}; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistruct crc_desc_ctx { 3162306a36Sopenharmony_ci u32 crc; 3262306a36Sopenharmony_ci}; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* Prototypes for functions in assembly files */ 3562306a36Sopenharmony_ciu32 crc32_le_vgfm_16(u32 crc, unsigned char const *buf, size_t size); 3662306a36Sopenharmony_ciu32 crc32_be_vgfm_16(u32 crc, unsigned char const *buf, size_t size); 3762306a36Sopenharmony_ciu32 crc32c_le_vgfm_16(u32 crc, unsigned char const *buf, size_t size); 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* 4062306a36Sopenharmony_ci * DEFINE_CRC32_VX() - Define a CRC-32 function using the vector extension 4162306a36Sopenharmony_ci * 4262306a36Sopenharmony_ci * Creates a function to perform a particular CRC-32 computation. Depending 4362306a36Sopenharmony_ci * on the message buffer, the hardware-accelerated or software implementation 4462306a36Sopenharmony_ci * is used. Note that the message buffer is aligned to improve fetch 4562306a36Sopenharmony_ci * operations of VECTOR LOAD MULTIPLE instructions. 4662306a36Sopenharmony_ci * 4762306a36Sopenharmony_ci */ 4862306a36Sopenharmony_ci#define DEFINE_CRC32_VX(___fname, ___crc32_vx, ___crc32_sw) \ 4962306a36Sopenharmony_ci static u32 __pure ___fname(u32 crc, \ 5062306a36Sopenharmony_ci unsigned char const *data, size_t datalen) \ 5162306a36Sopenharmony_ci { \ 5262306a36Sopenharmony_ci struct kernel_fpu vxstate; \ 5362306a36Sopenharmony_ci unsigned long prealign, aligned, remaining; \ 5462306a36Sopenharmony_ci \ 5562306a36Sopenharmony_ci if (datalen < VX_MIN_LEN + VX_ALIGN_MASK) \ 5662306a36Sopenharmony_ci return ___crc32_sw(crc, data, datalen); \ 5762306a36Sopenharmony_ci \ 5862306a36Sopenharmony_ci if ((unsigned long)data & VX_ALIGN_MASK) { \ 5962306a36Sopenharmony_ci prealign = VX_ALIGNMENT - \ 6062306a36Sopenharmony_ci ((unsigned long)data & VX_ALIGN_MASK); \ 6162306a36Sopenharmony_ci datalen -= prealign; \ 6262306a36Sopenharmony_ci crc = ___crc32_sw(crc, data, prealign); \ 6362306a36Sopenharmony_ci data = (void *)((unsigned long)data + prealign); \ 6462306a36Sopenharmony_ci } \ 6562306a36Sopenharmony_ci \ 6662306a36Sopenharmony_ci aligned = datalen & ~VX_ALIGN_MASK; \ 6762306a36Sopenharmony_ci remaining = datalen & VX_ALIGN_MASK; \ 6862306a36Sopenharmony_ci \ 6962306a36Sopenharmony_ci kernel_fpu_begin(&vxstate, KERNEL_VXR_LOW); \ 7062306a36Sopenharmony_ci crc = ___crc32_vx(crc, data, aligned); \ 7162306a36Sopenharmony_ci kernel_fpu_end(&vxstate, KERNEL_VXR_LOW); \ 7262306a36Sopenharmony_ci \ 7362306a36Sopenharmony_ci if (remaining) \ 7462306a36Sopenharmony_ci crc = ___crc32_sw(crc, data + aligned, remaining); \ 7562306a36Sopenharmony_ci \ 7662306a36Sopenharmony_ci return crc; \ 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ciDEFINE_CRC32_VX(crc32_le_vx, crc32_le_vgfm_16, crc32_le) 8062306a36Sopenharmony_ciDEFINE_CRC32_VX(crc32_be_vx, crc32_be_vgfm_16, crc32_be) 8162306a36Sopenharmony_ciDEFINE_CRC32_VX(crc32c_le_vx, crc32c_le_vgfm_16, __crc32c_le) 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic int crc32_vx_cra_init_zero(struct crypto_tfm *tfm) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci struct crc_ctx *mctx = crypto_tfm_ctx(tfm); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci mctx->key = 0; 8962306a36Sopenharmony_ci return 0; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic int crc32_vx_cra_init_invert(struct crypto_tfm *tfm) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci struct crc_ctx *mctx = crypto_tfm_ctx(tfm); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci mctx->key = ~0; 9762306a36Sopenharmony_ci return 0; 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic int crc32_vx_init(struct shash_desc *desc) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci struct crc_ctx *mctx = crypto_shash_ctx(desc->tfm); 10362306a36Sopenharmony_ci struct crc_desc_ctx *ctx = shash_desc_ctx(desc); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci ctx->crc = mctx->key; 10662306a36Sopenharmony_ci return 0; 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic int crc32_vx_setkey(struct crypto_shash *tfm, const u8 *newkey, 11062306a36Sopenharmony_ci unsigned int newkeylen) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci struct crc_ctx *mctx = crypto_shash_ctx(tfm); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci if (newkeylen != sizeof(mctx->key)) 11562306a36Sopenharmony_ci return -EINVAL; 11662306a36Sopenharmony_ci mctx->key = le32_to_cpu(*(__le32 *)newkey); 11762306a36Sopenharmony_ci return 0; 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic int crc32be_vx_setkey(struct crypto_shash *tfm, const u8 *newkey, 12162306a36Sopenharmony_ci unsigned int newkeylen) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci struct crc_ctx *mctx = crypto_shash_ctx(tfm); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci if (newkeylen != sizeof(mctx->key)) 12662306a36Sopenharmony_ci return -EINVAL; 12762306a36Sopenharmony_ci mctx->key = be32_to_cpu(*(__be32 *)newkey); 12862306a36Sopenharmony_ci return 0; 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic int crc32le_vx_final(struct shash_desc *desc, u8 *out) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci struct crc_desc_ctx *ctx = shash_desc_ctx(desc); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci *(__le32 *)out = cpu_to_le32p(&ctx->crc); 13662306a36Sopenharmony_ci return 0; 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic int crc32be_vx_final(struct shash_desc *desc, u8 *out) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci struct crc_desc_ctx *ctx = shash_desc_ctx(desc); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci *(__be32 *)out = cpu_to_be32p(&ctx->crc); 14462306a36Sopenharmony_ci return 0; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic int crc32c_vx_final(struct shash_desc *desc, u8 *out) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci struct crc_desc_ctx *ctx = shash_desc_ctx(desc); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* 15262306a36Sopenharmony_ci * Perform a final XOR with 0xFFFFFFFF to be in sync 15362306a36Sopenharmony_ci * with the generic crc32c shash implementation. 15462306a36Sopenharmony_ci */ 15562306a36Sopenharmony_ci *(__le32 *)out = ~cpu_to_le32p(&ctx->crc); 15662306a36Sopenharmony_ci return 0; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic int __crc32le_vx_finup(u32 *crc, const u8 *data, unsigned int len, 16062306a36Sopenharmony_ci u8 *out) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci *(__le32 *)out = cpu_to_le32(crc32_le_vx(*crc, data, len)); 16362306a36Sopenharmony_ci return 0; 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic int __crc32be_vx_finup(u32 *crc, const u8 *data, unsigned int len, 16762306a36Sopenharmony_ci u8 *out) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci *(__be32 *)out = cpu_to_be32(crc32_be_vx(*crc, data, len)); 17062306a36Sopenharmony_ci return 0; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic int __crc32c_vx_finup(u32 *crc, const u8 *data, unsigned int len, 17462306a36Sopenharmony_ci u8 *out) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci /* 17762306a36Sopenharmony_ci * Perform a final XOR with 0xFFFFFFFF to be in sync 17862306a36Sopenharmony_ci * with the generic crc32c shash implementation. 17962306a36Sopenharmony_ci */ 18062306a36Sopenharmony_ci *(__le32 *)out = ~cpu_to_le32(crc32c_le_vx(*crc, data, len)); 18162306a36Sopenharmony_ci return 0; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci#define CRC32_VX_FINUP(alg, func) \ 18662306a36Sopenharmony_ci static int alg ## _vx_finup(struct shash_desc *desc, const u8 *data, \ 18762306a36Sopenharmony_ci unsigned int datalen, u8 *out) \ 18862306a36Sopenharmony_ci { \ 18962306a36Sopenharmony_ci return __ ## alg ## _vx_finup(shash_desc_ctx(desc), \ 19062306a36Sopenharmony_ci data, datalen, out); \ 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ciCRC32_VX_FINUP(crc32le, crc32_le_vx) 19462306a36Sopenharmony_ciCRC32_VX_FINUP(crc32be, crc32_be_vx) 19562306a36Sopenharmony_ciCRC32_VX_FINUP(crc32c, crc32c_le_vx) 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci#define CRC32_VX_DIGEST(alg, func) \ 19862306a36Sopenharmony_ci static int alg ## _vx_digest(struct shash_desc *desc, const u8 *data, \ 19962306a36Sopenharmony_ci unsigned int len, u8 *out) \ 20062306a36Sopenharmony_ci { \ 20162306a36Sopenharmony_ci return __ ## alg ## _vx_finup(crypto_shash_ctx(desc->tfm), \ 20262306a36Sopenharmony_ci data, len, out); \ 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ciCRC32_VX_DIGEST(crc32le, crc32_le_vx) 20662306a36Sopenharmony_ciCRC32_VX_DIGEST(crc32be, crc32_be_vx) 20762306a36Sopenharmony_ciCRC32_VX_DIGEST(crc32c, crc32c_le_vx) 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci#define CRC32_VX_UPDATE(alg, func) \ 21062306a36Sopenharmony_ci static int alg ## _vx_update(struct shash_desc *desc, const u8 *data, \ 21162306a36Sopenharmony_ci unsigned int datalen) \ 21262306a36Sopenharmony_ci { \ 21362306a36Sopenharmony_ci struct crc_desc_ctx *ctx = shash_desc_ctx(desc); \ 21462306a36Sopenharmony_ci ctx->crc = func(ctx->crc, data, datalen); \ 21562306a36Sopenharmony_ci return 0; \ 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ciCRC32_VX_UPDATE(crc32le, crc32_le_vx) 21962306a36Sopenharmony_ciCRC32_VX_UPDATE(crc32be, crc32_be_vx) 22062306a36Sopenharmony_ciCRC32_VX_UPDATE(crc32c, crc32c_le_vx) 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic struct shash_alg crc32_vx_algs[] = { 22462306a36Sopenharmony_ci /* CRC-32 LE */ 22562306a36Sopenharmony_ci { 22662306a36Sopenharmony_ci .init = crc32_vx_init, 22762306a36Sopenharmony_ci .setkey = crc32_vx_setkey, 22862306a36Sopenharmony_ci .update = crc32le_vx_update, 22962306a36Sopenharmony_ci .final = crc32le_vx_final, 23062306a36Sopenharmony_ci .finup = crc32le_vx_finup, 23162306a36Sopenharmony_ci .digest = crc32le_vx_digest, 23262306a36Sopenharmony_ci .descsize = sizeof(struct crc_desc_ctx), 23362306a36Sopenharmony_ci .digestsize = CRC32_DIGEST_SIZE, 23462306a36Sopenharmony_ci .base = { 23562306a36Sopenharmony_ci .cra_name = "crc32", 23662306a36Sopenharmony_ci .cra_driver_name = "crc32-vx", 23762306a36Sopenharmony_ci .cra_priority = 200, 23862306a36Sopenharmony_ci .cra_flags = CRYPTO_ALG_OPTIONAL_KEY, 23962306a36Sopenharmony_ci .cra_blocksize = CRC32_BLOCK_SIZE, 24062306a36Sopenharmony_ci .cra_ctxsize = sizeof(struct crc_ctx), 24162306a36Sopenharmony_ci .cra_module = THIS_MODULE, 24262306a36Sopenharmony_ci .cra_init = crc32_vx_cra_init_zero, 24362306a36Sopenharmony_ci }, 24462306a36Sopenharmony_ci }, 24562306a36Sopenharmony_ci /* CRC-32 BE */ 24662306a36Sopenharmony_ci { 24762306a36Sopenharmony_ci .init = crc32_vx_init, 24862306a36Sopenharmony_ci .setkey = crc32be_vx_setkey, 24962306a36Sopenharmony_ci .update = crc32be_vx_update, 25062306a36Sopenharmony_ci .final = crc32be_vx_final, 25162306a36Sopenharmony_ci .finup = crc32be_vx_finup, 25262306a36Sopenharmony_ci .digest = crc32be_vx_digest, 25362306a36Sopenharmony_ci .descsize = sizeof(struct crc_desc_ctx), 25462306a36Sopenharmony_ci .digestsize = CRC32_DIGEST_SIZE, 25562306a36Sopenharmony_ci .base = { 25662306a36Sopenharmony_ci .cra_name = "crc32be", 25762306a36Sopenharmony_ci .cra_driver_name = "crc32be-vx", 25862306a36Sopenharmony_ci .cra_priority = 200, 25962306a36Sopenharmony_ci .cra_flags = CRYPTO_ALG_OPTIONAL_KEY, 26062306a36Sopenharmony_ci .cra_blocksize = CRC32_BLOCK_SIZE, 26162306a36Sopenharmony_ci .cra_ctxsize = sizeof(struct crc_ctx), 26262306a36Sopenharmony_ci .cra_module = THIS_MODULE, 26362306a36Sopenharmony_ci .cra_init = crc32_vx_cra_init_zero, 26462306a36Sopenharmony_ci }, 26562306a36Sopenharmony_ci }, 26662306a36Sopenharmony_ci /* CRC-32C LE */ 26762306a36Sopenharmony_ci { 26862306a36Sopenharmony_ci .init = crc32_vx_init, 26962306a36Sopenharmony_ci .setkey = crc32_vx_setkey, 27062306a36Sopenharmony_ci .update = crc32c_vx_update, 27162306a36Sopenharmony_ci .final = crc32c_vx_final, 27262306a36Sopenharmony_ci .finup = crc32c_vx_finup, 27362306a36Sopenharmony_ci .digest = crc32c_vx_digest, 27462306a36Sopenharmony_ci .descsize = sizeof(struct crc_desc_ctx), 27562306a36Sopenharmony_ci .digestsize = CRC32_DIGEST_SIZE, 27662306a36Sopenharmony_ci .base = { 27762306a36Sopenharmony_ci .cra_name = "crc32c", 27862306a36Sopenharmony_ci .cra_driver_name = "crc32c-vx", 27962306a36Sopenharmony_ci .cra_priority = 200, 28062306a36Sopenharmony_ci .cra_flags = CRYPTO_ALG_OPTIONAL_KEY, 28162306a36Sopenharmony_ci .cra_blocksize = CRC32_BLOCK_SIZE, 28262306a36Sopenharmony_ci .cra_ctxsize = sizeof(struct crc_ctx), 28362306a36Sopenharmony_ci .cra_module = THIS_MODULE, 28462306a36Sopenharmony_ci .cra_init = crc32_vx_cra_init_invert, 28562306a36Sopenharmony_ci }, 28662306a36Sopenharmony_ci }, 28762306a36Sopenharmony_ci}; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_cistatic int __init crc_vx_mod_init(void) 29162306a36Sopenharmony_ci{ 29262306a36Sopenharmony_ci return crypto_register_shashes(crc32_vx_algs, 29362306a36Sopenharmony_ci ARRAY_SIZE(crc32_vx_algs)); 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_cistatic void __exit crc_vx_mod_exit(void) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci crypto_unregister_shashes(crc32_vx_algs, ARRAY_SIZE(crc32_vx_algs)); 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_cimodule_cpu_feature_match(S390_CPU_FEATURE_VXRS, crc_vx_mod_init); 30262306a36Sopenharmony_cimodule_exit(crc_vx_mod_exit); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ciMODULE_AUTHOR("Hendrik Brueckner <brueckner@linux.vnet.ibm.com>"); 30562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("crc32"); 30862306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("crc32-vx"); 30962306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("crc32c"); 31062306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("crc32c-vx"); 311