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
md5_sparc64_init(struct shash_desc * desc)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
md5_sparc64_update(struct shash_desc * desc,const u8 * data,unsigned int len)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. */
md5_sparc64_finup(struct shash_desc * desc,const u8 * src,unsigned int offset,u8 * out)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
md5_sparc64_export(struct shash_desc * desc,void * out)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
md5_sparc64_import(struct shash_desc * desc,const void * in)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
sparc64_has_md5_opcode(void)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
md5_sparc64_mod_init(void)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
md5_sparc64_mod_fini(void)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