summaryrefslogtreecommitdiff
path: root/drivers/nxp/crypto/caam/src/rng.c
blob: 0b9d87de41e2ed7affd62e9d6bd5d005aa91c60f (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
/*
 * Copyright 2021 NXP
 *
 * SPDX-License-Identifier: BSD-3-Clause
 *
 */

#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

#include <arch_helpers.h>
#include "caam.h"
#include <common/debug.h>
#include "jobdesc.h"
#include "sec_hw_specific.h"


/* Callback function after Instantiation decsriptor is submitted to SEC */
static void rng_done(uint32_t *desc, uint32_t status, void *arg,
		     void *job_ring)
{
	INFO("RNG Desc SUCCESS with status %x\n", status);
}

/* Is the HW RNG instantiated?
 * Return code:
 * 0 - Not in the instantiated state
 * 1 - In the instantiated state
 * state_handle - 0 for SH0, 1 for SH1
 */
static int is_hw_rng_instantiated(uint32_t *state_handle)
{
	int ret_code = 0;
	uint32_t rdsta;

	rdsta = sec_in32(get_caam_addr() + RNG_REG_RDSTA_OFFSET);

	 /*Check if either of the two state handles has been instantiated */
	if (rdsta & RNG_STATE0_HANDLE_INSTANTIATED) {
		*state_handle = 0;
		ret_code = 1;
	} else if (rdsta & RNG_STATE0_HANDLE_INSTANTIATED) {
		*state_handle = 1;
		ret_code = 1;
	}

	return ret_code;
}

/* @brief Kick the TRNG block of the RNG HW Engine
 * @param [in] ent_delay       Entropy delay to be used
 *        By default, the TRNG runs for 200 clocks per sample;
 *        1200 clocks per sample generates better entropy.
 * @retval 0 on success
 * @retval -1 on error
 */
static void kick_trng(int ent_delay)
{
	uint32_t val;

	/* put RNG4 into program mode */
	val = sec_in32(get_caam_addr() + RNG_REG_RTMCTL_OFFSET);
	val = val | RTMCTL_PRGM;
	sec_out32(get_caam_addr() + RNG_REG_RTMCTL_OFFSET, val);

	/* rtsdctl bits 0-15 contain "Entropy Delay, which defines the
	 *  length (in system clocks) of each Entropy sample taken
	 */
	val = sec_in32(get_caam_addr() + RNG_REG_RTSDCTL_OFFSET);
	val = (val & ~RTSDCTL_ENT_DLY_MASK) |
	    (ent_delay << RTSDCTL_ENT_DLY_SHIFT);
	sec_out32(get_caam_addr() + RNG_REG_RTSDCTL_OFFSET, val);
	/* min. freq. count, equal to 1/4 of the entropy sample length */
	sec_out32(get_caam_addr() + RNG_REG_RTFRQMIN_OFFSET, ent_delay >> 2);
	/* disable maximum frequency count */
	sec_out32(get_caam_addr() + RNG_REG_RTFRQMAX_OFFSET, RTFRQMAX_DISABLE);

	/* select raw sampling in both entropy shifter
	 *  and statistical checker
	 */
	val = sec_in32(get_caam_addr() + RNG_REG_RTMCTL_OFFSET);
	val = val | RTMCTL_SAMP_MODE_RAW_ES_SC;
	sec_out32(get_caam_addr() + RNG_REG_RTMCTL_OFFSET, val);

	/* put RNG4 into run mode */
	val = sec_in32(get_caam_addr() + RNG_REG_RTMCTL_OFFSET);
	val = val & ~RTMCTL_PRGM;
	sec_out32(get_caam_addr() + RNG_REG_RTMCTL_OFFSET, val);
}

/* @brief Submit descriptor to instantiate the RNG
 * @retval 0 on success
 * @retval -1 on error
 */
static int instantiate_rng(void)
{
	int ret = 0;
	struct job_descriptor desc __aligned(CACHE_WRITEBACK_GRANULE);
	struct job_descriptor *jobdesc = &desc;

	jobdesc->arg = NULL;
	jobdesc->callback = rng_done;

	/* create the hw_rng descriptor */
	cnstr_rng_instantiate_jobdesc(jobdesc->desc);

	/* Finally, generate the requested random data bytes */
	ret = run_descriptor_jr(jobdesc);
	if (ret != 0) {
		ERROR("Error in running descriptor\n");
		ret = -1;
	}
	return ret;
}

/* Generate Random Data using HW RNG
 * Parameters:
 * uint8_t* add_input   - user specified optional input byte array
 * uint32_t add_input_len - number of bytes of additional input
 * uint8_t* out                   - user specified output byte array
 * uint32_t out_len       - number of bytes to store in output byte array
 * Return code:
 * 0 - SUCCESS
 * -1 - ERROR
 */
static int
hw_rng_generate(uint32_t *add_input, uint32_t add_input_len,
		uint8_t *out, uint32_t out_len, uint32_t state_handle)
{
	int ret = 0;
	struct job_descriptor desc __aligned(CACHE_WRITEBACK_GRANULE);
	struct job_descriptor *jobdesc = &desc;

	jobdesc->arg = NULL;
	jobdesc->callback = rng_done;

#if defined(SEC_MEM_NON_COHERENT) && defined(IMAGE_BL2)
	inv_dcache_range((uintptr_t)out, out_len);
	dmbsy();
#endif

	/* create the hw_rng descriptor */
	ret = cnstr_rng_jobdesc(jobdesc->desc, state_handle,
				add_input, add_input_len, out, out_len);
	if (ret != 0) {
		ERROR("Descriptor construction failed\n");
		ret = -1;
		goto out;
	}
	/* Finally, generate the requested random data bytes */
	ret = run_descriptor_jr(jobdesc);
	if (ret != 0) {
		ERROR("Error in running descriptor\n");
		ret = -1;
	}

out:
	return ret;
}

/* this function instantiates the rng
 *
 * Return code:
 *  0 - All is well
 * <0 - Error occurred somewhere
 */
int hw_rng_instantiate(void)
{
	int ret = 0;
	int ent_delay = RTSDCTL_ENT_DLY_MIN;
	uint32_t state_handle;

	ret = is_hw_rng_instantiated(&state_handle);
	if (ret != 0) {
		NOTICE("RNG already instantiated\n");
		return 0;
	}
	do {
		kick_trng(ent_delay);
		ent_delay += 400;
		/*if instantiate_rng(...) fails, the loop will rerun
		 *and the kick_trng(...) function will modify the
		 *upper and lower limits of the entropy sampling
		 *interval, leading to a sucessful initialization of
		 */
		ret = instantiate_rng();
	} while ((ret == -1) && (ent_delay < RTSDCTL_ENT_DLY_MAX));
	if (ret != 0) {
		ERROR("RNG: Failed to instantiate RNG\n");
		return ret;
	}

	NOTICE("RNG: INSTANTIATED\n");

	/* Enable RDB bit so that RNG works faster */
	// sec_setbits32(&sec->scfgr, SEC_SCFGR_RDBENABLE);

	return ret;
}

/* Generate random bytes, and stuff them into the bytes buffer
 *
 * If the HW RNG has not already been instantiated,
 *  it will be instantiated before data is generated.
 *
 * Parameters:
 * uint8_t* bytes  - byte buffer large enough to hold the requested random date
 * int byte_len - number of random bytes to generate
 *
 * Return code:
 *  0 - All is well
 *  ~0 - Error occurred somewhere
 */
int get_rand_bytes_hw(uint8_t *bytes, int byte_len)
{
	int ret_code = 0;
	uint32_t state_handle;

	/* If this is the first time this routine is called,
	 *  then the hash_drbg will not already be instantiated.
	 * Therefore, before generating data, instantiate the hash_drbg
	 */
	ret_code = is_hw_rng_instantiated(&state_handle);
	if (ret_code == 0) {
		INFO("Instantiating the HW RNG\n");

		/* Instantiate the hw RNG */
		ret_code = hw_rng_instantiate();
		if (ret_code != 0) {
			ERROR("HW RNG Instantiate failed\n");
			return ret_code;
		}
	}
	/* If  HW RNG is still not instantiated, something must have gone wrong,
	 * it must be in the error state, we will not generate any random data
	 */
	if (is_hw_rng_instantiated(&state_handle) == 0) {
		ERROR("HW RNG is in an Error state, and cannot be used\n");
		return -1;
	}
	/* Generate a random 256-bit value, as 32 bytes */
	ret_code = hw_rng_generate(0, 0, bytes, byte_len, state_handle);
	if (ret_code != 0) {
		ERROR("HW RNG Generate failed\n");
		return ret_code;
	}

	return ret_code;
}