summaryrefslogtreecommitdiff
path: root/drivers/crypto/nuvoton/npcm_sha.c
blob: 6da162069aa73bbf432a15369e92eeb371939de2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright (c) 2024 Nuvoton Technology Corp.
 */

#include <dm.h>
#include <hash.h>
#include <malloc.h>
#include <asm/io.h>
#include <linux/iopoll.h>

#define SHA512_BLOCK_LENGTH     (1024 / 8)

/* Register fields */
#define HASH_CTR_STS_SHA_EN             BIT(0)
#define HASH_CTR_STS_SHA_BUSY           BIT(1)
#define HASH_CTR_STS_SHA_RST            BIT(2)
#define HASH_CFG_SHA1_SHA2              BIT(0)
#define SHA512_CMD_SHA_512		BIT(3)
#define SHA512_CMD_INTERNAL_ROUND	BIT(2)
#define SHA512_CMD_WRITE		BIT(1)
#define SHA512_CMD_READ			BIT(0)

enum {
	type_sha1 = 0,
	type_sha256,
	type_sha384,
	type_sha512,
};

struct npcm_sha_regs {
	u8 data_in;
	u8 data_out;
	u8 ctr_sts;
	u8 hash_cfg;
	u8 sha512_cmd;
};

struct hash_info {
	u32 block_sz;
	u32 digest_len;
	u8 length_bytes;
	u8 type;
};

struct message_block {
	u64 length[2];
	u64 nonhash_sz;
	u8 buffer[SHA512_BLOCK_LENGTH * 2];
};

struct npcm_sha_priv {
	void *base;
	struct npcm_sha_regs *regs;
	struct hash_info *hash;
	struct message_block block;
	bool internal_round;
	bool support_sha512;
};

static struct npcm_sha_regs npcm_sha_reg_tbl[] = {
	{ .data_in = 0x0, .data_out = 0x20, .ctr_sts = 0x4, .hash_cfg = 0x8 },
	{ .data_in = 0x10, .data_out = 0x1c, .ctr_sts = 0x14, .sha512_cmd = 0x18 },
};

static struct hash_info npcm_hash_tbl[] = {
	{ .type = type_sha1, .block_sz = 64, .digest_len = 160, .length_bytes = 8 },
	{ .type = type_sha256, .block_sz = 64, .digest_len = 256, .length_bytes = 8 },
	{ .type = type_sha384, .block_sz = 128, .digest_len = 384, .length_bytes = 16 },
	{ .type = type_sha512, .block_sz = 128, .digest_len = 512, .length_bytes = 16 },
};

static struct npcm_sha_priv *sha_priv;

static int npcm_sha_init(u8 type)
{
	struct message_block *block = &sha_priv->block;

	if (type > type_sha512 ||
	    (!sha_priv->support_sha512 &&
	    (type == type_sha384 || type == type_sha512)))
		return -ENOTSUPP;

	sha_priv->regs = &npcm_sha_reg_tbl[type / 2];
	sha_priv->hash = &npcm_hash_tbl[type];
	block->length[0] = 0;
	block->length[1] = 0;
	block->nonhash_sz = 0;
	sha_priv->internal_round = false;

	return 0;
}

static void npcm_sha_reset(void)
{
	struct npcm_sha_regs *regs = sha_priv->regs;
	struct hash_info *hash = sha_priv->hash;
	u8 val;

	if (hash->type == type_sha1)
		writeb(HASH_CFG_SHA1_SHA2, sha_priv->base + regs->hash_cfg);
	else if (hash->type == type_sha256)
		writeb(0, sha_priv->base + regs->hash_cfg);
	else if (hash->type == type_sha384)
		writeb(0, sha_priv->base + regs->sha512_cmd);
	else if (hash->type == type_sha512)
		writeb(SHA512_CMD_SHA_512, sha_priv->base + regs->sha512_cmd);

	val = readb(sha_priv->base + regs->ctr_sts) & ~HASH_CTR_STS_SHA_EN;
	writeb(val | HASH_CTR_STS_SHA_RST, sha_priv->base + regs->ctr_sts);
}

static void npcm_sha_enable(bool on)
{
	struct npcm_sha_regs *regs = sha_priv->regs;
	u8 val;

	val = readb(sha_priv->base + regs->ctr_sts) & ~HASH_CTR_STS_SHA_EN;
	val |= on;
	writeb(val | on, sha_priv->base + regs->ctr_sts);
}

static int npcm_sha_flush_block(u8 *block)
{
	struct npcm_sha_regs *regs = sha_priv->regs;
	struct hash_info *hash = sha_priv->hash;
	u32 *blk_dw = (u32 *)block;
	u8 val;
	int i;

	if (readb_poll_timeout(sha_priv->base + regs->ctr_sts, val,
			       !(val & HASH_CTR_STS_SHA_BUSY), 100))
		return -ETIMEDOUT;

	if (hash->type == type_sha384 || hash->type == type_sha512) {
		val = SHA512_CMD_WRITE;
		if (hash->type == type_sha512)
			val |= SHA512_CMD_SHA_512;
		if (sha_priv->internal_round)
			val |= SHA512_CMD_INTERNAL_ROUND;
		writeb(val, sha_priv->base + regs->sha512_cmd);
	}
	for (i = 0; i < (hash->block_sz / sizeof(u32)); i++)
		writel(blk_dw[i], sha_priv->base + regs->data_in);

	sha_priv->internal_round = true;

	return 0;
}

static int npcm_sha_update_block(const u8 *in, u32 len)
{
	struct message_block *block = &sha_priv->block;
	struct hash_info *hash = sha_priv->hash;
	u8 *buffer = &block->buffer[0];
	u32 block_sz = hash->block_sz;
	u32 hash_sz;

	hash_sz = (block->nonhash_sz + len) > block_sz ?
		(block_sz - block->nonhash_sz) : len;
	memcpy(buffer + block->nonhash_sz, in, hash_sz);
	block->nonhash_sz += hash_sz;
	block->length[0] += hash_sz;
	if (block->length[0] < hash_sz)
		block->length[1]++;

	if (block->nonhash_sz == block_sz) {
		block->nonhash_sz = 0;
		if (npcm_sha_flush_block(buffer))
			return -EBUSY;
	}

	return hash_sz;
}

static int npcm_sha_update(const u8 *input, u32 len)
{
	int hash_sz;

	while (len) {
		hash_sz = npcm_sha_update_block(input, len);
		if (hash_sz < 0) {
			printf("SHA512 module busy\n");
			return -EBUSY;
		}
		len -= hash_sz;
		input += hash_sz;
	}

	return 0;
}

static int npcm_sha_finish(u8 *out)
{
	struct npcm_sha_regs *regs = sha_priv->regs;
	struct message_block *block = &sha_priv->block;
	struct hash_info *hash = sha_priv->hash;
	u8 *buffer = &block->buffer[0];
	u32 block_sz = hash->block_sz;
	u32 *out32 = (u32 *)out;
	u32 zero_len, val;
	u64 *length;
	u8 reg_data_out;
	int i;

	/* Padding, minimal padding size is last_byte+length_bytes */
	if ((block_sz - block->nonhash_sz) >= (hash->length_bytes + 1))
		zero_len = block_sz - block->nonhash_sz - (hash->length_bytes + 1);
	else
		zero_len = block_sz * 2 - block->nonhash_sz - (hash->length_bytes + 1);
	/* Last byte */
	buffer[block->nonhash_sz++] = 0x80;
	/* Zero bits padding */
	memset(&buffer[block->nonhash_sz], 0, zero_len);
	block->nonhash_sz += zero_len;
	/* Message length */
	length = (u64 *)&buffer[block->nonhash_sz];
	if (hash->length_bytes == 16) {
		*length++ = cpu_to_be64(block->length[1] << 3 | block->length[0] >> 61);
		block->nonhash_sz += 8;
	}
	*length = cpu_to_be64(block->length[0] << 3);
	block->nonhash_sz += 8;
	if (npcm_sha_flush_block(&block->buffer[0]))
		return -ETIMEDOUT;

	/* After padding, the last message may produce 2 blocks */
	if (block->nonhash_sz > block_sz) {
		if (npcm_sha_flush_block(&block->buffer[block_sz]))
			return -ETIMEDOUT;
	}
	/* Read digest */
	if (readb_poll_timeout(sha_priv->base + regs->ctr_sts, val,
			       !(val & HASH_CTR_STS_SHA_BUSY), 100))
		return -ETIMEDOUT;
	if (hash->type == type_sha384)
		writeb(SHA512_CMD_READ, sha_priv->base + regs->sha512_cmd);
	else if (hash->type == type_sha512)
		writeb(SHA512_CMD_SHA_512 | SHA512_CMD_READ,
		       sha_priv->base + regs->sha512_cmd);

	reg_data_out = regs->data_out;
	for (i = 0; i < (hash->digest_len / 32); i++) {
		*out32 = readl(sha_priv->base + reg_data_out);
		out32++;
		if (hash->type == type_sha1 || hash->type == type_sha256)
			reg_data_out += 4;
	}

	return 0;
}

int npcm_sha_calc(const u8 *input, u32 len, u8 *output, u8 type)
{
	if (npcm_sha_init(type))
		return -ENOTSUPP;
	npcm_sha_reset();
	npcm_sha_enable(true);
	npcm_sha_update(input, len);
	npcm_sha_finish(output);
	npcm_sha_enable(false);

	return 0;
}

void hw_sha512(const unsigned char *input, unsigned int len,
	       unsigned char *output, unsigned int chunk_sz)
{
	if (!sha_priv->support_sha512) {
		puts(" HW accelerator not support\n");
		return;
	}
	puts(" using BMC HW accelerator\n");
	npcm_sha_calc(input, len, output, type_sha512);
}

void hw_sha384(const unsigned char *input, unsigned int len,
	       unsigned char *output, unsigned int chunk_sz)
{
	if (!sha_priv->support_sha512) {
		puts(" HW accelerator not support\n");
		return;
	}
	puts(" using BMC HW accelerator\n");
	npcm_sha_calc(input, len, output, type_sha384);
}

void hw_sha256(const unsigned char *input, unsigned int len,
	       unsigned char *output, unsigned int chunk_sz)
{
	puts(" using BMC HW accelerator\n");
	npcm_sha_calc(input, len, output, type_sha256);
}

void hw_sha1(const unsigned char *input, unsigned int len,
	     unsigned char *output, unsigned int chunk_sz)
{
	puts(" using BMC HW accelerator\n");
	npcm_sha_calc(input, len, output, type_sha1);
}

int hw_sha_init(struct hash_algo *algo, void **ctxp)
{
	if (!strcmp("sha1", algo->name)) {
		npcm_sha_init(type_sha1);
	} else if (!strcmp("sha256", algo->name)) {
		npcm_sha_init(type_sha256);
	} else if (!strcmp("sha384", algo->name)) {
		if (!sha_priv->support_sha512)
			return -ENOTSUPP;
		npcm_sha_init(type_sha384);
	} else if (!strcmp("sha512", algo->name)) {
		if (!sha_priv->support_sha512)
			return -ENOTSUPP;
		npcm_sha_init(type_sha512);
	} else {
		return -ENOTSUPP;
	}

	printf("Using npcm SHA engine\n");
	npcm_sha_reset();
	npcm_sha_enable(true);

	return 0;
}

int hw_sha_update(struct hash_algo *algo, void *ctx, const void *buf,
		  unsigned int size, int is_last)
{
	return npcm_sha_update(buf, size);
}

int hw_sha_finish(struct hash_algo *algo, void *ctx, void *dest_buf,
		  int size)
{
	int ret;

	ret = npcm_sha_finish(dest_buf);
	npcm_sha_enable(false);

	return ret;
}

static int npcm_sha_bind(struct udevice *dev)
{
	sha_priv = calloc(1, sizeof(struct npcm_sha_priv));
	if (!sha_priv)
		return -ENOMEM;

	sha_priv->base = dev_read_addr_ptr(dev);
	if (!sha_priv->base) {
		printf("Cannot find sha reg address, binding failed\n");
		return -EINVAL;
	}

	if (IS_ENABLED(CONFIG_ARCH_NPCM8XX))
		sha_priv->support_sha512 = true;

	printf("SHA: NPCM SHA module bind OK\n");

	return 0;
}

static const struct udevice_id npcm_sha_ids[] = {
	{ .compatible = "nuvoton,npcm845-sha" },
	{ .compatible = "nuvoton,npcm750-sha" },
	{ }
};

U_BOOT_DRIVER(npcm_sha) = {
	.name = "npcm_sha",
	.id = UCLASS_MISC,
	.of_match = npcm_sha_ids,
	.priv_auto = sizeof(struct npcm_sha_priv),
	.bind = npcm_sha_bind,
};