xref: /freebsd/sys/dev/random/armv8rng.c (revision 9eecef052155646fbc5f8f533b952b372572d06a)
1*9eecef05SAndrew Turner /*-
2*9eecef05SAndrew Turner  * Copyright (c) 2022 The FreeBSD Foundation
3*9eecef05SAndrew Turner  *
4*9eecef05SAndrew Turner  * This software was developed by Andrew Turner under sponsorship from
5*9eecef05SAndrew Turner  * the FreeBSD Foundation.
6*9eecef05SAndrew Turner  *
7*9eecef05SAndrew Turner  * Redistribution and use in source and binary forms, with or without
8*9eecef05SAndrew Turner  * modification, are permitted provided that the following conditions
9*9eecef05SAndrew Turner  * are met:
10*9eecef05SAndrew Turner  * 1. Redistributions of source code must retain the above copyright
11*9eecef05SAndrew Turner  *    notice, this list of conditions and the following disclaimer.
12*9eecef05SAndrew Turner  * 2. Redistributions in binary form must reproduce the above copyright
13*9eecef05SAndrew Turner  *    notice, this list of conditions and the following disclaimer in the
14*9eecef05SAndrew Turner  *    documentation and/or other materials provided with the distribution.
15*9eecef05SAndrew Turner  *
16*9eecef05SAndrew Turner  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17*9eecef05SAndrew Turner  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18*9eecef05SAndrew Turner  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19*9eecef05SAndrew Turner  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20*9eecef05SAndrew Turner  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21*9eecef05SAndrew Turner  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22*9eecef05SAndrew Turner  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23*9eecef05SAndrew Turner  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24*9eecef05SAndrew Turner  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25*9eecef05SAndrew Turner  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26*9eecef05SAndrew Turner  * SUCH DAMAGE.
27*9eecef05SAndrew Turner  */
28*9eecef05SAndrew Turner 
29*9eecef05SAndrew Turner #include <sys/cdefs.h>
30*9eecef05SAndrew Turner 
31*9eecef05SAndrew Turner #include <sys/param.h>
32*9eecef05SAndrew Turner #include <sys/systm.h>
33*9eecef05SAndrew Turner #include <sys/conf.h>
34*9eecef05SAndrew Turner #include <sys/kernel.h>
35*9eecef05SAndrew Turner #include <sys/lock.h>
36*9eecef05SAndrew Turner #include <sys/malloc.h>
37*9eecef05SAndrew Turner #include <sys/module.h>
38*9eecef05SAndrew Turner #include <sys/random.h>
39*9eecef05SAndrew Turner 
40*9eecef05SAndrew Turner #include <machine/armreg.h>
41*9eecef05SAndrew Turner 
42*9eecef05SAndrew Turner #include <dev/random/randomdev.h>
43*9eecef05SAndrew Turner 
44*9eecef05SAndrew Turner static u_int random_rndr_read(void *, u_int);
45*9eecef05SAndrew Turner 
46*9eecef05SAndrew Turner static bool has_rndr;
47*9eecef05SAndrew Turner static struct random_source random_armv8_rndr = {
48*9eecef05SAndrew Turner 	.rs_ident = "Armv8 rndr RNG",
49*9eecef05SAndrew Turner 	.rs_source = RANDOM_PURE_ARMV8,
50*9eecef05SAndrew Turner 	.rs_read = random_rndr_read,
51*9eecef05SAndrew Turner };
52*9eecef05SAndrew Turner 
53*9eecef05SAndrew Turner static inline int
54*9eecef05SAndrew Turner random_rndr_read_one(u_long *buf)
55*9eecef05SAndrew Turner {
56*9eecef05SAndrew Turner 	u_long val;
57*9eecef05SAndrew Turner 	int loop, ret;
58*9eecef05SAndrew Turner 
59*9eecef05SAndrew Turner 	loop = 10;
60*9eecef05SAndrew Turner 	do {
61*9eecef05SAndrew Turner 		__asm __volatile(
62*9eecef05SAndrew Turner 		    ".arch_extension rng   \n"
63*9eecef05SAndrew Turner 		    "mrs	%0, rndrrs \n" /* Read the random number */
64*9eecef05SAndrew Turner 		    "cset	%w1, ne    \n" /* 1 on success, 0 on failure */
65*9eecef05SAndrew Turner 		    ".arch_extension norng \n"
66*9eecef05SAndrew Turner 		    : "=&r" (val), "=&r"(ret) :: "cc");
67*9eecef05SAndrew Turner 	} while (ret != 0 && --loop > 0);
68*9eecef05SAndrew Turner 
69*9eecef05SAndrew Turner 	if (ret != 0)
70*9eecef05SAndrew Turner 		*buf = val;
71*9eecef05SAndrew Turner 
72*9eecef05SAndrew Turner 	return (ret);
73*9eecef05SAndrew Turner }
74*9eecef05SAndrew Turner 
75*9eecef05SAndrew Turner static u_int
76*9eecef05SAndrew Turner random_rndr_read(void *buf, u_int c)
77*9eecef05SAndrew Turner {
78*9eecef05SAndrew Turner 	u_long *b;
79*9eecef05SAndrew Turner 	u_int count;
80*9eecef05SAndrew Turner 
81*9eecef05SAndrew Turner 	b = buf;
82*9eecef05SAndrew Turner 	for (count = 0; count < c; count += sizeof(*b)) {
83*9eecef05SAndrew Turner 		if (!random_rndr_read_one(b))
84*9eecef05SAndrew Turner 			break;
85*9eecef05SAndrew Turner 
86*9eecef05SAndrew Turner 		b++;
87*9eecef05SAndrew Turner 	}
88*9eecef05SAndrew Turner 
89*9eecef05SAndrew Turner 	return (count);
90*9eecef05SAndrew Turner }
91*9eecef05SAndrew Turner 
92*9eecef05SAndrew Turner static int
93*9eecef05SAndrew Turner rndr_modevent(module_t mod, int type, void *unused)
94*9eecef05SAndrew Turner {
95*9eecef05SAndrew Turner 	uint64_t reg;
96*9eecef05SAndrew Turner 	int error = 0;
97*9eecef05SAndrew Turner 
98*9eecef05SAndrew Turner 	switch (type) {
99*9eecef05SAndrew Turner 	case MOD_LOAD:
100*9eecef05SAndrew Turner 		has_rndr = false;
101*9eecef05SAndrew Turner 		if (get_kernel_reg(ID_AA64ISAR0_EL1, &reg) &&
102*9eecef05SAndrew Turner 		    ID_AA64ISAR0_RNDR_VAL(reg) != ID_AA64ISAR0_RNDR_NONE) {
103*9eecef05SAndrew Turner 			has_rndr = true;
104*9eecef05SAndrew Turner 			random_source_register(&random_armv8_rndr);
105*9eecef05SAndrew Turner 			printf("random: fast provider: \"%s\"\n",
106*9eecef05SAndrew Turner 			    random_armv8_rndr.rs_ident);
107*9eecef05SAndrew Turner 		}
108*9eecef05SAndrew Turner 		break;
109*9eecef05SAndrew Turner 
110*9eecef05SAndrew Turner 	case MOD_UNLOAD:
111*9eecef05SAndrew Turner 		if (has_rndr)
112*9eecef05SAndrew Turner 			random_source_deregister(&random_armv8_rndr);
113*9eecef05SAndrew Turner 		break;
114*9eecef05SAndrew Turner 
115*9eecef05SAndrew Turner 	case MOD_SHUTDOWN:
116*9eecef05SAndrew Turner 		break;
117*9eecef05SAndrew Turner 
118*9eecef05SAndrew Turner 	default:
119*9eecef05SAndrew Turner 		error = EOPNOTSUPP;
120*9eecef05SAndrew Turner 		break;
121*9eecef05SAndrew Turner 
122*9eecef05SAndrew Turner 	}
123*9eecef05SAndrew Turner 
124*9eecef05SAndrew Turner 	return (error);
125*9eecef05SAndrew Turner }
126*9eecef05SAndrew Turner 
127*9eecef05SAndrew Turner static moduledata_t rndr_mod = {
128*9eecef05SAndrew Turner 	"rndr",
129*9eecef05SAndrew Turner 	rndr_modevent,
130*9eecef05SAndrew Turner 	0
131*9eecef05SAndrew Turner };
132*9eecef05SAndrew Turner 
133*9eecef05SAndrew Turner DECLARE_MODULE(rndr, rndr_mod, SI_SUB_RANDOM, SI_ORDER_FOURTH);
134*9eecef05SAndrew Turner MODULE_VERSION(rndr, 1);
135*9eecef05SAndrew Turner MODULE_DEPEND(rndr, random_harvestq, 1, 1, 1);
136