18c2ecf20Sopenharmony_ci/* GPL HEADER START
28c2ecf20Sopenharmony_ci *
38c2ecf20Sopenharmony_ci * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify
68c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License version 2 only,
78c2ecf20Sopenharmony_ci * as published by the Free Software Foundation.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, but
108c2ecf20Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of
118c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
128c2ecf20Sopenharmony_ci * General Public License version 2 for more details (a copy is included
138c2ecf20Sopenharmony_ci * in the LICENSE file that accompanied this code).
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci * You should have received a copy of the GNU General Public License
168c2ecf20Sopenharmony_ci * version 2 along with this program; If not, see http://www.gnu.org/licenses
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci * Please  visit http://www.xyratex.com/contact if you need additional
198c2ecf20Sopenharmony_ci * information or have any questions.
208c2ecf20Sopenharmony_ci *
218c2ecf20Sopenharmony_ci * GPL HEADER END
228c2ecf20Sopenharmony_ci */
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci/*
258c2ecf20Sopenharmony_ci * Copyright 2012 Xyratex Technology Limited
268c2ecf20Sopenharmony_ci *
278c2ecf20Sopenharmony_ci * Wrappers for kernel crypto shash api to pclmulqdq crc32 imlementation.
288c2ecf20Sopenharmony_ci */
298c2ecf20Sopenharmony_ci#include <linux/init.h>
308c2ecf20Sopenharmony_ci#include <linux/module.h>
318c2ecf20Sopenharmony_ci#include <linux/string.h>
328c2ecf20Sopenharmony_ci#include <linux/kernel.h>
338c2ecf20Sopenharmony_ci#include <linux/crc32.h>
348c2ecf20Sopenharmony_ci#include <crypto/internal/hash.h>
358c2ecf20Sopenharmony_ci#include <crypto/internal/simd.h>
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#include <asm/cpufeatures.h>
388c2ecf20Sopenharmony_ci#include <asm/cpu_device_id.h>
398c2ecf20Sopenharmony_ci#include <asm/simd.h>
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#define CHKSUM_BLOCK_SIZE	1
428c2ecf20Sopenharmony_ci#define CHKSUM_DIGEST_SIZE	4
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci#define PCLMUL_MIN_LEN		64L     /* minimum size of buffer
458c2ecf20Sopenharmony_ci					 * for crc32_pclmul_le_16 */
468c2ecf20Sopenharmony_ci#define SCALE_F			16L	/* size of xmm register */
478c2ecf20Sopenharmony_ci#define SCALE_F_MASK		(SCALE_F - 1)
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ciu32 crc32_pclmul_le_16(unsigned char const *buffer, size_t len, u32 crc32);
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic u32 __attribute__((pure))
528c2ecf20Sopenharmony_ci	crc32_pclmul_le(u32 crc, unsigned char const *p, size_t len)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	unsigned int iquotient;
558c2ecf20Sopenharmony_ci	unsigned int iremainder;
568c2ecf20Sopenharmony_ci	unsigned int prealign;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	if (len < PCLMUL_MIN_LEN + SCALE_F_MASK || !crypto_simd_usable())
598c2ecf20Sopenharmony_ci		return crc32_le(crc, p, len);
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	if ((long)p & SCALE_F_MASK) {
628c2ecf20Sopenharmony_ci		/* align p to 16 byte */
638c2ecf20Sopenharmony_ci		prealign = SCALE_F - ((long)p & SCALE_F_MASK);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci		crc = crc32_le(crc, p, prealign);
668c2ecf20Sopenharmony_ci		len -= prealign;
678c2ecf20Sopenharmony_ci		p = (unsigned char *)(((unsigned long)p + SCALE_F_MASK) &
688c2ecf20Sopenharmony_ci				     ~SCALE_F_MASK);
698c2ecf20Sopenharmony_ci	}
708c2ecf20Sopenharmony_ci	iquotient = len & (~SCALE_F_MASK);
718c2ecf20Sopenharmony_ci	iremainder = len & SCALE_F_MASK;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	kernel_fpu_begin();
748c2ecf20Sopenharmony_ci	crc = crc32_pclmul_le_16(p, iquotient, crc);
758c2ecf20Sopenharmony_ci	kernel_fpu_end();
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	if (iremainder)
788c2ecf20Sopenharmony_ci		crc = crc32_le(crc, p + iquotient, iremainder);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	return crc;
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic int crc32_pclmul_cra_init(struct crypto_tfm *tfm)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	u32 *key = crypto_tfm_ctx(tfm);
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	*key = 0;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	return 0;
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic int crc32_pclmul_setkey(struct crypto_shash *hash, const u8 *key,
938c2ecf20Sopenharmony_ci			unsigned int keylen)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	u32 *mctx = crypto_shash_ctx(hash);
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	if (keylen != sizeof(u32))
988c2ecf20Sopenharmony_ci		return -EINVAL;
998c2ecf20Sopenharmony_ci	*mctx = le32_to_cpup((__le32 *)key);
1008c2ecf20Sopenharmony_ci	return 0;
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic int crc32_pclmul_init(struct shash_desc *desc)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	u32 *mctx = crypto_shash_ctx(desc->tfm);
1068c2ecf20Sopenharmony_ci	u32 *crcp = shash_desc_ctx(desc);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	*crcp = *mctx;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	return 0;
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic int crc32_pclmul_update(struct shash_desc *desc, const u8 *data,
1148c2ecf20Sopenharmony_ci			       unsigned int len)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	u32 *crcp = shash_desc_ctx(desc);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	*crcp = crc32_pclmul_le(*crcp, data, len);
1198c2ecf20Sopenharmony_ci	return 0;
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci/* No final XOR 0xFFFFFFFF, like crc32_le */
1238c2ecf20Sopenharmony_cistatic int __crc32_pclmul_finup(u32 *crcp, const u8 *data, unsigned int len,
1248c2ecf20Sopenharmony_ci				u8 *out)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	*(__le32 *)out = cpu_to_le32(crc32_pclmul_le(*crcp, data, len));
1278c2ecf20Sopenharmony_ci	return 0;
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic int crc32_pclmul_finup(struct shash_desc *desc, const u8 *data,
1318c2ecf20Sopenharmony_ci			      unsigned int len, u8 *out)
1328c2ecf20Sopenharmony_ci{
1338c2ecf20Sopenharmony_ci	return __crc32_pclmul_finup(shash_desc_ctx(desc), data, len, out);
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistatic int crc32_pclmul_final(struct shash_desc *desc, u8 *out)
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	u32 *crcp = shash_desc_ctx(desc);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	*(__le32 *)out = cpu_to_le32p(crcp);
1418c2ecf20Sopenharmony_ci	return 0;
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistatic int crc32_pclmul_digest(struct shash_desc *desc, const u8 *data,
1458c2ecf20Sopenharmony_ci			       unsigned int len, u8 *out)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	return __crc32_pclmul_finup(crypto_shash_ctx(desc->tfm), data, len,
1488c2ecf20Sopenharmony_ci				    out);
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cistatic struct shash_alg alg = {
1528c2ecf20Sopenharmony_ci	.setkey		= crc32_pclmul_setkey,
1538c2ecf20Sopenharmony_ci	.init		= crc32_pclmul_init,
1548c2ecf20Sopenharmony_ci	.update		= crc32_pclmul_update,
1558c2ecf20Sopenharmony_ci	.final		= crc32_pclmul_final,
1568c2ecf20Sopenharmony_ci	.finup		= crc32_pclmul_finup,
1578c2ecf20Sopenharmony_ci	.digest		= crc32_pclmul_digest,
1588c2ecf20Sopenharmony_ci	.descsize	= sizeof(u32),
1598c2ecf20Sopenharmony_ci	.digestsize	= CHKSUM_DIGEST_SIZE,
1608c2ecf20Sopenharmony_ci	.base		= {
1618c2ecf20Sopenharmony_ci			.cra_name		= "crc32",
1628c2ecf20Sopenharmony_ci			.cra_driver_name	= "crc32-pclmul",
1638c2ecf20Sopenharmony_ci			.cra_priority		= 200,
1648c2ecf20Sopenharmony_ci			.cra_flags		= CRYPTO_ALG_OPTIONAL_KEY,
1658c2ecf20Sopenharmony_ci			.cra_blocksize		= CHKSUM_BLOCK_SIZE,
1668c2ecf20Sopenharmony_ci			.cra_ctxsize		= sizeof(u32),
1678c2ecf20Sopenharmony_ci			.cra_module		= THIS_MODULE,
1688c2ecf20Sopenharmony_ci			.cra_init		= crc32_pclmul_cra_init,
1698c2ecf20Sopenharmony_ci	}
1708c2ecf20Sopenharmony_ci};
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cistatic const struct x86_cpu_id crc32pclmul_cpu_id[] = {
1738c2ecf20Sopenharmony_ci	X86_MATCH_FEATURE(X86_FEATURE_PCLMULQDQ, NULL),
1748c2ecf20Sopenharmony_ci	{}
1758c2ecf20Sopenharmony_ci};
1768c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(x86cpu, crc32pclmul_cpu_id);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cistatic int __init crc32_pclmul_mod_init(void)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	if (!x86_match_cpu(crc32pclmul_cpu_id)) {
1838c2ecf20Sopenharmony_ci		pr_info("PCLMULQDQ-NI instructions are not detected.\n");
1848c2ecf20Sopenharmony_ci		return -ENODEV;
1858c2ecf20Sopenharmony_ci	}
1868c2ecf20Sopenharmony_ci	return crypto_register_shash(&alg);
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic void __exit crc32_pclmul_mod_fini(void)
1908c2ecf20Sopenharmony_ci{
1918c2ecf20Sopenharmony_ci	crypto_unregister_shash(&alg);
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_cimodule_init(crc32_pclmul_mod_init);
1958c2ecf20Sopenharmony_cimodule_exit(crc32_pclmul_mod_fini);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ciMODULE_AUTHOR("Alexander Boyko <alexander_boyko@xyratex.com>");
1988c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ciMODULE_ALIAS_CRYPTO("crc32");
2018c2ecf20Sopenharmony_ciMODULE_ALIAS_CRYPTO("crc32-pclmul");
202