162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/* Glue code for MD5 hashing optimized for sparc64 crypto opcodes.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * This is based largely upon arch/x86/crypto/sha1_ssse3_glue.c
562306a36Sopenharmony_ci * and crypto/md5.c which are:
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (c) Alan Smithee.
862306a36Sopenharmony_ci * Copyright (c) Andrew McDonald <andrew@mcdonald.org.uk>
962306a36Sopenharmony_ci * Copyright (c) Jean-Francois Dive <jef@linuxbe.org>
1062306a36Sopenharmony_ci * Copyright (c) Mathias Krause <minipli@googlemail.com>
1162306a36Sopenharmony_ci * Copyright (c) Cryptoapi developers.
1262306a36Sopenharmony_ci * Copyright (c) 2002 James Morris <jmorris@intercode.com.au>
1362306a36Sopenharmony_ci */
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <crypto/internal/hash.h>
1862306a36Sopenharmony_ci#include <linux/init.h>
1962306a36Sopenharmony_ci#include <linux/module.h>
2062306a36Sopenharmony_ci#include <linux/mm.h>
2162306a36Sopenharmony_ci#include <linux/types.h>
2262306a36Sopenharmony_ci#include <crypto/md5.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include <asm/pstate.h>
2562306a36Sopenharmony_ci#include <asm/elf.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#include "opcodes.h"
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ciasmlinkage void md5_sparc64_transform(u32 *digest, const char *data,
3062306a36Sopenharmony_ci				      unsigned int rounds);
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic int md5_sparc64_init(struct shash_desc *desc)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	struct md5_state *mctx = shash_desc_ctx(desc);
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	mctx->hash[0] = MD5_H0;
3762306a36Sopenharmony_ci	mctx->hash[1] = MD5_H1;
3862306a36Sopenharmony_ci	mctx->hash[2] = MD5_H2;
3962306a36Sopenharmony_ci	mctx->hash[3] = MD5_H3;
4062306a36Sopenharmony_ci	le32_to_cpu_array(mctx->hash, 4);
4162306a36Sopenharmony_ci	mctx->byte_count = 0;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	return 0;
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic void __md5_sparc64_update(struct md5_state *sctx, const u8 *data,
4762306a36Sopenharmony_ci				 unsigned int len, unsigned int partial)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	unsigned int done = 0;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	sctx->byte_count += len;
5262306a36Sopenharmony_ci	if (partial) {
5362306a36Sopenharmony_ci		done = MD5_HMAC_BLOCK_SIZE - partial;
5462306a36Sopenharmony_ci		memcpy((u8 *)sctx->block + partial, data, done);
5562306a36Sopenharmony_ci		md5_sparc64_transform(sctx->hash, (u8 *)sctx->block, 1);
5662306a36Sopenharmony_ci	}
5762306a36Sopenharmony_ci	if (len - done >= MD5_HMAC_BLOCK_SIZE) {
5862306a36Sopenharmony_ci		const unsigned int rounds = (len - done) / MD5_HMAC_BLOCK_SIZE;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci		md5_sparc64_transform(sctx->hash, data + done, rounds);
6162306a36Sopenharmony_ci		done += rounds * MD5_HMAC_BLOCK_SIZE;
6262306a36Sopenharmony_ci	}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	memcpy(sctx->block, data + done, len - done);
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic int md5_sparc64_update(struct shash_desc *desc, const u8 *data,
6862306a36Sopenharmony_ci			      unsigned int len)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	struct md5_state *sctx = shash_desc_ctx(desc);
7162306a36Sopenharmony_ci	unsigned int partial = sctx->byte_count % MD5_HMAC_BLOCK_SIZE;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	/* Handle the fast case right here */
7462306a36Sopenharmony_ci	if (partial + len < MD5_HMAC_BLOCK_SIZE) {
7562306a36Sopenharmony_ci		sctx->byte_count += len;
7662306a36Sopenharmony_ci		memcpy((u8 *)sctx->block + partial, data, len);
7762306a36Sopenharmony_ci	} else
7862306a36Sopenharmony_ci		__md5_sparc64_update(sctx, data, len, partial);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	return 0;
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci/* Add padding and return the message digest. */
8462306a36Sopenharmony_cistatic int md5_sparc64_final(struct shash_desc *desc, u8 *out)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	struct md5_state *sctx = shash_desc_ctx(desc);
8762306a36Sopenharmony_ci	unsigned int i, index, padlen;
8862306a36Sopenharmony_ci	u32 *dst = (u32 *)out;
8962306a36Sopenharmony_ci	__le64 bits;
9062306a36Sopenharmony_ci	static const u8 padding[MD5_HMAC_BLOCK_SIZE] = { 0x80, };
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	bits = cpu_to_le64(sctx->byte_count << 3);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	/* Pad out to 56 mod 64 and append length */
9562306a36Sopenharmony_ci	index = sctx->byte_count % MD5_HMAC_BLOCK_SIZE;
9662306a36Sopenharmony_ci	padlen = (index < 56) ? (56 - index) : ((MD5_HMAC_BLOCK_SIZE+56) - index);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	/* We need to fill a whole block for __md5_sparc64_update() */
9962306a36Sopenharmony_ci	if (padlen <= 56) {
10062306a36Sopenharmony_ci		sctx->byte_count += padlen;
10162306a36Sopenharmony_ci		memcpy((u8 *)sctx->block + index, padding, padlen);
10262306a36Sopenharmony_ci	} else {
10362306a36Sopenharmony_ci		__md5_sparc64_update(sctx, padding, padlen, index);
10462306a36Sopenharmony_ci	}
10562306a36Sopenharmony_ci	__md5_sparc64_update(sctx, (const u8 *)&bits, sizeof(bits), 56);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	/* Store state in digest */
10862306a36Sopenharmony_ci	for (i = 0; i < MD5_HASH_WORDS; i++)
10962306a36Sopenharmony_ci		dst[i] = sctx->hash[i];
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	/* Wipe context */
11262306a36Sopenharmony_ci	memset(sctx, 0, sizeof(*sctx));
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	return 0;
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic int md5_sparc64_export(struct shash_desc *desc, void *out)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	struct md5_state *sctx = shash_desc_ctx(desc);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	memcpy(out, sctx, sizeof(*sctx));
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	return 0;
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic int md5_sparc64_import(struct shash_desc *desc, const void *in)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	struct md5_state *sctx = shash_desc_ctx(desc);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	memcpy(sctx, in, sizeof(*sctx));
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	return 0;
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic struct shash_alg alg = {
13662306a36Sopenharmony_ci	.digestsize	=	MD5_DIGEST_SIZE,
13762306a36Sopenharmony_ci	.init		=	md5_sparc64_init,
13862306a36Sopenharmony_ci	.update		=	md5_sparc64_update,
13962306a36Sopenharmony_ci	.final		=	md5_sparc64_final,
14062306a36Sopenharmony_ci	.export		=	md5_sparc64_export,
14162306a36Sopenharmony_ci	.import		=	md5_sparc64_import,
14262306a36Sopenharmony_ci	.descsize	=	sizeof(struct md5_state),
14362306a36Sopenharmony_ci	.statesize	=	sizeof(struct md5_state),
14462306a36Sopenharmony_ci	.base		=	{
14562306a36Sopenharmony_ci		.cra_name	=	"md5",
14662306a36Sopenharmony_ci		.cra_driver_name=	"md5-sparc64",
14762306a36Sopenharmony_ci		.cra_priority	=	SPARC_CR_OPCODE_PRIORITY,
14862306a36Sopenharmony_ci		.cra_blocksize	=	MD5_HMAC_BLOCK_SIZE,
14962306a36Sopenharmony_ci		.cra_module	=	THIS_MODULE,
15062306a36Sopenharmony_ci	}
15162306a36Sopenharmony_ci};
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic bool __init sparc64_has_md5_opcode(void)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	unsigned long cfr;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	if (!(sparc64_elf_hwcap & HWCAP_SPARC_CRYPTO))
15862306a36Sopenharmony_ci		return false;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	__asm__ __volatile__("rd %%asr26, %0" : "=r" (cfr));
16162306a36Sopenharmony_ci	if (!(cfr & CFR_MD5))
16262306a36Sopenharmony_ci		return false;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	return true;
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_cistatic int __init md5_sparc64_mod_init(void)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	if (sparc64_has_md5_opcode()) {
17062306a36Sopenharmony_ci		pr_info("Using sparc64 md5 opcode optimized MD5 implementation\n");
17162306a36Sopenharmony_ci		return crypto_register_shash(&alg);
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci	pr_info("sparc64 md5 opcode not available.\n");
17462306a36Sopenharmony_ci	return -ENODEV;
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistatic void __exit md5_sparc64_mod_fini(void)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	crypto_unregister_shash(&alg);
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cimodule_init(md5_sparc64_mod_init);
18362306a36Sopenharmony_cimodule_exit(md5_sparc64_mod_fini);
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
18662306a36Sopenharmony_ciMODULE_DESCRIPTION("MD5 Message Digest Algorithm, sparc64 md5 opcode accelerated");
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("md5");
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci#include "crop_devid.c"
191