git: a68e606c402e - stable/12 - random: x86 driver: Prefer RDSEED over RDRAND when available

From: David E. O'Brien <obrien_at_FreeBSD.org>
Date: Mon, 21 Feb 2022 08:07:42 UTC
The branch stable/12 has been updated by obrien:

URL: https://cgit.FreeBSD.org/src/commit/?id=a68e606c402ea5ce2c8257d0566e6f397594aa54

commit a68e606c402ea5ce2c8257d0566e6f397594aa54
Author:     Conrad Meyer <cem@FreeBSD.org>
AuthorDate: 2019-05-08 00:45:16 +0000
Commit:     David E. O'Brien <obrien@FreeBSD.org>
CommitDate: 2022-02-21 05:56:42 +0000

    random: x86 driver: Prefer RDSEED over RDRAND when available
    
    Per
    https://software.intel.com/en-us/blogs/2012/11/17/the-difference-between-rdrand-and-rdseed
    , RDRAND is a PRNG seeded from the same source as RDSEED.  The source is
    more suitable as PRNG seed material, so prefer it when the RDSEED intrinsic
    is available (indicated in CPU feature bits).
    
    (cherry picked from commit 2cb54a800c3a42cc256adcda0be98bd0af39096c)
---
 sys/dev/random/ivy.c | 48 +++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 39 insertions(+), 9 deletions(-)

diff --git a/sys/dev/random/ivy.c b/sys/dev/random/ivy.c
index 7431f56f0f69..5ff288b394ac 100644
--- a/sys/dev/random/ivy.c
+++ b/sys/dev/random/ivy.c
@@ -44,11 +44,13 @@ __FBSDID("$FreeBSD$");
 
 #include <machine/md_var.h>
 #include <machine/specialreg.h>
+#include <x86/ifunc.h>
 
 #include <dev/random/randomdev.h>
 
 #define	RETRY_COUNT	10
 
+static bool has_rdrand, has_rdseed;
 static u_int random_ivy_read(void *, u_int);
 
 static struct random_source random_ivy = {
@@ -57,10 +59,9 @@ static struct random_source random_ivy = {
 	.rs_read = random_ivy_read
 };
 
-static inline int
-ivy_rng_store(u_long *buf)
+static int
+x86_rdrand_store(u_long *buf)
 {
-#ifdef __GNUCLIKE_ASM
 	u_long rndval;
 	int retry;
 
@@ -75,9 +76,38 @@ ivy_rng_store(u_long *buf)
 	    : "+r" (retry), "=r" (rndval) : : "cc");
 	*buf = rndval;
 	return (retry);
-#else /* __GNUCLIKE_ASM */
-	return (0);
-#endif
+}
+
+static int
+x86_rdseed_store(u_long *buf)
+{
+	u_long rndval;
+	int retry;
+
+	retry = RETRY_COUNT;
+	__asm __volatile(
+	    "1:\n\t"
+	    "rdseed	%1\n\t"	/* read randomness into rndval */
+	    "jc		2f\n\t" /* CF is set on success, exit retry loop */
+	    "dec	%0\n\t" /* otherwise, retry-- */
+	    "jne	1b\n\t" /* and loop if retries are not exhausted */
+	    "2:"
+	    : "+r" (retry), "=r" (rndval) : : "cc");
+	*buf = rndval;
+	return (retry);
+}
+
+DEFINE_IFUNC(static, int, x86_rng_store, (u_long *buf), static)
+{
+	has_rdrand = (cpu_feature2 & CPUID2_RDRAND);
+	has_rdseed = (cpu_stdext_feature & CPUID_STDEXT_RDSEED);
+
+	if (has_rdseed)
+		return (x86_rdseed_store);
+	else if (has_rdrand)
+		return (x86_rdrand_store);
+	else
+		return (NULL);
 }
 
 /* It is required that buf length is a multiple of sizeof(u_long). */
@@ -90,7 +120,7 @@ random_ivy_read(void *buf, u_int c)
 	KASSERT(c % sizeof(*b) == 0, ("partial read %d", c));
 	b = buf;
 	for (count = c; count > 0; count -= sizeof(*b)) {
-		if (ivy_rng_store(&rndval) == 0)
+		if (x86_rng_store(&rndval) == 0)
 			break;
 		*b++ = rndval;
 	}
@@ -104,14 +134,14 @@ rdrand_modevent(module_t mod, int type, void *unused)
 
 	switch (type) {
 	case MOD_LOAD:
-		if (cpu_feature2 & CPUID2_RDRAND) {
+		if (has_rdrand || has_rdseed) {
 			random_source_register(&random_ivy);
 			printf("random: fast provider: \"%s\"\n", random_ivy.rs_ident);
 		}
 		break;
 
 	case MOD_UNLOAD:
-		if (cpu_feature2 & CPUID2_RDRAND)
+		if (has_rdrand || has_rdseed)
 			random_source_deregister(&random_ivy);
 		break;