xref: /optee_os/core/drivers/stm32_rng.c (revision 097f329a1c27a4eabc87f4b6d457627832d6670b)
1f3c22059SEtienne Carriere // SPDX-License-Identifier: BSD-3-Clause
2f3c22059SEtienne Carriere /*
3f3c22059SEtienne Carriere  * Copyright (c) 2018-2019, STMicroelectronics
4f3c22059SEtienne Carriere  */
5f3c22059SEtienne Carriere 
6f3c22059SEtienne Carriere #include <assert.h>
7d7a1a7d2SEtienne Carriere #include <drivers/clk.h>
8d7a1a7d2SEtienne Carriere #include <drivers/clk_dt.h>
9f3c22059SEtienne Carriere #include <drivers/stm32_rng.h>
10f3c22059SEtienne Carriere #include <io.h>
11f3c22059SEtienne Carriere #include <kernel/delay.h>
12a2fc83d1SJerome Forissier #include <kernel/dt.h>
1365401337SJens Wiklander #include <kernel/boot.h>
14f3c22059SEtienne Carriere #include <kernel/panic.h>
1565b5ada4SMarouene Boubakri #include <kernel/thread.h>
16a2fc83d1SJerome Forissier #include <libfdt.h>
17f3c22059SEtienne Carriere #include <mm/core_memprot.h>
18*097f329aSEtienne Carriere #include <rng_support.h>
19f3c22059SEtienne Carriere #include <stdbool.h>
20f3c22059SEtienne Carriere #include <stm32_util.h>
21f3c22059SEtienne Carriere #include <string.h>
22f3c22059SEtienne Carriere 
23f3c22059SEtienne Carriere #define DT_RNG_COMPAT		"st,stm32-rng"
24f3c22059SEtienne Carriere #define RNG_CR			0x00U
25f3c22059SEtienne Carriere #define RNG_SR			0x04U
26f3c22059SEtienne Carriere #define RNG_DR			0x08U
27f3c22059SEtienne Carriere 
28f3c22059SEtienne Carriere #define RNG_CR_RNGEN		BIT(2)
29f3c22059SEtienne Carriere #define RNG_CR_IE		BIT(3)
30f3c22059SEtienne Carriere #define RNG_CR_CED		BIT(5)
31f3c22059SEtienne Carriere 
32f3c22059SEtienne Carriere #define RNG_SR_DRDY		BIT(0)
33f3c22059SEtienne Carriere #define RNG_SR_CECS		BIT(1)
34f3c22059SEtienne Carriere #define RNG_SR_SECS		BIT(2)
35f3c22059SEtienne Carriere #define RNG_SR_CEIS		BIT(5)
36f3c22059SEtienne Carriere #define RNG_SR_SEIS		BIT(6)
37f3c22059SEtienne Carriere 
38c99311c8SEtienne Carriere #define RNG_TIMEOUT_US		U(100000)
39f3c22059SEtienne Carriere 
40f3c22059SEtienne Carriere struct stm32_rng_instance {
41f3c22059SEtienne Carriere 	struct io_pa_va base;
42d7a1a7d2SEtienne Carriere 	struct clk *clock;
43f3c22059SEtienne Carriere 	unsigned int lock;
44f3c22059SEtienne Carriere 	unsigned int refcount;
45d8682c4cSEtienne Carriere 	bool release_post_boot;
46f3c22059SEtienne Carriere };
47f3c22059SEtienne Carriere 
48f3c22059SEtienne Carriere static struct stm32_rng_instance *stm32_rng;
49f3c22059SEtienne Carriere 
50f3c22059SEtienne Carriere /*
51f3c22059SEtienne Carriere  * Extracts from the STM32 RNG specification:
52f3c22059SEtienne Carriere  *
53f3c22059SEtienne Carriere  * When a noise source (or seed) error occurs, the RNG stops generating
54f3c22059SEtienne Carriere  * random numbers and sets to “1” both SEIS and SECS bits to indicate
55f3c22059SEtienne Carriere  * that a seed error occurred. (...)
56f3c22059SEtienne Carriere 
57f3c22059SEtienne Carriere  * The following sequence shall be used to fully recover from a seed
58f3c22059SEtienne Carriere  * error after the RNG initialization:
59f3c22059SEtienne Carriere  * 1. Clear the SEIS bit by writing it to “0”.
60f3c22059SEtienne Carriere  * 2. Read out 12 words from the RNG_DR register, and discard each of
61f3c22059SEtienne Carriere  * them in order to clean the pipeline.
62f3c22059SEtienne Carriere  * 3. Confirm that SEIS is still cleared. Random number generation is
63f3c22059SEtienne Carriere  * back to normal.
64f3c22059SEtienne Carriere  */
65f3c22059SEtienne Carriere static void conceal_seed_error(vaddr_t rng_base)
66f3c22059SEtienne Carriere {
67f3c22059SEtienne Carriere 	if (io_read32(rng_base + RNG_SR) & (RNG_SR_SECS | RNG_SR_SEIS)) {
68f3c22059SEtienne Carriere 		size_t i = 0;
69f3c22059SEtienne Carriere 
70f3c22059SEtienne Carriere 		io_mask32(rng_base + RNG_SR, 0, RNG_SR_SEIS);
71f3c22059SEtienne Carriere 
72f3c22059SEtienne Carriere 		for (i = 12; i != 0; i--)
73f3c22059SEtienne Carriere 			(void)io_read32(rng_base + RNG_DR);
74f3c22059SEtienne Carriere 
75f3c22059SEtienne Carriere 		if (io_read32(rng_base + RNG_SR) & RNG_SR_SEIS)
76f3c22059SEtienne Carriere 			panic("RNG noise");
77f3c22059SEtienne Carriere 	}
78f3c22059SEtienne Carriere }
79f3c22059SEtienne Carriere 
80f3c22059SEtienne Carriere #define RNG_FIFO_BYTE_DEPTH		16u
81f3c22059SEtienne Carriere 
82c99311c8SEtienne Carriere static TEE_Result read_available(vaddr_t rng_base, uint8_t *out, size_t *size)
83f3c22059SEtienne Carriere {
84c99311c8SEtienne Carriere 	uint8_t *buf = NULL;
85c99311c8SEtienne Carriere 	size_t req_size = 0;
86c99311c8SEtienne Carriere 	size_t len = 0;
87f3c22059SEtienne Carriere 
88f3c22059SEtienne Carriere 	conceal_seed_error(rng_base);
89f3c22059SEtienne Carriere 
9023123473SEtienne Carriere 	if (!(io_read32(rng_base + RNG_SR) & RNG_SR_DRDY)) {
9123123473SEtienne Carriere 		FMSG("RNG not ready");
92c99311c8SEtienne Carriere 		return TEE_ERROR_NO_DATA;
9323123473SEtienne Carriere 	}
94f3c22059SEtienne Carriere 
9523123473SEtienne Carriere 	if (io_read32(rng_base + RNG_SR) & RNG_SR_SEIS) {
9623123473SEtienne Carriere 		FMSG("RNG noise error");
97c99311c8SEtienne Carriere 		return TEE_ERROR_NO_DATA;
9823123473SEtienne Carriere 	}
99c99311c8SEtienne Carriere 
100c99311c8SEtienne Carriere 	buf = out;
101c99311c8SEtienne Carriere 	req_size = MIN(RNG_FIFO_BYTE_DEPTH, *size);
102c99311c8SEtienne Carriere 	len = req_size;
103f3c22059SEtienne Carriere 
104f3c22059SEtienne Carriere 	/* RNG is ready: read up to 4 32bit words */
105f3c22059SEtienne Carriere 	while (len) {
106f3c22059SEtienne Carriere 		uint32_t data32 = io_read32(rng_base + RNG_DR);
107f3c22059SEtienne Carriere 		size_t sz = MIN(len, sizeof(uint32_t));
108f3c22059SEtienne Carriere 
109f3c22059SEtienne Carriere 		memcpy(buf, &data32, sz);
110f3c22059SEtienne Carriere 		buf += sz;
111f3c22059SEtienne Carriere 		len -= sz;
112f3c22059SEtienne Carriere 	}
113c99311c8SEtienne Carriere 
114f3c22059SEtienne Carriere 	*size = req_size;
115f3c22059SEtienne Carriere 
116c99311c8SEtienne Carriere 	return TEE_SUCCESS;
117f3c22059SEtienne Carriere }
118f3c22059SEtienne Carriere 
119f3c22059SEtienne Carriere static void gate_rng(bool enable, struct stm32_rng_instance *dev)
120f3c22059SEtienne Carriere {
121c2e4eb43SAnton Rybakov 	vaddr_t rng_cr = io_pa_or_va(&dev->base, 1) + RNG_CR;
122f3c22059SEtienne Carriere 	uint32_t exceptions = may_spin_lock(&dev->lock);
123f3c22059SEtienne Carriere 
124f3c22059SEtienne Carriere 	if (enable) {
125f3c22059SEtienne Carriere 		/* incr_refcnt return non zero if resource shall be enabled */
126f3c22059SEtienne Carriere 		if (incr_refcnt(&dev->refcount)) {
12723123473SEtienne Carriere 			FMSG("enable RNG");
128d7a1a7d2SEtienne Carriere 			clk_enable(dev->clock);
129f3c22059SEtienne Carriere 			io_write32(rng_cr, 0);
130f3c22059SEtienne Carriere 			io_write32(rng_cr, RNG_CR_RNGEN | RNG_CR_CED);
131f3c22059SEtienne Carriere 		}
132f3c22059SEtienne Carriere 	} else {
133f3c22059SEtienne Carriere 		/* decr_refcnt return non zero if resource shall be disabled */
134f3c22059SEtienne Carriere 		if (decr_refcnt(&dev->refcount)) {
13523123473SEtienne Carriere 			FMSG("disable RNG");
136f3c22059SEtienne Carriere 			io_write32(rng_cr, 0);
137d7a1a7d2SEtienne Carriere 			clk_disable(dev->clock);
138f3c22059SEtienne Carriere 		}
139f3c22059SEtienne Carriere 	}
140f3c22059SEtienne Carriere 
141f3c22059SEtienne Carriere 	may_spin_unlock(&dev->lock, exceptions);
142f3c22059SEtienne Carriere }
143f3c22059SEtienne Carriere 
144f3c22059SEtienne Carriere TEE_Result stm32_rng_read(uint8_t *out, size_t size)
145f3c22059SEtienne Carriere {
146c99311c8SEtienne Carriere 	TEE_Result rc = TEE_ERROR_GENERIC;
147c99311c8SEtienne Carriere 	bool burst_timeout = false;
148c99311c8SEtienne Carriere 	uint64_t timeout_ref = 0;
149f3c22059SEtienne Carriere 	uint32_t exceptions = 0;
150f3c22059SEtienne Carriere 	uint8_t *out_ptr = out;
151c99311c8SEtienne Carriere 	vaddr_t rng_base = 0;
152f3c22059SEtienne Carriere 	size_t out_size = 0;
153f3c22059SEtienne Carriere 
154f3c22059SEtienne Carriere 	if (!stm32_rng) {
155f3c22059SEtienne Carriere 		DMSG("No RNG");
156f3c22059SEtienne Carriere 		return TEE_ERROR_NOT_SUPPORTED;
157f3c22059SEtienne Carriere 	}
158f3c22059SEtienne Carriere 
159f3c22059SEtienne Carriere 	gate_rng(true, stm32_rng);
160c99311c8SEtienne Carriere 	rng_base = io_pa_or_va(&stm32_rng->base, 1);
161c99311c8SEtienne Carriere 
162c99311c8SEtienne Carriere 	/* Arm timeout */
163c99311c8SEtienne Carriere 	timeout_ref = timeout_init_us(RNG_TIMEOUT_US);
164c99311c8SEtienne Carriere 	burst_timeout = false;
165f3c22059SEtienne Carriere 
166f3c22059SEtienne Carriere 	while (out_size < size) {
167f3c22059SEtienne Carriere 		/* Read by chunks of the size the RNG FIFO depth */
168f3c22059SEtienne Carriere 		size_t sz = size - out_size;
169f3c22059SEtienne Carriere 
170f3c22059SEtienne Carriere 		exceptions = may_spin_lock(&stm32_rng->lock);
171f3c22059SEtienne Carriere 
172c99311c8SEtienne Carriere 		rc = read_available(rng_base, out_ptr, &sz);
173c99311c8SEtienne Carriere 
174c99311c8SEtienne Carriere 		/* Raise timeout only if we failed to get some samples */
175c99311c8SEtienne Carriere 		assert(!rc || rc == TEE_ERROR_NO_DATA);
176c99311c8SEtienne Carriere 		if (rc)
177c99311c8SEtienne Carriere 			burst_timeout = timeout_elapsed(timeout_ref);
178f3c22059SEtienne Carriere 
179f3c22059SEtienne Carriere 		may_spin_unlock(&stm32_rng->lock, exceptions);
180f3c22059SEtienne Carriere 
181c99311c8SEtienne Carriere 		if (burst_timeout) {
182c99311c8SEtienne Carriere 			rc = TEE_ERROR_GENERIC;
183c99311c8SEtienne Carriere 			goto out;
184f3c22059SEtienne Carriere 		}
185f3c22059SEtienne Carriere 
186c99311c8SEtienne Carriere 		if (!rc) {
187c99311c8SEtienne Carriere 			out_size += sz;
188c99311c8SEtienne Carriere 			out_ptr += sz;
189c99311c8SEtienne Carriere 			/* Re-arm timeout */
190c99311c8SEtienne Carriere 			timeout_ref = timeout_init_us(RNG_TIMEOUT_US);
191c99311c8SEtienne Carriere 			burst_timeout = false;
192c99311c8SEtienne Carriere 		}
193c99311c8SEtienne Carriere 	}
194c99311c8SEtienne Carriere 
195c99311c8SEtienne Carriere out:
196c99311c8SEtienne Carriere 	assert(!rc || rc == TEE_ERROR_GENERIC);
197f3c22059SEtienne Carriere 	gate_rng(false, stm32_rng);
198f3c22059SEtienne Carriere 
199f3c22059SEtienne Carriere 	return rc;
200f3c22059SEtienne Carriere }
201f3c22059SEtienne Carriere 
202*097f329aSEtienne Carriere #ifndef CFG_WITH_SOFTWARE_PRNG
203*097f329aSEtienne Carriere TEE_Result crypto_rng_read(void *out, size_t size)
204*097f329aSEtienne Carriere {
205*097f329aSEtienne Carriere 	return stm32_rng_read(out, size);
206*097f329aSEtienne Carriere }
207*097f329aSEtienne Carriere 
208*097f329aSEtienne Carriere uint8_t hw_get_random_byte(void)
209*097f329aSEtienne Carriere {
210*097f329aSEtienne Carriere 	uint8_t byte = 0;
211*097f329aSEtienne Carriere 
212*097f329aSEtienne Carriere 	if (stm32_rng_read(&byte, sizeof(byte)))
213*097f329aSEtienne Carriere 		panic();
214*097f329aSEtienne Carriere 
215*097f329aSEtienne Carriere 	return byte;
216*097f329aSEtienne Carriere }
217*097f329aSEtienne Carriere #endif
218*097f329aSEtienne Carriere 
219f3c22059SEtienne Carriere #ifdef CFG_EMBED_DTB
220f3c22059SEtienne Carriere static TEE_Result stm32_rng_init(void)
221f3c22059SEtienne Carriere {
222f3c22059SEtienne Carriere 	void *fdt = NULL;
223f3c22059SEtienne Carriere 	int node = -1;
224f3c22059SEtienne Carriere 	struct dt_node_info dt_info;
225d7a1a7d2SEtienne Carriere 	TEE_Result res = TEE_ERROR_GENERIC;
226f3c22059SEtienne Carriere 
227f3c22059SEtienne Carriere 	memset(&dt_info, 0, sizeof(dt_info));
228f3c22059SEtienne Carriere 
229f3c22059SEtienne Carriere 	fdt = get_embedded_dt();
230f3c22059SEtienne Carriere 	if (!fdt)
231f3c22059SEtienne Carriere 		panic();
232f3c22059SEtienne Carriere 
233f3c22059SEtienne Carriere 	while (true) {
234f3c22059SEtienne Carriere 		node = fdt_node_offset_by_compatible(fdt, node, DT_RNG_COMPAT);
235f3c22059SEtienne Carriere 		if (node < 0)
236f3c22059SEtienne Carriere 			break;
237f3c22059SEtienne Carriere 
238f3c22059SEtienne Carriere 		_fdt_fill_device_info(fdt, &dt_info, node);
239f3c22059SEtienne Carriere 
240f3c22059SEtienne Carriere 		if (!(dt_info.status & DT_STATUS_OK_SEC))
241f3c22059SEtienne Carriere 			continue;
242f3c22059SEtienne Carriere 
243f3c22059SEtienne Carriere 		if (stm32_rng)
244f3c22059SEtienne Carriere 			panic();
245f3c22059SEtienne Carriere 
246f3c22059SEtienne Carriere 		stm32_rng = calloc(1, sizeof(*stm32_rng));
247f3c22059SEtienne Carriere 		if (!stm32_rng)
248f3c22059SEtienne Carriere 			panic();
249f3c22059SEtienne Carriere 
250f3c22059SEtienne Carriere 		assert(dt_info.clock != DT_INFO_INVALID_CLOCK &&
251aabd492eSLionel Debieve 		       dt_info.reg != DT_INFO_INVALID_REG &&
252aabd492eSLionel Debieve 		       dt_info.reg_size != DT_INFO_INVALID_REG_SIZE);
253f3c22059SEtienne Carriere 
25428f25d8dSEtienne Carriere 		if (dt_info.status & DT_STATUS_OK_NSEC) {
25528f25d8dSEtienne Carriere 			stm32mp_register_non_secure_periph_iomem(dt_info.reg);
256d8682c4cSEtienne Carriere 			stm32_rng->release_post_boot = true;
25728f25d8dSEtienne Carriere 		} else {
25828f25d8dSEtienne Carriere 			stm32mp_register_secure_periph_iomem(dt_info.reg);
25928f25d8dSEtienne Carriere 		}
26028f25d8dSEtienne Carriere 
261f3c22059SEtienne Carriere 		stm32_rng->base.pa = dt_info.reg;
262d8682c4cSEtienne Carriere 		if (!io_pa_or_va_secure(&stm32_rng->base, dt_info.reg_size))
263d8682c4cSEtienne Carriere 			panic();
26468c4a16bSEtienne Carriere 
265d7a1a7d2SEtienne Carriere 		res = clk_dt_get_by_index(fdt, node, 0, &stm32_rng->clock);
266d7a1a7d2SEtienne Carriere 		if (res)
267d7a1a7d2SEtienne Carriere 			return res;
268d7a1a7d2SEtienne Carriere 
269d7a1a7d2SEtienne Carriere 		assert(stm32_rng->clock);
270f3c22059SEtienne Carriere 
271f3c22059SEtienne Carriere 		DMSG("RNG init");
272f3c22059SEtienne Carriere 	}
273f3c22059SEtienne Carriere 
274f3c22059SEtienne Carriere 	return TEE_SUCCESS;
275f3c22059SEtienne Carriere }
276f3c22059SEtienne Carriere 
277d8682c4cSEtienne Carriere early_init_late(stm32_rng_init);
278d8682c4cSEtienne Carriere 
279d8682c4cSEtienne Carriere static TEE_Result stm32_rng_release(void)
280d8682c4cSEtienne Carriere {
281d8682c4cSEtienne Carriere 	if (stm32_rng && stm32_rng->release_post_boot) {
282d8682c4cSEtienne Carriere 		DMSG("Release RNG driver");
283d8682c4cSEtienne Carriere 		assert(!stm32_rng->refcount);
284d8682c4cSEtienne Carriere 		free(stm32_rng);
285d8682c4cSEtienne Carriere 		stm32_rng = NULL;
286d8682c4cSEtienne Carriere 	}
287d8682c4cSEtienne Carriere 
288d8682c4cSEtienne Carriere 	return TEE_SUCCESS;
289d8682c4cSEtienne Carriere }
290d8682c4cSEtienne Carriere 
291d8682c4cSEtienne Carriere release_init_resource(stm32_rng_release);
292f3c22059SEtienne Carriere #endif /*CFG_EMBED_DTB*/
293