162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * crc32-mips.c - CRC32 and CRC32C using optional MIPSr6 instructions
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Module based on arm64/crypto/crc32-arm.c
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (C) 2014 Linaro Ltd <yazen.ghannam@linaro.org>
862306a36Sopenharmony_ci * Copyright (C) 2018 MIPS Tech, LLC
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/cpufeature.h>
1262306a36Sopenharmony_ci#include <linux/init.h>
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/string.h>
1662306a36Sopenharmony_ci#include <asm/mipsregs.h>
1762306a36Sopenharmony_ci#include <asm/unaligned.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <crypto/internal/hash.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cienum crc_op_size {
2262306a36Sopenharmony_ci	b, h, w, d,
2362306a36Sopenharmony_ci};
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cienum crc_type {
2662306a36Sopenharmony_ci	crc32,
2762306a36Sopenharmony_ci	crc32c,
2862306a36Sopenharmony_ci};
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#ifndef TOOLCHAIN_SUPPORTS_CRC
3162306a36Sopenharmony_ci#define _ASM_SET_CRC(OP, SZ, TYPE)					  \
3262306a36Sopenharmony_ci_ASM_MACRO_3R(OP, rt, rs, rt2,						  \
3362306a36Sopenharmony_ci	".ifnc	\\rt, \\rt2\n\t"					  \
3462306a36Sopenharmony_ci	".error	\"invalid operands \\\"" #OP " \\rt,\\rs,\\rt2\\\"\"\n\t" \
3562306a36Sopenharmony_ci	".endif\n\t"							  \
3662306a36Sopenharmony_ci	_ASM_INSN_IF_MIPS(0x7c00000f | (__rt << 16) | (__rs << 21) |	  \
3762306a36Sopenharmony_ci			  ((SZ) <<  6) | ((TYPE) << 8))			  \
3862306a36Sopenharmony_ci	_ASM_INSN32_IF_MM(0x00000030 | (__rs << 16) | (__rt << 21) |	  \
3962306a36Sopenharmony_ci			  ((SZ) << 14) | ((TYPE) << 3)))
4062306a36Sopenharmony_ci#define _ASM_UNSET_CRC(op, SZ, TYPE) ".purgem " #op "\n\t"
4162306a36Sopenharmony_ci#else /* !TOOLCHAIN_SUPPORTS_CRC */
4262306a36Sopenharmony_ci#define _ASM_SET_CRC(op, SZ, TYPE) ".set\tcrc\n\t"
4362306a36Sopenharmony_ci#define _ASM_UNSET_CRC(op, SZ, TYPE)
4462306a36Sopenharmony_ci#endif
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#define __CRC32(crc, value, op, SZ, TYPE)		\
4762306a36Sopenharmony_cido {							\
4862306a36Sopenharmony_ci	__asm__ __volatile__(				\
4962306a36Sopenharmony_ci		".set	push\n\t"			\
5062306a36Sopenharmony_ci		_ASM_SET_CRC(op, SZ, TYPE)		\
5162306a36Sopenharmony_ci		#op "	%0, %1, %0\n\t"			\
5262306a36Sopenharmony_ci		_ASM_UNSET_CRC(op, SZ, TYPE)		\
5362306a36Sopenharmony_ci		".set	pop"				\
5462306a36Sopenharmony_ci		: "+r" (crc)				\
5562306a36Sopenharmony_ci		: "r" (value));				\
5662306a36Sopenharmony_ci} while (0)
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci#define _CRC32_crc32b(crc, value)	__CRC32(crc, value, crc32b, 0, 0)
5962306a36Sopenharmony_ci#define _CRC32_crc32h(crc, value)	__CRC32(crc, value, crc32h, 1, 0)
6062306a36Sopenharmony_ci#define _CRC32_crc32w(crc, value)	__CRC32(crc, value, crc32w, 2, 0)
6162306a36Sopenharmony_ci#define _CRC32_crc32d(crc, value)	__CRC32(crc, value, crc32d, 3, 0)
6262306a36Sopenharmony_ci#define _CRC32_crc32cb(crc, value)	__CRC32(crc, value, crc32cb, 0, 1)
6362306a36Sopenharmony_ci#define _CRC32_crc32ch(crc, value)	__CRC32(crc, value, crc32ch, 1, 1)
6462306a36Sopenharmony_ci#define _CRC32_crc32cw(crc, value)	__CRC32(crc, value, crc32cw, 2, 1)
6562306a36Sopenharmony_ci#define _CRC32_crc32cd(crc, value)	__CRC32(crc, value, crc32cd, 3, 1)
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci#define _CRC32(crc, value, size, op) \
6862306a36Sopenharmony_ci	_CRC32_##op##size(crc, value)
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci#define CRC32(crc, value, size) \
7162306a36Sopenharmony_ci	_CRC32(crc, value, size, crc32)
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci#define CRC32C(crc, value, size) \
7462306a36Sopenharmony_ci	_CRC32(crc, value, size, crc32c)
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic u32 crc32_mips_le_hw(u32 crc_, const u8 *p, unsigned int len)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	u32 crc = crc_;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci#ifdef CONFIG_64BIT
8162306a36Sopenharmony_ci	while (len >= sizeof(u64)) {
8262306a36Sopenharmony_ci		u64 value = get_unaligned_le64(p);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci		CRC32(crc, value, d);
8562306a36Sopenharmony_ci		p += sizeof(u64);
8662306a36Sopenharmony_ci		len -= sizeof(u64);
8762306a36Sopenharmony_ci	}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	if (len & sizeof(u32)) {
9062306a36Sopenharmony_ci#else /* !CONFIG_64BIT */
9162306a36Sopenharmony_ci	while (len >= sizeof(u32)) {
9262306a36Sopenharmony_ci#endif
9362306a36Sopenharmony_ci		u32 value = get_unaligned_le32(p);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci		CRC32(crc, value, w);
9662306a36Sopenharmony_ci		p += sizeof(u32);
9762306a36Sopenharmony_ci		len -= sizeof(u32);
9862306a36Sopenharmony_ci	}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	if (len & sizeof(u16)) {
10162306a36Sopenharmony_ci		u16 value = get_unaligned_le16(p);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci		CRC32(crc, value, h);
10462306a36Sopenharmony_ci		p += sizeof(u16);
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	if (len & sizeof(u8)) {
10862306a36Sopenharmony_ci		u8 value = *p++;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci		CRC32(crc, value, b);
11162306a36Sopenharmony_ci	}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	return crc;
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic u32 crc32c_mips_le_hw(u32 crc_, const u8 *p, unsigned int len)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	u32 crc = crc_;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci#ifdef CONFIG_64BIT
12162306a36Sopenharmony_ci	while (len >= sizeof(u64)) {
12262306a36Sopenharmony_ci		u64 value = get_unaligned_le64(p);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci		CRC32C(crc, value, d);
12562306a36Sopenharmony_ci		p += sizeof(u64);
12662306a36Sopenharmony_ci		len -= sizeof(u64);
12762306a36Sopenharmony_ci	}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	if (len & sizeof(u32)) {
13062306a36Sopenharmony_ci#else /* !CONFIG_64BIT */
13162306a36Sopenharmony_ci	while (len >= sizeof(u32)) {
13262306a36Sopenharmony_ci#endif
13362306a36Sopenharmony_ci		u32 value = get_unaligned_le32(p);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci		CRC32C(crc, value, w);
13662306a36Sopenharmony_ci		p += sizeof(u32);
13762306a36Sopenharmony_ci		len -= sizeof(u32);
13862306a36Sopenharmony_ci	}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	if (len & sizeof(u16)) {
14162306a36Sopenharmony_ci		u16 value = get_unaligned_le16(p);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci		CRC32C(crc, value, h);
14462306a36Sopenharmony_ci		p += sizeof(u16);
14562306a36Sopenharmony_ci	}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	if (len & sizeof(u8)) {
14862306a36Sopenharmony_ci		u8 value = *p++;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci		CRC32C(crc, value, b);
15162306a36Sopenharmony_ci	}
15262306a36Sopenharmony_ci	return crc;
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci#define CHKSUM_BLOCK_SIZE	1
15662306a36Sopenharmony_ci#define CHKSUM_DIGEST_SIZE	4
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_cistruct chksum_ctx {
15962306a36Sopenharmony_ci	u32 key;
16062306a36Sopenharmony_ci};
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_cistruct chksum_desc_ctx {
16362306a36Sopenharmony_ci	u32 crc;
16462306a36Sopenharmony_ci};
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cistatic int chksum_init(struct shash_desc *desc)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	struct chksum_ctx *mctx = crypto_shash_ctx(desc->tfm);
16962306a36Sopenharmony_ci	struct chksum_desc_ctx *ctx = shash_desc_ctx(desc);
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	ctx->crc = mctx->key;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	return 0;
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci/*
17762306a36Sopenharmony_ci * Setting the seed allows arbitrary accumulators and flexible XOR policy
17862306a36Sopenharmony_ci * If your algorithm starts with ~0, then XOR with ~0 before you set
17962306a36Sopenharmony_ci * the seed.
18062306a36Sopenharmony_ci */
18162306a36Sopenharmony_cistatic int chksum_setkey(struct crypto_shash *tfm, const u8 *key,
18262306a36Sopenharmony_ci			 unsigned int keylen)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	struct chksum_ctx *mctx = crypto_shash_ctx(tfm);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	if (keylen != sizeof(mctx->key))
18762306a36Sopenharmony_ci		return -EINVAL;
18862306a36Sopenharmony_ci	mctx->key = get_unaligned_le32(key);
18962306a36Sopenharmony_ci	return 0;
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic int chksum_update(struct shash_desc *desc, const u8 *data,
19362306a36Sopenharmony_ci			 unsigned int length)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	struct chksum_desc_ctx *ctx = shash_desc_ctx(desc);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	ctx->crc = crc32_mips_le_hw(ctx->crc, data, length);
19862306a36Sopenharmony_ci	return 0;
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic int chksumc_update(struct shash_desc *desc, const u8 *data,
20262306a36Sopenharmony_ci			 unsigned int length)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	struct chksum_desc_ctx *ctx = shash_desc_ctx(desc);
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	ctx->crc = crc32c_mips_le_hw(ctx->crc, data, length);
20762306a36Sopenharmony_ci	return 0;
20862306a36Sopenharmony_ci}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_cistatic int chksum_final(struct shash_desc *desc, u8 *out)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	struct chksum_desc_ctx *ctx = shash_desc_ctx(desc);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	put_unaligned_le32(ctx->crc, out);
21562306a36Sopenharmony_ci	return 0;
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic int chksumc_final(struct shash_desc *desc, u8 *out)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	struct chksum_desc_ctx *ctx = shash_desc_ctx(desc);
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	put_unaligned_le32(~ctx->crc, out);
22362306a36Sopenharmony_ci	return 0;
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic int __chksum_finup(u32 crc, const u8 *data, unsigned int len, u8 *out)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	put_unaligned_le32(crc32_mips_le_hw(crc, data, len), out);
22962306a36Sopenharmony_ci	return 0;
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_cistatic int __chksumc_finup(u32 crc, const u8 *data, unsigned int len, u8 *out)
23362306a36Sopenharmony_ci{
23462306a36Sopenharmony_ci	put_unaligned_le32(~crc32c_mips_le_hw(crc, data, len), out);
23562306a36Sopenharmony_ci	return 0;
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_cistatic int chksum_finup(struct shash_desc *desc, const u8 *data,
23962306a36Sopenharmony_ci			unsigned int len, u8 *out)
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci	struct chksum_desc_ctx *ctx = shash_desc_ctx(desc);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	return __chksum_finup(ctx->crc, data, len, out);
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_cistatic int chksumc_finup(struct shash_desc *desc, const u8 *data,
24762306a36Sopenharmony_ci			unsigned int len, u8 *out)
24862306a36Sopenharmony_ci{
24962306a36Sopenharmony_ci	struct chksum_desc_ctx *ctx = shash_desc_ctx(desc);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	return __chksumc_finup(ctx->crc, data, len, out);
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_cistatic int chksum_digest(struct shash_desc *desc, const u8 *data,
25562306a36Sopenharmony_ci			 unsigned int length, u8 *out)
25662306a36Sopenharmony_ci{
25762306a36Sopenharmony_ci	struct chksum_ctx *mctx = crypto_shash_ctx(desc->tfm);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	return __chksum_finup(mctx->key, data, length, out);
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_cistatic int chksumc_digest(struct shash_desc *desc, const u8 *data,
26362306a36Sopenharmony_ci			 unsigned int length, u8 *out)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	struct chksum_ctx *mctx = crypto_shash_ctx(desc->tfm);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	return __chksumc_finup(mctx->key, data, length, out);
26862306a36Sopenharmony_ci}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_cistatic int chksum_cra_init(struct crypto_tfm *tfm)
27162306a36Sopenharmony_ci{
27262306a36Sopenharmony_ci	struct chksum_ctx *mctx = crypto_tfm_ctx(tfm);
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	mctx->key = ~0;
27562306a36Sopenharmony_ci	return 0;
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_cistatic struct shash_alg crc32_alg = {
27962306a36Sopenharmony_ci	.digestsize		=	CHKSUM_DIGEST_SIZE,
28062306a36Sopenharmony_ci	.setkey			=	chksum_setkey,
28162306a36Sopenharmony_ci	.init			=	chksum_init,
28262306a36Sopenharmony_ci	.update			=	chksum_update,
28362306a36Sopenharmony_ci	.final			=	chksum_final,
28462306a36Sopenharmony_ci	.finup			=	chksum_finup,
28562306a36Sopenharmony_ci	.digest			=	chksum_digest,
28662306a36Sopenharmony_ci	.descsize		=	sizeof(struct chksum_desc_ctx),
28762306a36Sopenharmony_ci	.base			=	{
28862306a36Sopenharmony_ci		.cra_name		=	"crc32",
28962306a36Sopenharmony_ci		.cra_driver_name	=	"crc32-mips-hw",
29062306a36Sopenharmony_ci		.cra_priority		=	300,
29162306a36Sopenharmony_ci		.cra_flags		=	CRYPTO_ALG_OPTIONAL_KEY,
29262306a36Sopenharmony_ci		.cra_blocksize		=	CHKSUM_BLOCK_SIZE,
29362306a36Sopenharmony_ci		.cra_alignmask		=	0,
29462306a36Sopenharmony_ci		.cra_ctxsize		=	sizeof(struct chksum_ctx),
29562306a36Sopenharmony_ci		.cra_module		=	THIS_MODULE,
29662306a36Sopenharmony_ci		.cra_init		=	chksum_cra_init,
29762306a36Sopenharmony_ci	}
29862306a36Sopenharmony_ci};
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_cistatic struct shash_alg crc32c_alg = {
30162306a36Sopenharmony_ci	.digestsize		=	CHKSUM_DIGEST_SIZE,
30262306a36Sopenharmony_ci	.setkey			=	chksum_setkey,
30362306a36Sopenharmony_ci	.init			=	chksum_init,
30462306a36Sopenharmony_ci	.update			=	chksumc_update,
30562306a36Sopenharmony_ci	.final			=	chksumc_final,
30662306a36Sopenharmony_ci	.finup			=	chksumc_finup,
30762306a36Sopenharmony_ci	.digest			=	chksumc_digest,
30862306a36Sopenharmony_ci	.descsize		=	sizeof(struct chksum_desc_ctx),
30962306a36Sopenharmony_ci	.base			=	{
31062306a36Sopenharmony_ci		.cra_name		=	"crc32c",
31162306a36Sopenharmony_ci		.cra_driver_name	=	"crc32c-mips-hw",
31262306a36Sopenharmony_ci		.cra_priority		=	300,
31362306a36Sopenharmony_ci		.cra_flags		=	CRYPTO_ALG_OPTIONAL_KEY,
31462306a36Sopenharmony_ci		.cra_blocksize		=	CHKSUM_BLOCK_SIZE,
31562306a36Sopenharmony_ci		.cra_alignmask		=	0,
31662306a36Sopenharmony_ci		.cra_ctxsize		=	sizeof(struct chksum_ctx),
31762306a36Sopenharmony_ci		.cra_module		=	THIS_MODULE,
31862306a36Sopenharmony_ci		.cra_init		=	chksum_cra_init,
31962306a36Sopenharmony_ci	}
32062306a36Sopenharmony_ci};
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_cistatic int __init crc32_mod_init(void)
32362306a36Sopenharmony_ci{
32462306a36Sopenharmony_ci	int err;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	err = crypto_register_shash(&crc32_alg);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	if (err)
32962306a36Sopenharmony_ci		return err;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	err = crypto_register_shash(&crc32c_alg);
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	if (err) {
33462306a36Sopenharmony_ci		crypto_unregister_shash(&crc32_alg);
33562306a36Sopenharmony_ci		return err;
33662306a36Sopenharmony_ci	}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	return 0;
33962306a36Sopenharmony_ci}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_cistatic void __exit crc32_mod_exit(void)
34262306a36Sopenharmony_ci{
34362306a36Sopenharmony_ci	crypto_unregister_shash(&crc32_alg);
34462306a36Sopenharmony_ci	crypto_unregister_shash(&crc32c_alg);
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ciMODULE_AUTHOR("Marcin Nowakowski <marcin.nowakowski@mips.com");
34862306a36Sopenharmony_ciMODULE_DESCRIPTION("CRC32 and CRC32C using optional MIPS instructions");
34962306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_cimodule_cpu_feature_match(MIPS_CRC32, crc32_mod_init);
35262306a36Sopenharmony_cimodule_exit(crc32_mod_exit);
353