1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Glue code for MD5 hashing optimized for sparc64 crypto opcodes.
3  *
4  * This is based largely upon arch/x86/crypto/sha1_ssse3_glue.c
5  * and crypto/md5.c which are:
6  *
7  * Copyright (c) Alan Smithee.
8  * Copyright (c) Andrew McDonald <andrew@mcdonald.org.uk>
9  * Copyright (c) Jean-Francois Dive <jef@linuxbe.org>
10  * Copyright (c) Mathias Krause <minipli@googlemail.com>
11  * Copyright (c) Cryptoapi developers.
12  * Copyright (c) 2002 James Morris <jmorris@intercode.com.au>
13  */
14 
15 #define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
16 
17 #include <asm/elf.h>
18 #include <asm/opcodes.h>
19 #include <asm/pstate.h>
20 #include <crypto/internal/hash.h>
21 #include <crypto/md5.h>
22 #include <linux/errno.h>
23 #include <linux/kernel.h>
24 #include <linux/module.h>
25 #include <linux/string.h>
26 #include <linux/unaligned.h>
27 
28 struct sparc_md5_state {
29 	__le32 hash[MD5_HASH_WORDS];
30 	u64 byte_count;
31 };
32 
33 asmlinkage void md5_sparc64_transform(__le32 *digest, const char *data,
34 				      unsigned int rounds);
35 
36 static int md5_sparc64_init(struct shash_desc *desc)
37 {
38 	struct sparc_md5_state *mctx = shash_desc_ctx(desc);
39 
40 	mctx->hash[0] = cpu_to_le32(MD5_H0);
41 	mctx->hash[1] = cpu_to_le32(MD5_H1);
42 	mctx->hash[2] = cpu_to_le32(MD5_H2);
43 	mctx->hash[3] = cpu_to_le32(MD5_H3);
44 	mctx->byte_count = 0;
45 
46 	return 0;
47 }
48 
49 static int md5_sparc64_update(struct shash_desc *desc, const u8 *data,
50 			      unsigned int len)
51 {
52 	struct sparc_md5_state *sctx = shash_desc_ctx(desc);
53 
54 	sctx->byte_count += round_down(len, MD5_HMAC_BLOCK_SIZE);
55 	md5_sparc64_transform(sctx->hash, data, len / MD5_HMAC_BLOCK_SIZE);
56 	return len - round_down(len, MD5_HMAC_BLOCK_SIZE);
57 }
58 
59 /* Add padding and return the message digest. */
60 static int md5_sparc64_finup(struct shash_desc *desc, const u8 *src,
61 			     unsigned int offset, u8 *out)
62 {
63 	struct sparc_md5_state *sctx = shash_desc_ctx(desc);
64 	__le64 block[MD5_BLOCK_WORDS] = {};
65 	u8 *p = memcpy(block, src, offset);
66 	__le32 *dst = (__le32 *)out;
67 	__le64 *pbits;
68 	int i;
69 
70 	src = p;
71 	p += offset;
72 	*p++ = 0x80;
73 	sctx->byte_count += offset;
74 	pbits = &block[(MD5_BLOCK_WORDS / (offset > 55 ? 1 : 2)) - 1];
75 	*pbits = cpu_to_le64(sctx->byte_count << 3);
76 	md5_sparc64_transform(sctx->hash, src, (pbits - block + 1) / 8);
77 	memzero_explicit(block, sizeof(block));
78 
79 	/* Store state in digest */
80 	for (i = 0; i < MD5_HASH_WORDS; i++)
81 		dst[i] = sctx->hash[i];
82 
83 	return 0;
84 }
85 
86 static int md5_sparc64_export(struct shash_desc *desc, void *out)
87 {
88 	struct sparc_md5_state *sctx = shash_desc_ctx(desc);
89 	union {
90 		u8 *u8;
91 		u32 *u32;
92 		u64 *u64;
93 	} p = { .u8 = out };
94 	int i;
95 
96 	for (i = 0; i < MD5_HASH_WORDS; i++)
97 		put_unaligned(le32_to_cpu(sctx->hash[i]), p.u32++);
98 	put_unaligned(sctx->byte_count, p.u64);
99 	return 0;
100 }
101 
102 static int md5_sparc64_import(struct shash_desc *desc, const void *in)
103 {
104 	struct sparc_md5_state *sctx = shash_desc_ctx(desc);
105 	union {
106 		const u8 *u8;
107 		const u32 *u32;
108 		const u64 *u64;
109 	} p = { .u8 = in };
110 	int i;
111 
112 	for (i = 0; i < MD5_HASH_WORDS; i++)
113 		sctx->hash[i] = cpu_to_le32(get_unaligned(p.u32++));
114 	sctx->byte_count = get_unaligned(p.u64);
115 	return 0;
116 }
117 
118 static struct shash_alg alg = {
119 	.digestsize	=	MD5_DIGEST_SIZE,
120 	.init		=	md5_sparc64_init,
121 	.update		=	md5_sparc64_update,
122 	.finup		=	md5_sparc64_finup,
123 	.export		=	md5_sparc64_export,
124 	.import		=	md5_sparc64_import,
125 	.descsize	=	sizeof(struct sparc_md5_state),
126 	.statesize	=	sizeof(struct sparc_md5_state),
127 	.base		=	{
128 		.cra_name	=	"md5",
129 		.cra_driver_name=	"md5-sparc64",
130 		.cra_priority	=	SPARC_CR_OPCODE_PRIORITY,
131 		.cra_flags	=	CRYPTO_AHASH_ALG_BLOCK_ONLY,
132 		.cra_blocksize	=	MD5_HMAC_BLOCK_SIZE,
133 		.cra_module	=	THIS_MODULE,
134 	}
135 };
136 
137 static bool __init sparc64_has_md5_opcode(void)
138 {
139 	unsigned long cfr;
140 
141 	if (!(sparc64_elf_hwcap & HWCAP_SPARC_CRYPTO))
142 		return false;
143 
144 	__asm__ __volatile__("rd %%asr26, %0" : "=r" (cfr));
145 	if (!(cfr & CFR_MD5))
146 		return false;
147 
148 	return true;
149 }
150 
151 static int __init md5_sparc64_mod_init(void)
152 {
153 	if (sparc64_has_md5_opcode()) {
154 		pr_info("Using sparc64 md5 opcode optimized MD5 implementation\n");
155 		return crypto_register_shash(&alg);
156 	}
157 	pr_info("sparc64 md5 opcode not available.\n");
158 	return -ENODEV;
159 }
160 
161 static void __exit md5_sparc64_mod_fini(void)
162 {
163 	crypto_unregister_shash(&alg);
164 }
165 
166 module_init(md5_sparc64_mod_init);
167 module_exit(md5_sparc64_mod_fini);
168 
169 MODULE_LICENSE("GPL");
170 MODULE_DESCRIPTION("MD5 Message Digest Algorithm, sparc64 md5 opcode accelerated");
171 
172 MODULE_ALIAS_CRYPTO("md5");
173 
174 #include "crop_devid.c"
175