18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/* Glue code for SHA512 hashing optimized for sparc64 crypto opcodes.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * This is based largely upon crypto/sha512_generic.c
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (c) Jean-Luc Cooke <jlcooke@certainkey.com>
78c2ecf20Sopenharmony_ci * Copyright (c) Andrew McDonald <andrew@mcdonald.org.uk>
88c2ecf20Sopenharmony_ci * Copyright (c) 2003 Kyle McMartin <kyle@debian.org>
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <crypto/internal/hash.h>
148c2ecf20Sopenharmony_ci#include <linux/init.h>
158c2ecf20Sopenharmony_ci#include <linux/module.h>
168c2ecf20Sopenharmony_ci#include <linux/mm.h>
178c2ecf20Sopenharmony_ci#include <linux/types.h>
188c2ecf20Sopenharmony_ci#include <crypto/sha.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include <asm/pstate.h>
218c2ecf20Sopenharmony_ci#include <asm/elf.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include "opcodes.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ciasmlinkage void sha512_sparc64_transform(u64 *digest, const char *data,
268c2ecf20Sopenharmony_ci					 unsigned int rounds);
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic int sha512_sparc64_init(struct shash_desc *desc)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	struct sha512_state *sctx = shash_desc_ctx(desc);
318c2ecf20Sopenharmony_ci	sctx->state[0] = SHA512_H0;
328c2ecf20Sopenharmony_ci	sctx->state[1] = SHA512_H1;
338c2ecf20Sopenharmony_ci	sctx->state[2] = SHA512_H2;
348c2ecf20Sopenharmony_ci	sctx->state[3] = SHA512_H3;
358c2ecf20Sopenharmony_ci	sctx->state[4] = SHA512_H4;
368c2ecf20Sopenharmony_ci	sctx->state[5] = SHA512_H5;
378c2ecf20Sopenharmony_ci	sctx->state[6] = SHA512_H6;
388c2ecf20Sopenharmony_ci	sctx->state[7] = SHA512_H7;
398c2ecf20Sopenharmony_ci	sctx->count[0] = sctx->count[1] = 0;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	return 0;
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic int sha384_sparc64_init(struct shash_desc *desc)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	struct sha512_state *sctx = shash_desc_ctx(desc);
478c2ecf20Sopenharmony_ci	sctx->state[0] = SHA384_H0;
488c2ecf20Sopenharmony_ci	sctx->state[1] = SHA384_H1;
498c2ecf20Sopenharmony_ci	sctx->state[2] = SHA384_H2;
508c2ecf20Sopenharmony_ci	sctx->state[3] = SHA384_H3;
518c2ecf20Sopenharmony_ci	sctx->state[4] = SHA384_H4;
528c2ecf20Sopenharmony_ci	sctx->state[5] = SHA384_H5;
538c2ecf20Sopenharmony_ci	sctx->state[6] = SHA384_H6;
548c2ecf20Sopenharmony_ci	sctx->state[7] = SHA384_H7;
558c2ecf20Sopenharmony_ci	sctx->count[0] = sctx->count[1] = 0;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	return 0;
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic void __sha512_sparc64_update(struct sha512_state *sctx, const u8 *data,
618c2ecf20Sopenharmony_ci				    unsigned int len, unsigned int partial)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	unsigned int done = 0;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	if ((sctx->count[0] += len) < len)
668c2ecf20Sopenharmony_ci		sctx->count[1]++;
678c2ecf20Sopenharmony_ci	if (partial) {
688c2ecf20Sopenharmony_ci		done = SHA512_BLOCK_SIZE - partial;
698c2ecf20Sopenharmony_ci		memcpy(sctx->buf + partial, data, done);
708c2ecf20Sopenharmony_ci		sha512_sparc64_transform(sctx->state, sctx->buf, 1);
718c2ecf20Sopenharmony_ci	}
728c2ecf20Sopenharmony_ci	if (len - done >= SHA512_BLOCK_SIZE) {
738c2ecf20Sopenharmony_ci		const unsigned int rounds = (len - done) / SHA512_BLOCK_SIZE;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci		sha512_sparc64_transform(sctx->state, data + done, rounds);
768c2ecf20Sopenharmony_ci		done += rounds * SHA512_BLOCK_SIZE;
778c2ecf20Sopenharmony_ci	}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	memcpy(sctx->buf, data + done, len - done);
808c2ecf20Sopenharmony_ci}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic int sha512_sparc64_update(struct shash_desc *desc, const u8 *data,
838c2ecf20Sopenharmony_ci				 unsigned int len)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	struct sha512_state *sctx = shash_desc_ctx(desc);
868c2ecf20Sopenharmony_ci	unsigned int partial = sctx->count[0] % SHA512_BLOCK_SIZE;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	/* Handle the fast case right here */
898c2ecf20Sopenharmony_ci	if (partial + len < SHA512_BLOCK_SIZE) {
908c2ecf20Sopenharmony_ci		if ((sctx->count[0] += len) < len)
918c2ecf20Sopenharmony_ci			sctx->count[1]++;
928c2ecf20Sopenharmony_ci		memcpy(sctx->buf + partial, data, len);
938c2ecf20Sopenharmony_ci	} else
948c2ecf20Sopenharmony_ci		__sha512_sparc64_update(sctx, data, len, partial);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	return 0;
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic int sha512_sparc64_final(struct shash_desc *desc, u8 *out)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	struct sha512_state *sctx = shash_desc_ctx(desc);
1028c2ecf20Sopenharmony_ci	unsigned int i, index, padlen;
1038c2ecf20Sopenharmony_ci	__be64 *dst = (__be64 *)out;
1048c2ecf20Sopenharmony_ci	__be64 bits[2];
1058c2ecf20Sopenharmony_ci	static const u8 padding[SHA512_BLOCK_SIZE] = { 0x80, };
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	/* Save number of bits */
1088c2ecf20Sopenharmony_ci	bits[1] = cpu_to_be64(sctx->count[0] << 3);
1098c2ecf20Sopenharmony_ci	bits[0] = cpu_to_be64(sctx->count[1] << 3 | sctx->count[0] >> 61);
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	/* Pad out to 112 mod 128 and append length */
1128c2ecf20Sopenharmony_ci	index = sctx->count[0] % SHA512_BLOCK_SIZE;
1138c2ecf20Sopenharmony_ci	padlen = (index < 112) ? (112 - index) : ((SHA512_BLOCK_SIZE+112) - index);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	/* We need to fill a whole block for __sha512_sparc64_update() */
1168c2ecf20Sopenharmony_ci	if (padlen <= 112) {
1178c2ecf20Sopenharmony_ci		if ((sctx->count[0] += padlen) < padlen)
1188c2ecf20Sopenharmony_ci			sctx->count[1]++;
1198c2ecf20Sopenharmony_ci		memcpy(sctx->buf + index, padding, padlen);
1208c2ecf20Sopenharmony_ci	} else {
1218c2ecf20Sopenharmony_ci		__sha512_sparc64_update(sctx, padding, padlen, index);
1228c2ecf20Sopenharmony_ci	}
1238c2ecf20Sopenharmony_ci	__sha512_sparc64_update(sctx, (const u8 *)&bits, sizeof(bits), 112);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	/* Store state in digest */
1268c2ecf20Sopenharmony_ci	for (i = 0; i < 8; i++)
1278c2ecf20Sopenharmony_ci		dst[i] = cpu_to_be64(sctx->state[i]);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	/* Wipe context */
1308c2ecf20Sopenharmony_ci	memset(sctx, 0, sizeof(*sctx));
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	return 0;
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistatic int sha384_sparc64_final(struct shash_desc *desc, u8 *hash)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	u8 D[64];
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	sha512_sparc64_final(desc, D);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	memcpy(hash, D, 48);
1428c2ecf20Sopenharmony_ci	memzero_explicit(D, 64);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	return 0;
1458c2ecf20Sopenharmony_ci}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_cistatic struct shash_alg sha512 = {
1488c2ecf20Sopenharmony_ci	.digestsize	=	SHA512_DIGEST_SIZE,
1498c2ecf20Sopenharmony_ci	.init		=	sha512_sparc64_init,
1508c2ecf20Sopenharmony_ci	.update		=	sha512_sparc64_update,
1518c2ecf20Sopenharmony_ci	.final		=	sha512_sparc64_final,
1528c2ecf20Sopenharmony_ci	.descsize	=	sizeof(struct sha512_state),
1538c2ecf20Sopenharmony_ci	.base		=	{
1548c2ecf20Sopenharmony_ci		.cra_name	=	"sha512",
1558c2ecf20Sopenharmony_ci		.cra_driver_name=	"sha512-sparc64",
1568c2ecf20Sopenharmony_ci		.cra_priority	=	SPARC_CR_OPCODE_PRIORITY,
1578c2ecf20Sopenharmony_ci		.cra_blocksize	=	SHA512_BLOCK_SIZE,
1588c2ecf20Sopenharmony_ci		.cra_module	=	THIS_MODULE,
1598c2ecf20Sopenharmony_ci	}
1608c2ecf20Sopenharmony_ci};
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cistatic struct shash_alg sha384 = {
1638c2ecf20Sopenharmony_ci	.digestsize	=	SHA384_DIGEST_SIZE,
1648c2ecf20Sopenharmony_ci	.init		=	sha384_sparc64_init,
1658c2ecf20Sopenharmony_ci	.update		=	sha512_sparc64_update,
1668c2ecf20Sopenharmony_ci	.final		=	sha384_sparc64_final,
1678c2ecf20Sopenharmony_ci	.descsize	=	sizeof(struct sha512_state),
1688c2ecf20Sopenharmony_ci	.base		=	{
1698c2ecf20Sopenharmony_ci		.cra_name	=	"sha384",
1708c2ecf20Sopenharmony_ci		.cra_driver_name=	"sha384-sparc64",
1718c2ecf20Sopenharmony_ci		.cra_priority	=	SPARC_CR_OPCODE_PRIORITY,
1728c2ecf20Sopenharmony_ci		.cra_blocksize	=	SHA384_BLOCK_SIZE,
1738c2ecf20Sopenharmony_ci		.cra_module	=	THIS_MODULE,
1748c2ecf20Sopenharmony_ci	}
1758c2ecf20Sopenharmony_ci};
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_cistatic bool __init sparc64_has_sha512_opcode(void)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	unsigned long cfr;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	if (!(sparc64_elf_hwcap & HWCAP_SPARC_CRYPTO))
1828c2ecf20Sopenharmony_ci		return false;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	__asm__ __volatile__("rd %%asr26, %0" : "=r" (cfr));
1858c2ecf20Sopenharmony_ci	if (!(cfr & CFR_SHA512))
1868c2ecf20Sopenharmony_ci		return false;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	return true;
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_cistatic int __init sha512_sparc64_mod_init(void)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	if (sparc64_has_sha512_opcode()) {
1948c2ecf20Sopenharmony_ci		int ret = crypto_register_shash(&sha384);
1958c2ecf20Sopenharmony_ci		if (ret < 0)
1968c2ecf20Sopenharmony_ci			return ret;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci		ret = crypto_register_shash(&sha512);
1998c2ecf20Sopenharmony_ci		if (ret < 0) {
2008c2ecf20Sopenharmony_ci			crypto_unregister_shash(&sha384);
2018c2ecf20Sopenharmony_ci			return ret;
2028c2ecf20Sopenharmony_ci		}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci		pr_info("Using sparc64 sha512 opcode optimized SHA-512/SHA-384 implementation\n");
2058c2ecf20Sopenharmony_ci		return 0;
2068c2ecf20Sopenharmony_ci	}
2078c2ecf20Sopenharmony_ci	pr_info("sparc64 sha512 opcode not available.\n");
2088c2ecf20Sopenharmony_ci	return -ENODEV;
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cistatic void __exit sha512_sparc64_mod_fini(void)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	crypto_unregister_shash(&sha384);
2148c2ecf20Sopenharmony_ci	crypto_unregister_shash(&sha512);
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_cimodule_init(sha512_sparc64_mod_init);
2188c2ecf20Sopenharmony_cimodule_exit(sha512_sparc64_mod_fini);
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
2218c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SHA-384 and SHA-512 Secure Hash Algorithm, sparc64 sha512 opcode accelerated");
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ciMODULE_ALIAS_CRYPTO("sha384");
2248c2ecf20Sopenharmony_ciMODULE_ALIAS_CRYPTO("sha512");
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci#include "crop_devid.c"
227