summaryrefslogtreecommitdiff
path: root/drivers/crypto/ace_sha.c
blob: 34611056ecf1850f08a28a674f8ab2c73c7d3f53 (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
// SPDX-License-Identifier: GPL-2.0+
/*
 * Advanced Crypto Engine - SHA Firmware
 * Copyright (c) 2012  Samsung Electronics
 */
#include <common.h>
#include "ace_sha.h"

#ifdef CONFIG_SHA_HW_ACCEL
#include <u-boot/sha256.h>
#include <u-boot/sha1.h>
#include <linux/errno.h>

/* SHA1 value for the message of zero length */
static const unsigned char sha1_digest_emptymsg[SHA1_SUM_LEN] = {
	0xDA, 0x39, 0xA3, 0xEE, 0x5E, 0x6B, 0x4B, 0x0D,
	0x32, 0x55, 0xBF, 0xFF, 0x95, 0x60, 0x18, 0x90,
	0xAF, 0xD8, 0x07, 0x09};

/* SHA256 value for the message of zero length */
static const unsigned char sha256_digest_emptymsg[SHA256_SUM_LEN] = {
	0xE3, 0xB0, 0xC4, 0x42, 0x98, 0xFC, 0x1C, 0x14,
	0x9A, 0xFB, 0xF4, 0xC8, 0x99, 0x6F, 0xB9, 0x24,
	0x27, 0xAE, 0x41, 0xE4, 0x64, 0x9B, 0x93, 0x4C,
	0xA4, 0x95, 0x99, 0x1B, 0x78, 0x52, 0xB8, 0x55};

int ace_sha_hash_digest(const unsigned char *pbuf, unsigned int buf_len,
			unsigned char *pout, unsigned int hash_type)
{
	unsigned int i, reg, len;
	unsigned int *pdigest;
	struct exynos_ace_sfr *ace_sha_reg =
		(struct exynos_ace_sfr *)samsung_get_base_ace_sfr();

	if (buf_len == 0) {
		/* ACE H/W cannot compute hash value for empty string */
		if (hash_type == ACE_SHA_TYPE_SHA1)
			memcpy(pout, sha1_digest_emptymsg, SHA1_SUM_LEN);
		else
			memcpy(pout, sha256_digest_emptymsg, SHA256_SUM_LEN);
		return 0;
	}

	/* Flush HRDMA */
	writel(ACE_FC_HRDMACFLUSH_ON, &ace_sha_reg->fc_hrdmac);
	writel(ACE_FC_HRDMACFLUSH_OFF, &ace_sha_reg->fc_hrdmac);

	/* Set byte swap of data in */
	writel(ACE_HASH_SWAPDI_ON | ACE_HASH_SWAPDO_ON | ACE_HASH_SWAPIV_ON,
	       &ace_sha_reg->hash_byteswap);

	/* Select Hash input mux as external source */
	reg = readl(&ace_sha_reg->fc_fifoctrl);
	reg = (reg & ~ACE_FC_SELHASH_MASK) | ACE_FC_SELHASH_EXOUT;
	writel(reg, &ace_sha_reg->fc_fifoctrl);

	/* Set Hash as SHA1 or SHA256 and start Hash engine */
	reg = (hash_type == ACE_SHA_TYPE_SHA1) ?
		ACE_HASH_ENGSEL_SHA1HASH : ACE_HASH_ENGSEL_SHA256HASH;
	reg |= ACE_HASH_STARTBIT_ON;
	writel(reg, &ace_sha_reg->hash_control);

	/* Enable FIFO mode */
	writel(ACE_HASH_FIFO_ON, &ace_sha_reg->hash_fifo_mode);

	/* Set message length */
	writel(buf_len, &ace_sha_reg->hash_msgsize_low);
	writel(0, &ace_sha_reg->hash_msgsize_high);

	/* Set HRDMA */
	writel((unsigned int)pbuf, &ace_sha_reg->fc_hrdmas);
	writel(buf_len, &ace_sha_reg->fc_hrdmal);

	while ((readl(&ace_sha_reg->hash_status) & ACE_HASH_MSGDONE_MASK) ==
		ACE_HASH_MSGDONE_OFF) {
		/*
		 * PRNG error bit goes HIGH if a PRNG request occurs without
		 * a complete seed setup. We are using this bit to check h/w
		 * fault because proper setup is not expected in that case.
		 */
		if ((readl(&ace_sha_reg->hash_status)
			& ACE_HASH_PRNGERROR_MASK) == ACE_HASH_PRNGERROR_ON)
			return -EBUSY;
	}

	/* Clear MSG_DONE bit */
	writel(ACE_HASH_MSGDONE_ON, &ace_sha_reg->hash_status);

	/* Read hash result */
	pdigest = (unsigned int *)pout;
	len = (hash_type == ACE_SHA_TYPE_SHA1) ? SHA1_SUM_LEN : SHA256_SUM_LEN;

	for (i = 0; i < len / 4; i++)
		pdigest[i] = readl(&ace_sha_reg->hash_result[i]);

	/* Clear HRDMA pending bit */
	writel(ACE_FC_HRDMA, &ace_sha_reg->fc_intpend);

	return 0;
}

void hw_sha256(const unsigned char *pbuf, unsigned int buf_len,
			unsigned char *pout, unsigned int chunk_size)
{
	if (ace_sha_hash_digest(pbuf, buf_len, pout, ACE_SHA_TYPE_SHA256))
		debug("ACE was not setup properly or it is faulty\n");
}

void hw_sha1(const unsigned char *pbuf, unsigned int buf_len,
			unsigned char *pout, unsigned int chunk_size)
{
	if (ace_sha_hash_digest(pbuf, buf_len, pout, ACE_SHA_TYPE_SHA1))
		debug("ACE was not setup properly or it is faulty\n");
}
#endif /* CONFIG_SHA_HW_ACCEL */

#ifdef CONFIG_LIB_HW_RAND
static unsigned int seed_done;

void srand(unsigned int seed)
{
	struct exynos_ace_sfr *reg =
		(struct exynos_ace_sfr *)samsung_get_base_ace_sfr();
	int i, status;

	/* Seed data */
	for (i = 0; i < ACE_HASH_PRNG_REG_NUM; i++)
		writel(seed << i, &reg->hash_seed[i]);

	/* Wait for seed setup done */
	while (1) {
		status = readl(&reg->hash_status);
		if ((status & ACE_HASH_SEEDSETTING_MASK) ||
		    (status & ACE_HASH_PRNGERROR_MASK))
			break;
	}

	seed_done = 1;
}

unsigned int rand(void)
{
	struct exynos_ace_sfr *reg =
		(struct exynos_ace_sfr *)samsung_get_base_ace_sfr();
	int i, status;
	unsigned int seed = (unsigned int)&status;
	unsigned int ret = 0;

	if (!seed_done)
		srand(seed);

	/* Start PRNG */
	writel(ACE_HASH_ENGSEL_PRNG | ACE_HASH_STARTBIT_ON, &reg->hash_control);

	/* Wait for PRNG done */
	while (1) {
		status = readl(&reg->hash_status);
		if (status & ACE_HASH_PRNGDONE_MASK)
			break;
		if (status & ACE_HASH_PRNGERROR_MASK) {
			seed_done = 0;
			return 0;
		}
	}

	/* Clear Done IRQ */
	writel(ACE_HASH_PRNGDONE_MASK, &reg->hash_status);

	/* Read a PRNG result */
	for (i = 0; i < ACE_HASH_PRNG_REG_NUM; i++)
		ret += readl(&reg->hash_prng[i]);

	seed_done = 0;
	return ret;
}

unsigned int rand_r(unsigned int *seedp)
{
	srand(*seedp);

	return rand();
}
#endif /* CONFIG_LIB_HW_RAND */