xref: /freebsd/sys/dev/random/armv8rng.c (revision 9560ac4b638edf688566f576adc65d3654f2240c)
19eecef05SAndrew Turner /*-
29eecef05SAndrew Turner  * Copyright (c) 2022 The FreeBSD Foundation
39eecef05SAndrew Turner  *
49eecef05SAndrew Turner  * This software was developed by Andrew Turner under sponsorship from
59eecef05SAndrew Turner  * the FreeBSD Foundation.
69eecef05SAndrew Turner  *
79eecef05SAndrew Turner  * Redistribution and use in source and binary forms, with or without
89eecef05SAndrew Turner  * modification, are permitted provided that the following conditions
99eecef05SAndrew Turner  * are met:
109eecef05SAndrew Turner  * 1. Redistributions of source code must retain the above copyright
119eecef05SAndrew Turner  *    notice, this list of conditions and the following disclaimer.
129eecef05SAndrew Turner  * 2. Redistributions in binary form must reproduce the above copyright
139eecef05SAndrew Turner  *    notice, this list of conditions and the following disclaimer in the
149eecef05SAndrew Turner  *    documentation and/or other materials provided with the distribution.
159eecef05SAndrew Turner  *
169eecef05SAndrew Turner  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
179eecef05SAndrew Turner  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
189eecef05SAndrew Turner  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
199eecef05SAndrew Turner  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
209eecef05SAndrew Turner  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
219eecef05SAndrew Turner  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
229eecef05SAndrew Turner  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
239eecef05SAndrew Turner  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
249eecef05SAndrew Turner  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
259eecef05SAndrew Turner  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
269eecef05SAndrew Turner  * SUCH DAMAGE.
279eecef05SAndrew Turner  */
289eecef05SAndrew Turner 
299eecef05SAndrew Turner #include <sys/cdefs.h>
309eecef05SAndrew Turner 
319eecef05SAndrew Turner #include <sys/param.h>
329eecef05SAndrew Turner #include <sys/systm.h>
339eecef05SAndrew Turner #include <sys/conf.h>
349eecef05SAndrew Turner #include <sys/kernel.h>
359eecef05SAndrew Turner #include <sys/lock.h>
369eecef05SAndrew Turner #include <sys/malloc.h>
379eecef05SAndrew Turner #include <sys/module.h>
389eecef05SAndrew Turner #include <sys/random.h>
399eecef05SAndrew Turner 
409eecef05SAndrew Turner #include <machine/armreg.h>
419eecef05SAndrew Turner 
429eecef05SAndrew Turner #include <dev/random/randomdev.h>
439eecef05SAndrew Turner 
449eecef05SAndrew Turner static u_int random_rndr_read(void *, u_int);
459eecef05SAndrew Turner 
469eecef05SAndrew Turner static bool has_rndr;
479eecef05SAndrew Turner static struct random_source random_armv8_rndr = {
489eecef05SAndrew Turner 	.rs_ident = "Armv8 rndr RNG",
499eecef05SAndrew Turner 	.rs_source = RANDOM_PURE_ARMV8,
509eecef05SAndrew Turner 	.rs_read = random_rndr_read,
519eecef05SAndrew Turner };
529eecef05SAndrew Turner 
539eecef05SAndrew Turner static inline int
random_rndr_read_one(u_long * buf)549eecef05SAndrew Turner random_rndr_read_one(u_long *buf)
559eecef05SAndrew Turner {
569eecef05SAndrew Turner 	u_long val;
579eecef05SAndrew Turner 	int loop, ret;
589eecef05SAndrew Turner 
599eecef05SAndrew Turner 	loop = 10;
609eecef05SAndrew Turner 	do {
619eecef05SAndrew Turner 		__asm __volatile(
62*9560ac4bSJessica Clarke 		    /* Read the random number */
63*9560ac4bSJessica Clarke 		    "mrs	%0, " __XSTRING(RNDRRS_REG) "\n"
64*9560ac4bSJessica Clarke 		    /* 1 on success, 0 on failure */
65*9560ac4bSJessica Clarke 		    "cset	%w1, ne\n"
669eecef05SAndrew Turner 		    : "=&r" (val), "=&r"(ret) :: "cc");
679eecef05SAndrew Turner 	} while (ret != 0 && --loop > 0);
689eecef05SAndrew Turner 
699eecef05SAndrew Turner 	if (ret != 0)
709eecef05SAndrew Turner 		*buf = val;
719eecef05SAndrew Turner 
729eecef05SAndrew Turner 	return (ret);
739eecef05SAndrew Turner }
749eecef05SAndrew Turner 
759eecef05SAndrew Turner static u_int
random_rndr_read(void * buf,u_int c)769eecef05SAndrew Turner random_rndr_read(void *buf, u_int c)
779eecef05SAndrew Turner {
789eecef05SAndrew Turner 	u_long *b;
799eecef05SAndrew Turner 	u_int count;
809eecef05SAndrew Turner 
819eecef05SAndrew Turner 	b = buf;
829eecef05SAndrew Turner 	for (count = 0; count < c; count += sizeof(*b)) {
839eecef05SAndrew Turner 		if (!random_rndr_read_one(b))
849eecef05SAndrew Turner 			break;
859eecef05SAndrew Turner 
869eecef05SAndrew Turner 		b++;
879eecef05SAndrew Turner 	}
889eecef05SAndrew Turner 
899eecef05SAndrew Turner 	return (count);
909eecef05SAndrew Turner }
919eecef05SAndrew Turner 
929eecef05SAndrew Turner static int
rndr_modevent(module_t mod,int type,void * unused)939eecef05SAndrew Turner rndr_modevent(module_t mod, int type, void *unused)
949eecef05SAndrew Turner {
959eecef05SAndrew Turner 	uint64_t reg;
969eecef05SAndrew Turner 	int error = 0;
979eecef05SAndrew Turner 
989eecef05SAndrew Turner 	switch (type) {
999eecef05SAndrew Turner 	case MOD_LOAD:
1009eecef05SAndrew Turner 		has_rndr = false;
1019eecef05SAndrew Turner 		if (get_kernel_reg(ID_AA64ISAR0_EL1, &reg) &&
1029eecef05SAndrew Turner 		    ID_AA64ISAR0_RNDR_VAL(reg) != ID_AA64ISAR0_RNDR_NONE) {
1039eecef05SAndrew Turner 			has_rndr = true;
1049eecef05SAndrew Turner 			random_source_register(&random_armv8_rndr);
1059eecef05SAndrew Turner 			printf("random: fast provider: \"%s\"\n",
1069eecef05SAndrew Turner 			    random_armv8_rndr.rs_ident);
1079eecef05SAndrew Turner 		}
1089eecef05SAndrew Turner 		break;
1099eecef05SAndrew Turner 
1109eecef05SAndrew Turner 	case MOD_UNLOAD:
1119eecef05SAndrew Turner 		if (has_rndr)
1129eecef05SAndrew Turner 			random_source_deregister(&random_armv8_rndr);
1139eecef05SAndrew Turner 		break;
1149eecef05SAndrew Turner 
1159eecef05SAndrew Turner 	case MOD_SHUTDOWN:
1169eecef05SAndrew Turner 		break;
1179eecef05SAndrew Turner 
1189eecef05SAndrew Turner 	default:
1199eecef05SAndrew Turner 		error = EOPNOTSUPP;
1209eecef05SAndrew Turner 		break;
1219eecef05SAndrew Turner 
1229eecef05SAndrew Turner 	}
1239eecef05SAndrew Turner 
1249eecef05SAndrew Turner 	return (error);
1259eecef05SAndrew Turner }
1269eecef05SAndrew Turner 
1279eecef05SAndrew Turner static moduledata_t rndr_mod = {
1289eecef05SAndrew Turner 	"rndr",
1299eecef05SAndrew Turner 	rndr_modevent,
1309eecef05SAndrew Turner 	0
1319eecef05SAndrew Turner };
1329eecef05SAndrew Turner 
1339eecef05SAndrew Turner DECLARE_MODULE(rndr, rndr_mod, SI_SUB_RANDOM, SI_ORDER_FOURTH);
1349eecef05SAndrew Turner MODULE_VERSION(rndr, 1);
1359eecef05SAndrew Turner MODULE_DEPEND(rndr, random_harvestq, 1, 1, 1);
136