Index: head/sys/dev/random/fenestrasX/fx_brng.c
===================================================================
--- head/sys/dev/random/fenestrasX/fx_brng.c	(revision 366620)
+++ head/sys/dev/random/fenestrasX/fx_brng.c	(revision 366621)
@@ -1,295 +1,296 @@
 /*-
  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
  *
  * Copyright (c) 2019 Conrad Meyer <cem@FreeBSD.org>
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
 
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
 #include <sys/param.h>
 #include <sys/fail.h>
 #include <sys/limits.h>
 #include <sys/lock.h>
 #include <sys/kernel.h>
 #include <sys/malloc.h>
 #include <sys/mutex.h>
 #include <sys/random.h>
 #include <sys/sdt.h>
 #include <sys/sysctl.h>
 #include <sys/systm.h>
 
 #include <machine/cpu.h>
 
 #include <dev/random/randomdev.h>
 #include <dev/random/random_harvestq.h>
 #include <dev/random/uint128.h>
 
 #include <dev/random/fenestrasX/fx_brng.h>
 #include <dev/random/fenestrasX/fx_priv.h>
+#include <dev/random/fenestrasX/fx_pub.h>
 #include <dev/random/fenestrasX/fx_rng.h>
 
 /*
  * Implementation of a buffered RNG, described in § 1.2-1.4 of the whitepaper.
  */
 
 /*
  * Initialize a buffered rng instance (either the static root instance, or a
  * per-cpu instance on the heap.  Both should be zero initialized before this
  * routine.
  */
 void
 fxrng_brng_init(struct fxrng_buffered_rng *rng)
 {
 	fxrng_rng_init(&rng->brng_rng, rng == &fxrng_root);
 
 	/* I.e., the buffer is empty. */
 	rng->brng_avail_idx = sizeof(rng->brng_buffer);
 
 	/*
 	 * It is fine and correct for brng_generation and brng_buffer to be
 	 * zero values.
 	 *
 	 * brng_prf and brng_generation must be initialized later.
 	 * Initialization is special for the root BRNG.  PCPU child instances
 	 * use fxrng_brng_produce_seed_data_internal() below.
 	 */
 }
 
 /*
  * Directly reseed the root BRNG from a first-time entropy source,
  * incorporating the existing BRNG state.  The main motivation for doing so "is
  * to ensure that as soon as an entropy source produces data, PRNG output
  * depends on the data from that source." (§ 3.1)
  *
  * The root BRNG is locked on entry and initial keying (brng_generation > 0)
  * has already been performed.  The root BRNG is unlocked on return.
  */
 void
 fxrng_brng_src_reseed(const struct harvest_event *event)
 {
 	struct fxrng_buffered_rng *rng;
 
 	rng = &fxrng_root;
 	FXRNG_BRNG_ASSERT(rng);
 	ASSERT_DEBUG(rng->brng_generation > 0, "root RNG not seeded");
 
 	fxrng_rng_src_reseed(&rng->brng_rng, event);
 	FXRNG_BRNG_ASSERT(rng);
 
 	/*
 	 * Bump root generation (which is costly) to force downstream BRNGs to
 	 * reseed and quickly incorporate the new entropy.  The intuition is
 	 * that this tradeoff is worth it because new sources show up extremely
 	 * rarely (limiting cost) and if they can contribute any entropy to a
 	 * weak state, we want to propagate it to all generators ASAP.
 	 */
 	rng->brng_generation++;
 	atomic_store_rel_64(&fxrng_root_generation, rng->brng_generation);
 	FXRNG_BRNG_UNLOCK(rng);
 }
 
 /*
  * Reseed a brng from some amount of pooled entropy (determined in fx_pool.c by
  * fxent_timer_reseed_npools).  For initial seeding, we pool entropy in a
  * single pool and use this API as well (fxrng_alg_seeded).
  */
 void
 fxrng_brng_reseed(const void *entr, size_t sz)
 {
 	struct fxrng_buffered_rng *rng;
 
 	rng = &fxrng_root;
 	FXRNG_BRNG_LOCK(rng);
 
 	fxrng_rng_reseed(&rng->brng_rng, (rng->brng_generation > 0), entr, sz);
 	FXRNG_BRNG_ASSERT(rng);
 
 	rng->brng_generation++;
 	atomic_store_rel_64(&fxrng_root_generation, rng->brng_generation);
 	FXRNG_BRNG_UNLOCK(rng);
 }
 
 /*
  * Grab some bytes off an initialized, current generation RNG.
  *
  * (Does not handle reseeding if our generation is stale.)
  *
  * Locking protocol is a bit odd.  The RNG is locked on entrance, but the lock
  * is dropped on exit.  This avoids holding a lock during expensive and slow
  * RNG generation.
  */
 static void
 fxrng_brng_getbytes_internal(struct fxrng_buffered_rng *rng, void *buf,
     size_t nbytes)
 {
 
 	FXRNG_BRNG_ASSERT(rng);
 
 	/* Make the zero request impossible for the rest of the logic. */
 	if (__predict_false(nbytes == 0)) {
 		FXRNG_BRNG_UNLOCK(rng);
 		goto out;
 	}
 
 	/* Fast/easy case: Use some bytes from the buffer. */
 	if (rng->brng_avail_idx + nbytes <= sizeof(rng->brng_buffer)) {
 		memcpy(buf, &rng->brng_buffer[rng->brng_avail_idx], nbytes);
 		explicit_bzero(&rng->brng_buffer[rng->brng_avail_idx], nbytes);
 		rng->brng_avail_idx += nbytes;
 		FXRNG_BRNG_UNLOCK(rng);
 		goto out;
 	}
 
 	/* Buffer case: */
 	if (nbytes < sizeof(rng->brng_buffer)) {
 		size_t rem;
 
 		/* Drain anything left in the buffer first. */
 		if (rng->brng_avail_idx < sizeof(rng->brng_buffer)) {
 			rem = sizeof(rng->brng_buffer) - rng->brng_avail_idx;
 			ASSERT_DEBUG(nbytes > rem, "invariant");
 
 			memcpy(buf, &rng->brng_buffer[rng->brng_avail_idx], rem);
 
 			buf = (uint8_t*)buf + rem;
 			nbytes -= rem;
 			ASSERT_DEBUG(nbytes != 0, "invariant");
 		}
 
 		/*
 		 * Partial fill from first buffer, have to rekey and generate a
 		 * new buffer to do the rest.
 		 */
 		fxrng_rng_genrandom_internal(&rng->brng_rng, rng->brng_buffer,
 		    sizeof(rng->brng_buffer), false);
 		FXRNG_BRNG_ASSERT(rng);
 		rng->brng_avail_idx = 0;
 
 		memcpy(buf, &rng->brng_buffer[rng->brng_avail_idx], nbytes);
 		explicit_bzero(&rng->brng_buffer[rng->brng_avail_idx], nbytes);
 		rng->brng_avail_idx += nbytes;
 		FXRNG_BRNG_UNLOCK(rng);
 		goto out;
 	}
 
 	/* Large request; skip the buffer. */
 	fxrng_rng_genrandom_internal(&rng->brng_rng, buf, nbytes, true);
 
 out:
 	FXRNG_BRNG_ASSERT_NOT(rng);
 	return;
 }
 
 /*
  * API to get a new key for a downstream RNG.  Returns the new key in 'buf', as
  * well as the generator's reseed_generation.
  *
  * 'rng' is locked on entry and unlocked on return.
  *
  * Only valid after confirming the caller's seed version or reseed_generation
  * matches roots (or we are root).  (For now, this is only used to reseed the
  * per-CPU generators from root.)
  */
 void
 fxrng_brng_produce_seed_data_internal(struct fxrng_buffered_rng *rng,
     void *buf, size_t keysz, uint64_t *seed_generation)
 {
 	FXRNG_BRNG_ASSERT(rng);
 	ASSERT_DEBUG(keysz == FX_CHACHA20_KEYSIZE, "keysz: %zu", keysz);
 
 	*seed_generation = rng->brng_generation;
 	fxrng_brng_getbytes_internal(rng, buf, keysz);
 	FXRNG_BRNG_ASSERT_NOT(rng);
 }
 
 /*
  * Read from an allocated and initialized buffered BRNG.  This a high-level
  * API, but doesn't handle PCPU BRNG allocation.
  *
  * BRNG is locked on entry.  It is unlocked on return.
  */
 void
 fxrng_brng_read(struct fxrng_buffered_rng *rng, void *buf, size_t nbytes)
 {
 	uint8_t newkey[FX_CHACHA20_KEYSIZE];
 
 	FXRNG_BRNG_ASSERT(rng);
 
 	/* Fast path: there hasn't been a global reseed since last read. */
 	if (rng->brng_generation == atomic_load_acq_64(&fxrng_root_generation))
 		goto done_reseeding;
 
 	ASSERT(rng != &fxrng_root, "root rng inconsistent seed version");
 
 	/*
 	 * Slow path: We need to rekey from the parent BRNG to incorporate new
 	 * entropy material.
 	 *
 	 * Lock order is always root -> percpu.
 	 */
 	FXRNG_BRNG_UNLOCK(rng);
 	FXRNG_BRNG_LOCK(&fxrng_root);
 	FXRNG_BRNG_LOCK(rng);
 
 	/*
 	 * If we lost the reseeding race when the lock was dropped, don't
 	 * duplicate work.
 	 */
 	if (__predict_false(rng->brng_generation ==
 	    atomic_load_acq_64(&fxrng_root_generation))) {
 		FXRNG_BRNG_UNLOCK(&fxrng_root);
 		goto done_reseeding;
 	}
 
 	fxrng_brng_produce_seed_data_internal(&fxrng_root, newkey,
 	    sizeof(newkey), &rng->brng_generation);
 
 	FXRNG_BRNG_ASSERT_NOT(&fxrng_root);
 	FXRNG_BRNG_ASSERT(rng);
 
 	fxrng_rng_setkey(&rng->brng_rng, newkey, sizeof(newkey));
 	explicit_bzero(newkey, sizeof(newkey));
 
 	/*
 	 * A reseed invalidates any previous buffered contents.  Here, we
 	 * forward the available index to the end of the buffer, i.e., empty.
 	 * Requests that would use the buffer (< 128 bytes) will refill its
 	 * contents on demand.
 	 *
 	 * It is explicitly ok that we do not zero out any remaining buffer
 	 * bytes; they will never be handed out to callers, and they reveal
 	 * nothing about the reseeded key (which came from the root BRNG).
 	 * (§ 1.3)
 	 */
 	rng->brng_avail_idx = sizeof(rng->brng_buffer);
 
 done_reseeding:
 	if (rng != &fxrng_root)
 		FXRNG_BRNG_ASSERT_NOT(&fxrng_root);
 	FXRNG_BRNG_ASSERT(rng);
 
 	fxrng_brng_getbytes_internal(rng, buf, nbytes);
 	FXRNG_BRNG_ASSERT_NOT(rng);
 }
Index: head/sys/dev/random/fenestrasX/fx_main.c
===================================================================
--- head/sys/dev/random/fenestrasX/fx_main.c	(revision 366620)
+++ head/sys/dev/random/fenestrasX/fx_main.c	(revision 366621)
@@ -1,274 +1,296 @@
 /*-
  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
  *
  * Copyright (c) 2019 Conrad Meyer <cem@FreeBSD.org>
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
 
 /*
  * This random algorithm is derived in part from the "Windows 10 random number
  * generation infrastructure" whitepaper published by Niels Ferguson and
  * Microsoft: https://aka.ms/win10rng
  *
  * It is also inspired by DJB's writing on buffered key-erasure PRNGs:
  * https://blog.cr.yp.to/20170723-random.html
  *
  * The Windows 10 RNG bears some similarity to Fortuna, which Ferguson was also
  * involved with.  Notable differences include:
  *  - Extended to multi-CPU design
  *  - Extended to pre-buffer some PRNG output
  *  - Pool-based reseeding is solely time-based (rather than on-access w/
  *    pacing)
  *  - Extended to specify efficient userspace design
  *  - Always-available design (requires the equivalent of loader(8) for all
  *    boots; probably relatively easy given the limited platforms Windows 10
  *    supports)
  *
  * Some aspects of the design document I found confusing and may have
  * misinterpreted:
  *  - Relationship between root PRNG seed version and periodic reseed pool use.
  *    I interpreted these as separate sequences.  The root PRNG seed version is
  *    bumped both by the periodic pool based reseed, and also special
  *    conditions such as the first time an entropy source provides entropy.  I
  *    don't think first-time entropy sources should cause us to skip an entropy
  *    pool reseed.
  *  - Initial seeding.  The paper is pretty terse on the subject.  My
  *    interpretation of the document is that the Windows RNG infrastructure
  *    relies on the loader(8)-provided material for initial seeding and either
  *    ignores or doesn't start entropy sources until after that time.  So when
  *    the paper says that first-time entropy source material "bypasses the
  *    pools," the root PRNG state already has been keyed for the first time and
  *    can generate 256 bits, mix it with the first-time entropy, and reseed
  *    immediately.
  *
  * Some notable design choices in this implementation divergent from that
  * specified in the document above:
  *  - Blake2b instead of SHA-2 512 for entropy pooling
  *  - Chacha20 instead of AES-CTR DRBG for PRF
  *  - Initial seeding.  We treat the 0->1 seed version (brng_generation) edge
  *    as the transition from blocked to unblocked.  That edge is also the first
  *    time the key of the root BRNG's PRF is set.  We perform initial seeding
  *    when the first request for entropy arrives.
  *    • As a result: Entropy callbacks prior to this edge do not have a keyed
  *      root PRNG, so bypassing the pools is kind of meaningless.  Instead,
  *      they feed into pool0.  (They also do not set the root PRNG key or bump
  *      the root PRNG seed version.)
  *    • Entropy callbacks after the edge behave like the specification.
  *    • All one-off sources are fed into pool0 and the result used to seed the
  *      root BRNG during the initial seed step.
  *    • All memory needed for initial seeding must be preallocated or static or
  *      fit on the stack; random reads can occur in nonsleepable contexts and
  *      we cannot allocate M_WAITOK.  (We also cannot fail to incorporate any
  *      present one-off source, to the extent it is in the control of
  *      software.)
  * - Timer interval reseeding.  We also start the timer-based reseeding at
  *   initial seed, but unlike the design, our initial seed is some time after
  *   load (usually within the order of micro- or milliseconds due to
  *   stack_guard on x86, but conceivably later if nothing reads from random for
  *   a while).
  *
  * Not yet implemented, not in scope, or todo:
- *  - arc4random(9) injection/replacement
  *  - Userspace portions -- shared page, like timehands vdso?
  */
 
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
 #include <sys/param.h>
 #include <sys/domainset.h>
 #include <sys/fail.h>
 #include <sys/limits.h>
 #include <sys/lock.h>
 #include <sys/kernel.h>
 #include <sys/malloc.h>
 #include <sys/mutex.h>
 #include <sys/random.h>
 #include <sys/sdt.h>
 #include <sys/smp.h>
 #include <sys/sysctl.h>
 #include <sys/systm.h>
 
 #include <machine/cpu.h>
 
 #include <vm/vm.h>
 #include <vm/vm_param.h>
 #include <vm/vm_page.h>
 #include <vm/vm_phys.h>
 #include <vm/vm_pagequeue.h>
 
 #include <dev/random/randomdev.h>
 #include <dev/random/random_harvestq.h>
 #include <dev/random/uint128.h>
 
 #include <dev/random/fenestrasX/fx_brng.h>
 #include <dev/random/fenestrasX/fx_hash.h>
 #include <dev/random/fenestrasX/fx_pool.h>
 #include <dev/random/fenestrasX/fx_priv.h>
+#include <dev/random/fenestrasX/fx_pub.h>
 #include <dev/random/fenestrasX/fx_rng.h>
 
 struct fxrng_buffered_rng fxrng_root;
 uint64_t __read_mostly fxrng_root_generation;
 DPCPU_DEFINE_STATIC(struct fxrng_buffered_rng *, fxrng_brng);
 
 /*
  * Top-level read API from randomdev.  Responsible for NOWAIT-allocating
  * per-cpu NUMA-local BRNGs, if needed and satisfiable; subroutines handle
  * reseeding if the local BRNG is stale and rekeying when necessary.  In
  * low-memory conditions when a local BRNG cannot be allocated, the request is
  * simply forwarded to the root BRNG.
  *
  * It is a precondition is that the root BRNG initial seeding has completed and
  * the root generation number >0.
  */
 static void
-fxrng_alg_read(uint8_t *output, size_t nbytes)
+_fxrng_alg_read(uint8_t *output, size_t nbytes, uint64_t *seed_version_out)
 {
 	struct fxrng_buffered_rng **pcpu_brng_p, *rng, *tmp;
 	struct pcpu *pcpu;
 
 	pcpu = get_pcpu();
 
 	/*
 	 * The following statement directly accesses an implementation detail
 	 * of DPCPU, but the macros cater only to pinned threads; we want to
 	 * operate on our initial CPU, without pinning, *even if* we migrate.
 	 */
 	pcpu_brng_p = _DPCPU_PTR(pcpu->pc_dynamic, fxrng_brng);
 
 	rng = (void *)atomic_load_acq_ptr((uintptr_t *)pcpu_brng_p);
 
 	/*
 	 * Usually the pcpu BRNG has already been allocated, but we do it
 	 * on-demand and need to check first.  BRNGs are never deallocated and
 	 * are valid as soon as the pointer is initialized.
 	 */
 	if (__predict_false(rng == NULL)) {
 		uint8_t newkey[FX_CHACHA20_KEYSIZE];
 		struct domainset *ds;
 		int domain;
 
 		domain = pcpu->pc_domain;
 
 		/*
 		 * Allocate pcpu BRNGs off-domain on weird NUMA machines like
 		 * AMD Threadripper 2990WX, which has 2 NUMA nodes without
 		 * local memory controllers.  The PREF policy is automatically
 		 * converted to something appropriate when domains are empty.
 		 * (FIXED is not.)
 		 *
 		 * Otherwise, allocate strictly CPU-local memory.  The
 		 * rationale is this: if there is a memory shortage such that
 		 * PREF policy would fallback to RR, we have no business
 		 * wasting memory on a faster BRNG.  So, use a FIXED domainset
 		 * policy.  If we cannot allocate, that's fine!  We fall back
 		 * to invoking the root BRNG.
 		 */
 		if (VM_DOMAIN_EMPTY(domain))
 			ds = DOMAINSET_PREF(domain);
 		else
 			ds = DOMAINSET_FIXED(domain);
 
 		rng = malloc_domainset(sizeof(*rng), M_ENTROPY, ds,
 		    M_NOWAIT | M_ZERO);
 		if (rng == NULL) {
 			/* Relatively easy case: fall back to root BRNG. */
 			rng = &fxrng_root;
 			goto have_valid_rng;
 		}
 
 		fxrng_brng_init(rng);
 
 		/*
 		 * The root BRNG is always up and available.  Requests are
 		 * always satisfiable.  This is a design invariant.
 		 */
 		ASSERT_DEBUG(atomic_load_acq_64(&fxrng_root_generation) != 0,
 		    "%s: attempting to seed child BRNG when root hasn't "
 		    "been initialized yet.", __func__);
 
 		FXRNG_BRNG_LOCK(&fxrng_root);
 #ifdef WITNESS
 		/* Establish lock order root->pcpu for WITNESS. */
 		FXRNG_BRNG_LOCK(rng);
 		FXRNG_BRNG_UNLOCK(rng);
 #endif
 		fxrng_brng_produce_seed_data_internal(&fxrng_root, newkey,
 		    sizeof(newkey), &rng->brng_generation);
 		FXRNG_BRNG_ASSERT_NOT(&fxrng_root);
 
 		fxrng_rng_setkey(&rng->brng_rng, newkey, sizeof(newkey));
 		explicit_bzero(newkey, sizeof(newkey));
 
 		/*
 		 * We have a valid RNG.  Try to install it, or grab the other
 		 * one if we lost the race.
 		 */
 		tmp = NULL;
 		while (tmp == NULL)
 			if (atomic_fcmpset_ptr((uintptr_t *)pcpu_brng_p,
 			    (uintptr_t *)&tmp, (uintptr_t)rng))
 				goto have_valid_rng;
 
 		/*
 		 * We lost the race.  There's nothing sensitive about
 		 * our BRNG's PRF state, because it will never be used
 		 * for anything and the key doesn't expose any
 		 * information about the parent (root) generator's
 		 * state -- it has already rekeyed.  The generation
 		 * number is public, and a zero counter isn't sensitive.
 		 */
 		free(rng, M_ENTROPY);
 		/*
 		 * Use the winner's PCPU BRNG.
 		 */
 		rng = tmp;
 	}
 
 have_valid_rng:
 	/* At this point we have a valid, initialized and seeded rng pointer. */
 	FXRNG_BRNG_LOCK(rng);
+	if (seed_version_out != NULL)
+		*seed_version_out = rng->brng_generation;
 	fxrng_brng_read(rng, output, nbytes);
 	FXRNG_BRNG_ASSERT_NOT(rng);
+}
+
+static void
+fxrng_alg_read(uint8_t *output, size_t nbytes)
+{
+	_fxrng_alg_read(output, nbytes, NULL);
+}
+
+/*
+ * External API for arc4random(9) to fetch new key material and associated seed
+ * version in chacha20_randomstir().
+ */
+void
+read_random_key(void *output, size_t nbytes, uint64_t *seed_version_out)
+{
+	/* Ensure _fxrng_alg_read invariant. */
+	if (__predict_false(atomic_load_acq_64(&fxrng_root_generation) == 0))
+		(void)fxrng_alg_seeded();
+
+	_fxrng_alg_read(output, nbytes, seed_version_out);
 }
 
 static void
 fxrng_init_alg(void *dummy __unused)
 {
 	DPCPU_ZERO(fxrng_brng);
 	fxrng_brng_init(&fxrng_root);
 	fxrng_pools_init();
 }
 SYSINIT(random_alg, SI_SUB_RANDOM, SI_ORDER_SECOND, fxrng_init_alg, NULL);
 
 /*
  * Public visibility struct referenced directly by other parts of randomdev.
  */
 const struct random_algorithm random_alg_context = {
 	.ra_ident = "fenestrasX",
 	.ra_pre_read = (void (*)(void))nullop,
 	.ra_read = fxrng_alg_read,
 	.ra_seeded = fxrng_alg_seeded,
 	.ra_event_processor = fxrng_event_processor,
 	.ra_poolcount = FXRNG_NPOOLS,
 };
Index: head/sys/dev/random/fenestrasX/fx_pool.c
===================================================================
--- head/sys/dev/random/fenestrasX/fx_pool.c	(revision 366620)
+++ head/sys/dev/random/fenestrasX/fx_pool.c	(revision 366621)
@@ -1,615 +1,616 @@
 /*-
  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
  *
  * Copyright (c) 2019 Conrad Meyer <cem@FreeBSD.org>
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
 
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
 #include <sys/param.h>
 #include <sys/domainset.h>
 #include <sys/fail.h>
 #include <sys/limits.h>
 #include <sys/lock.h>
 #include <sys/kernel.h>
 #include <sys/malloc.h>
 #include <sys/mutex.h>
 #include <sys/queue.h>
 #include <sys/random.h>
 #include <sys/sdt.h>
 #include <sys/sysctl.h>
 #include <sys/systm.h>
 #include <sys/taskqueue.h>
 
 #include <machine/atomic.h>
 #include <machine/smp.h>
 
 #include <dev/random/randomdev.h>
 #include <dev/random/random_harvestq.h>
 
 #include <dev/random/fenestrasX/fx_brng.h>
 #include <dev/random/fenestrasX/fx_hash.h>
 #include <dev/random/fenestrasX/fx_pool.h>
 #include <dev/random/fenestrasX/fx_priv.h>
+#include <dev/random/fenestrasX/fx_pub.h>
 
 /*
  * Timer-based reseed interval growth factor and limit in seconds. (§ 3.2)
  */
 #define	FXENT_RESSED_INTVL_GFACT	3
 #define	FXENT_RESEED_INTVL_MAX		3600
 
 /*
  * Pool reseed schedule.  Initially, only pool 0 is active.  Until the timer
  * interval reaches INTVL_MAX, only pool 0 is used.
  *
  * After reaching INTVL_MAX, pool k is either activated (if inactive) or used
  * (if active) every 3^k timer reseeds.  (§ 3.3)
  *
  * (Entropy harvesting only round robins across active pools.)
  */
 #define	FXENT_RESEED_BASE		3
 
 /*
  * Number of bytes from high quality sources to allocate to pool 0 before
  * normal round-robin allocation after each timer reseed. (§ 3.4)
  */
 #define	FXENT_HI_SRC_POOL0_BYTES	32
 
 /*
  * § 3.1
  *
  * Low sources provide unconditioned entropy, such as mouse movements; high
  * sources are assumed to provide high-quality random bytes.  Pull sources are
  * those which can be polled, i.e., anything randomdev calls a "random_source."
  *
  * In the whitepaper, low sources are pull.  For us, at least in the existing
  * design, low-quality sources push into some global ring buffer and then get
  * forwarded into the RNG by a thread that continually polls.  Presumably their
  * design batches low entopy signals in some way (SHA512?) and only requests
  * them dynamically on reseed.  I'm not sure what the benefit is vs feeding
  * into the pools directly.
  */
 enum fxrng_ent_access_cls {
 	FXRNG_PUSH,
 	FXRNG_PULL,
 };
 enum fxrng_ent_source_cls {
 	FXRNG_HI,
 	FXRNG_LO,
 	FXRNG_GARBAGE,
 };
 struct fxrng_ent_cls {
 	enum fxrng_ent_access_cls	entc_axx_cls;
 	enum fxrng_ent_source_cls	entc_src_cls;
 };
 
 static const struct fxrng_ent_cls fxrng_hi_pull = {
 	.entc_axx_cls = FXRNG_PULL,
 	.entc_src_cls = FXRNG_HI,
 };
 static const struct fxrng_ent_cls fxrng_hi_push = {
 	.entc_axx_cls = FXRNG_PUSH,
 	.entc_src_cls = FXRNG_HI,
 };
 static const struct fxrng_ent_cls fxrng_lo_push = {
 	.entc_axx_cls = FXRNG_PUSH,
 	.entc_src_cls = FXRNG_LO,
 };
 static const struct fxrng_ent_cls fxrng_garbage = {
 	.entc_axx_cls = FXRNG_PUSH,
 	.entc_src_cls = FXRNG_GARBAGE,
 };
 
 /*
  * This table is a mapping of randomdev's current source abstractions to the
  * designations above; at some point, if the design seems reasonable, it would
  * make more sense to pull this up into the abstraction layer instead.
  */
 static const struct fxrng_ent_char {
 	const struct fxrng_ent_cls	*entc_cls;
 } fxrng_ent_char[ENTROPYSOURCE] = {
 	[RANDOM_CACHED] = {
 		.entc_cls = &fxrng_hi_push,
 	},
 	[RANDOM_ATTACH] = {
 		.entc_cls = &fxrng_lo_push,
 	},
 	[RANDOM_KEYBOARD] = {
 		.entc_cls = &fxrng_lo_push,
 	},
 	[RANDOM_MOUSE] = {
 		.entc_cls = &fxrng_lo_push,
 	},
 	[RANDOM_NET_TUN] = {
 		.entc_cls = &fxrng_lo_push,
 	},
 	[RANDOM_NET_ETHER] = {
 		.entc_cls = &fxrng_lo_push,
 	},
 	[RANDOM_NET_NG] = {
 		.entc_cls = &fxrng_lo_push,
 	},
 	[RANDOM_INTERRUPT] = {
 		.entc_cls = &fxrng_lo_push,
 	},
 	[RANDOM_SWI] = {
 		.entc_cls = &fxrng_lo_push,
 	},
 	[RANDOM_FS_ATIME] = {
 		.entc_cls = &fxrng_lo_push,
 	},
 	[RANDOM_UMA] = {
 		.entc_cls = &fxrng_lo_push,
 	},
 	[RANDOM_PURE_OCTEON] = {
 		.entc_cls = &fxrng_hi_push,	/* Could be made pull. */
 	},
 	[RANDOM_PURE_SAFE] = {
 		.entc_cls = &fxrng_hi_push,
 	},
 	[RANDOM_PURE_GLXSB] = {
 		.entc_cls = &fxrng_hi_push,
 	},
 	[RANDOM_PURE_HIFN] = {
 		.entc_cls = &fxrng_hi_push,
 	},
 	[RANDOM_PURE_RDRAND] = {
 		.entc_cls = &fxrng_hi_pull,
 	},
 	[RANDOM_PURE_NEHEMIAH] = {
 		.entc_cls = &fxrng_hi_pull,
 	},
 	[RANDOM_PURE_RNDTEST] = {
 		.entc_cls = &fxrng_garbage,
 	},
 	[RANDOM_PURE_VIRTIO] = {
 		.entc_cls = &fxrng_hi_pull,
 	},
 	[RANDOM_PURE_BROADCOM] = {
 		.entc_cls = &fxrng_hi_push,
 	},
 	[RANDOM_PURE_CCP] = {
 		.entc_cls = &fxrng_hi_pull,
 	},
 	[RANDOM_PURE_DARN] = {
 		.entc_cls = &fxrng_hi_pull,
 	},
 	[RANDOM_PURE_TPM] = {
 		.entc_cls = &fxrng_hi_push,
 	},
 	[RANDOM_PURE_VMGENID] = {
 		.entc_cls = &fxrng_hi_push,
 	},
 };
 
 /* Useful for single-bit-per-source state. */
 BITSET_DEFINE(fxrng_bits, ENTROPYSOURCE);
 
 /* XXX Borrowed from not-yet-committed D22702. */
 #ifndef BIT_TEST_SET_ATOMIC_ACQ
 #define	BIT_TEST_SET_ATOMIC_ACQ(_s, n, p)	\
 	(atomic_testandset_acq_long(		\
 	    &(p)->__bits[__bitset_word((_s), (n))], (n)) != 0)
 #endif
 #define	FXENT_TEST_SET_ATOMIC_ACQ(n, p) \
 	BIT_TEST_SET_ATOMIC_ACQ(ENTROPYSOURCE, n, p)
 
 /* For special behavior on first-time entropy sources. (§ 3.1) */
 static struct fxrng_bits __read_mostly fxrng_seen;
 
 /* For special behavior for high-entropy sources after a reseed. (§ 3.4) */
 _Static_assert(FXENT_HI_SRC_POOL0_BYTES <= UINT8_MAX, "");
 static uint8_t __read_mostly fxrng_reseed_seen[ENTROPYSOURCE];
 
 /* Entropy pools.  Lock order is ENT -> RNG(root) -> RNG(leaf). */
 static struct mtx fxent_pool_lk;
 MTX_SYSINIT(fx_pool, &fxent_pool_lk, "fx entropy pool lock", MTX_DEF);
 #define	FXENT_LOCK()		mtx_lock(&fxent_pool_lk)
 #define	FXENT_UNLOCK()		mtx_unlock(&fxent_pool_lk)
 #define	FXENT_ASSERT(rng)	mtx_assert(&fxent_pool_lk, MA_OWNED)
 #define	FXENT_ASSERT_NOT(rng)	mtx_assert(&fxent_pool_lk, MA_NOTOWNED)
 static struct fxrng_hash fxent_pool[FXRNG_NPOOLS];
 static unsigned __read_mostly fxent_nactpools = 1;
 static struct timeout_task fxent_reseed_timer;
 static int __read_mostly fxent_timer_ready;
 
 /*
  * Track number of bytes of entropy harvested from high-quality sources prior
  * to initial keying.  The idea is to collect more jitter entropy when fewer
  * high-quality bytes were available and less if we had other good sources.  We
  * want to provide always-on availability but don't necessarily have *any*
  * great sources on some platforms.
  *
  * Like fxrng_ent_char: at some point, if the design seems reasonable, it would
  * make more sense to pull this up into the abstraction layer instead.
  *
  * Jitter entropy is unimplemented for now.
  */
 static unsigned long fxrng_preseed_ent;
 
 void
 fxrng_pools_init(void)
 {
 	size_t i;
 
 	for (i = 0; i < nitems(fxent_pool); i++)
 		fxrng_hash_init(&fxent_pool[i]);
 }
 
 static inline bool
 fxrng_hi_source(enum random_entropy_source src)
 {
 	return (fxrng_ent_char[src].entc_cls->entc_src_cls == FXRNG_HI);
 }
 
 /*
  * A racy check that this high-entropy source's event should contribute to
  * pool0 on the basis of per-source byte count.  The check is racy for two
  * reasons:
  *   - Performance: The vast majority of the time, we've already taken 32 bytes
  *     from any present high quality source and the racy check lets us avoid
  *     dirtying the cache for the global array.
  *   - Correctness: It's fine that the check is racy.  The failure modes are:
  *     • False positive: We will detect when we take the lock.
  *     • False negative: We still collect the entropy; it just won't be
  *       preferentially placed in pool0 in this case.
  */
 static inline bool
 fxrng_hi_pool0_eligible_racy(enum random_entropy_source src)
 {
 	return (atomic_load_acq_8(&fxrng_reseed_seen[src]) <
 	    FXENT_HI_SRC_POOL0_BYTES);
 }
 
 /*
  * Top level entropy processing API from randomdev.
  *
  * Invoked by the core randomdev subsystem both for preload entropy, "push"
  * sources (like interrupts, keyboard, etc) and pull sources (RDRAND, etc).
  */
 void
 fxrng_event_processor(struct harvest_event *event)
 {
 	enum random_entropy_source src;
 	unsigned pool;
 	bool first_time, first_32;
 
 	src = event->he_source;
 
 	ASSERT_DEBUG(event->he_size <= sizeof(event->he_entropy),
 	    "%s: he_size: %u > sizeof(he_entropy): %zu", __func__,
 	    (unsigned)event->he_size, sizeof(event->he_entropy));
 
 	/*
 	 * Zero bytes of source entropy doesn't count as observing this source
 	 * for the first time.  We still harvest the counter entropy.
 	 */
 	first_time = event->he_size > 0 &&
 	    !FXENT_TEST_SET_ATOMIC_ACQ(src, &fxrng_seen);
 	if (__predict_false(first_time)) {
 		/*
 		 * "The first time [any source] provides entropy, it is used to
 		 * directly reseed the root PRNG.  The entropy pools are
 		 * bypassed." (§ 3.1)
 		 *
 		 * Unlike Windows, we cannot rely on loader(8) seed material
 		 * being present, so we perform initial keying in the kernel.
 		 * We use brng_generation 0 to represent an unkeyed state.
 		 *
 		 * Prior to initial keying, it doesn't make sense to try to mix
 		 * the entropy directly with the root PRNG state, as the root
 		 * PRNG is unkeyed.  Instead, we collect pre-keying dynamic
 		 * entropy in pool0 and do not bump the root PRNG seed version
 		 * or set its key.  Initial keying will incorporate pool0 and
 		 * bump the brng_generation (seed version).
 		 *
 		 * After initial keying, we do directly mix in first-time
 		 * entropy sources.  We use the root BRNG to generate 32 bytes
 		 * and use fxrng_hash to mix it with the new entropy source and
 		 * re-key with the first 256 bits of hash output.
 		 */
 		FXENT_LOCK();
 		FXRNG_BRNG_LOCK(&fxrng_root);
 		if (__predict_true(fxrng_root.brng_generation > 0)) {
 			/* Bypass the pools: */
 			FXENT_UNLOCK();
 			fxrng_brng_src_reseed(event);
 			FXRNG_BRNG_ASSERT_NOT(&fxrng_root);
 			return;
 		}
 
 		/*
 		 * Keying the root PRNG requires both FXENT_LOCK and the PRNG's
 		 * lock, so we only need to hold on to the pool lock to prevent
 		 * initial keying without this entropy.
 		 */
 		FXRNG_BRNG_UNLOCK(&fxrng_root);
 
 		/* Root PRNG hasn't been keyed yet, just accumulate event. */
 		fxrng_hash_update(&fxent_pool[0], &event->he_somecounter,
 		    sizeof(event->he_somecounter));
 		fxrng_hash_update(&fxent_pool[0], event->he_entropy,
 		    event->he_size);
 
 		if (fxrng_hi_source(src)) {
 			/* Prevent overflow. */
 			if (fxrng_preseed_ent <= ULONG_MAX - event->he_size)
 				fxrng_preseed_ent += event->he_size;
 		}
 		FXENT_UNLOCK();
 		return;
 	}
 	/* !first_time */
 
 	/*
 	 * "The first 32 bytes produced by a high entropy source after a reseed
 	 * from the pools is always put in pool 0." (§ 3.4)
 	 *
 	 * The first-32-byte tracking data in fxrng_reseed_seen is reset in
 	 * fxent_timer_reseed_npools() below.
 	 */
 	first_32 = event->he_size > 0 &&
 	    fxrng_hi_source(src) &&
 	    atomic_load_acq_int(&fxent_nactpools) > 1 &&
 	    fxrng_hi_pool0_eligible_racy(src);
 	if (__predict_false(first_32)) {
 		unsigned rem, seen;
 
 		FXENT_LOCK();
 		seen = fxrng_reseed_seen[src];
 		if (seen == FXENT_HI_SRC_POOL0_BYTES)
 			goto round_robin;
 
 		rem = FXENT_HI_SRC_POOL0_BYTES - seen;
 		rem = MIN(rem, event->he_size);
 
 		fxrng_reseed_seen[src] = seen + rem;
 
 		/*
 		 * We put 'rem' bytes in pool0, and any remaining bytes are
 		 * round-robin'd across other pools.
 		 */
 		fxrng_hash_update(&fxent_pool[0],
 		    ((uint8_t *)event->he_entropy) + event->he_size - rem,
 		    rem);
 		if (rem == event->he_size) {
 			fxrng_hash_update(&fxent_pool[0], &event->he_somecounter,
 			    sizeof(event->he_somecounter));
 			FXENT_UNLOCK();
 			return;
 		}
 
 		/*
 		 * If fewer bytes were needed than this even provied, We only
 		 * take the last rem bytes of the entropy buffer and leave the
 		 * timecounter to be round-robin'd with the remaining entropy.
 		 */
 		event->he_size -= rem;
 		goto round_robin;
 	}
 	/* !first_32 */
 
 	FXENT_LOCK();
 
 round_robin:
 	FXENT_ASSERT();
 	pool = event->he_destination % fxent_nactpools;
 	fxrng_hash_update(&fxent_pool[pool], event->he_entropy,
 	    event->he_size);
 	fxrng_hash_update(&fxent_pool[pool], &event->he_somecounter,
 	    sizeof(event->he_somecounter));
 
 	if (__predict_false(fxrng_hi_source(src) &&
 	    atomic_load_acq_64(&fxrng_root_generation) == 0)) {
 		/* Prevent overflow. */
 		if (fxrng_preseed_ent <= ULONG_MAX - event->he_size)
 			fxrng_preseed_ent += event->he_size;
 	}
 	FXENT_UNLOCK();
 }
 
 /*
  * Top level "seeded" API/signal from randomdev.
  *
  * This is our warning that a request is coming: we need to be seeded.  In
  * fenestrasX, a request for random bytes _never_ fails.  "We (ed: ditto) have
  * observed that there are many callers that never check for the error code,
  * even if they are generating cryptographic key material." (§ 1.6)
  *
  * If we returned 'false', both read_random(9) and chacha20_randomstir()
  * (arc4random(9)) will blindly charge on with something almost certainly worse
  * than what we've got, or are able to get quickly enough.
  */
 bool
 fxrng_alg_seeded(void)
 {
 	uint8_t hash[FXRNG_HASH_SZ];
 	sbintime_t sbt;
 
 	/* The vast majority of the time, we expect to already be seeded. */
 	if (__predict_true(atomic_load_acq_64(&fxrng_root_generation) != 0))
 		return (true);
 
 	/*
 	 * Take the lock and recheck; only one thread needs to do the initial
 	 * seeding work.
 	 */
 	FXENT_LOCK();
 	if (atomic_load_acq_64(&fxrng_root_generation) != 0) {
 		FXENT_UNLOCK();
 		return (true);
 	}
 	/* XXX Any one-off initial seeding goes here. */
 
 	fxrng_hash_finish(&fxent_pool[0], hash, sizeof(hash));
 	fxrng_hash_init(&fxent_pool[0]);
 
 	fxrng_brng_reseed(hash, sizeof(hash));
 	FXENT_UNLOCK();
 
 	randomdev_unblock();
 	explicit_bzero(hash, sizeof(hash));
 
 	/*
 	 * This may be called too early for taskqueue_thread to be initialized.
 	 * fxent_pool_timer_init will detect if we've already unblocked and
 	 * queue the first timer reseed at that point.
 	 */
 	if (atomic_load_acq_int(&fxent_timer_ready) != 0) {
 		sbt = SBT_1S;
 		taskqueue_enqueue_timeout_sbt(taskqueue_thread,
 		    &fxent_reseed_timer, -sbt, (sbt / 3), C_PREL(2));
 	}
 	return (true);
 }
 
 /*
  * Timer-based reseeds and pool expansion.
  */
 static void
 fxent_timer_reseed_npools(unsigned n)
 {
 	/*
 	 * 64 * 8 => moderately large 512 bytes.  Could be static, as we are
 	 * only used in a static context.  On the other hand, this is in
 	 * threadqueue TASK context and we're likely nearly at top of stack
 	 * already.
 	 */
 	uint8_t hash[FXRNG_HASH_SZ * FXRNG_NPOOLS];
 	unsigned i;
 
 	ASSERT_DEBUG(n > 0 && n <= FXRNG_NPOOLS, "n:%u", n);
 
 	FXENT_ASSERT();
 	/*
 	 * Collect entropy from pools 0..n-1 by concatenating the output hashes
 	 * and then feeding them into fxrng_brng_reseed, which will hash the
 	 * aggregate together with the current root PRNG keystate to produce a
 	 * new key.  It will also bump the global generation counter
 	 * appropriately.
 	 */
 	for (i = 0; i < n; i++) {
 		fxrng_hash_finish(&fxent_pool[i], hash + i * FXRNG_HASH_SZ,
 		    FXRNG_HASH_SZ);
 		fxrng_hash_init(&fxent_pool[i]);
 	}
 
 	fxrng_brng_reseed(hash, n * FXRNG_HASH_SZ);
 	explicit_bzero(hash, n * FXRNG_HASH_SZ);
 
 	/*
 	 * "The first 32 bytes produced by a high entropy source after a reseed
 	 * from the pools is always put in pool 0." (§ 3.4)
 	 *
 	 * So here we reset the tracking (somewhat naively given the majority
 	 * of sources on most machines are not what we consider "high", but at
 	 * 32 bytes it's smaller than a cache line), so the next 32 bytes are
 	 * prioritized into pool0.
 	 *
 	 * See corresponding use of fxrng_reseed_seen in fxrng_event_processor.
 	 */
 	memset(fxrng_reseed_seen, 0, sizeof(fxrng_reseed_seen));
 	FXENT_ASSERT();
 }
 
 static void
 fxent_timer_reseed(void *ctx __unused, int pending __unused)
 {
 	static unsigned reseed_intvl_sec = 1;
 	/* Only reseeds after FXENT_RESEED_INTVL_MAX is achieved. */
 	static uint64_t reseed_number = 1;
 
 	unsigned next_ival, i, k;
 	sbintime_t sbt;
 
 	if (reseed_intvl_sec < FXENT_RESEED_INTVL_MAX) {
 		next_ival = FXENT_RESSED_INTVL_GFACT * reseed_intvl_sec;
 		if (next_ival > FXENT_RESEED_INTVL_MAX)
 			next_ival = FXENT_RESEED_INTVL_MAX;
 		FXENT_LOCK();
 		fxent_timer_reseed_npools(1);
 		FXENT_UNLOCK();
 	} else {
 		/*
 		 * The creation of entropy pools beyond 0 is enabled when the
 		 * reseed interval hits the maximum. (§ 3.3)
 		 */
 		next_ival = reseed_intvl_sec;
 
 		/*
 		 * Pool 0 is used every reseed; pool 1..0 every 3rd reseed; and in
 		 * general, pool n..0 every 3^n reseeds.
 		 */
 		k = reseed_number;
 		reseed_number++;
 
 		/* Count how many pools, from [0, i), to use for reseed. */
 		for (i = 1; i < MIN(fxent_nactpools + 1, FXRNG_NPOOLS); i++) {
 			if ((k % FXENT_RESEED_BASE) != 0)
 				break;
 			k /= FXENT_RESEED_BASE;
 		}
 
 		/*
 		 * If we haven't activated pool i yet, activate it and only
 		 * reseed from [0, i-1).  (§ 3.3)
 		 */
 		FXENT_LOCK();
 		if (i == fxent_nactpools + 1) {
 			fxent_timer_reseed_npools(fxent_nactpools);
 			fxent_nactpools++;
 		} else {
 			/* Just reseed from [0, i). */
 			fxent_timer_reseed_npools(i);
 		}
 		FXENT_UNLOCK();
 	}
 
 	/* Schedule the next reseed. */
 	sbt = next_ival * SBT_1S;
 	taskqueue_enqueue_timeout_sbt(taskqueue_thread, &fxent_reseed_timer,
 	    -sbt, (sbt / 3), C_PREL(2));
 
 	reseed_intvl_sec = next_ival;
 }
 
 static void
 fxent_pool_timer_init(void *dummy __unused)
 {
 	sbintime_t sbt;
 
 	TIMEOUT_TASK_INIT(taskqueue_thread, &fxent_reseed_timer, 0,
 	    fxent_timer_reseed, NULL);
 
 	if (atomic_load_acq_64(&fxrng_root_generation) != 0) {
 		sbt = SBT_1S;
 		taskqueue_enqueue_timeout_sbt(taskqueue_thread,
 		    &fxent_reseed_timer, -sbt, (sbt / 3), C_PREL(2));
 	}
 	atomic_store_rel_int(&fxent_timer_ready, 1);
 }
 /* After taskqueue_thread is initialized in SI_SUB_TASKQ:SI_ORDER_SECOND. */
 SYSINIT(fxent_pool_timer_init, SI_SUB_TASKQ, SI_ORDER_ANY,
     fxent_pool_timer_init, NULL);
Index: head/sys/dev/random/fenestrasX/fx_priv.h
===================================================================
--- head/sys/dev/random/fenestrasX/fx_priv.h	(revision 366620)
+++ head/sys/dev/random/fenestrasX/fx_priv.h	(revision 366621)
@@ -1,49 +1,48 @@
 /*-
  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
  *
  * Copyright (c) 2019 Conrad Meyer <cem@FreeBSD.org>
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
  * $FreeBSD$
  */
 #pragma once
 
 #define	ASSERT(x, fmt, ...) do {					\
 	if (__predict_true(x))					\
 		break;							\
 	panic("%s:%d: Assertion '" #x "' in function %s failed: " fmt, 	\
 	    __FILE__, __LINE__, __func__, ## __VA_ARGS__);		\
 } while (false)
 #ifdef INVARIANTS
 #define	ASSERT_DEBUG(x, fmt, ...) do {					\
 	if (__predict_true(x))					\
 		break;							\
 	panic("%s:%d: Assertion '" #x "' in function %s failed: " fmt, 	\
 	    __FILE__, __LINE__, __func__, ## __VA_ARGS__);		\
 } while (false)
 #else
 #define	ASSERT_DEBUG(...)
 #endif
 
 extern struct fxrng_buffered_rng fxrng_root;
-extern uint64_t __read_mostly fxrng_root_generation;
Index: head/sys/dev/random/fenestrasX/fx_pub.h
===================================================================
--- head/sys/dev/random/fenestrasX/fx_pub.h	(nonexistent)
+++ head/sys/dev/random/fenestrasX/fx_pub.h	(revision 366621)
@@ -0,0 +1,53 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2019 Conrad Meyer <cem@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#pragma once
+
+#include <sys/systm.h>
+
+/*
+ * The root BRNG seed version, or generation.
+ *
+ * FenestrasX-aware downstream CSPRNGs (i.e., arc4random(9)) should track the
+ * generation number they seeded from, using the read_random_key(9) API below.
+ * If their current seed version is older than the root generation, they should
+ * reseed before producing output.
+ *
+ * The variable is read-only outside of the fenestrasX implementation and
+ * should be accessed using 'atomic_load_acq_64(&fxrng_root_generation)'.
+ * Reseeds are extremely infrequent, so callers may wish to hint to the
+ * compiler that a matching generation is the expected case, with
+ * __predict_true() or __predict_false().
+ */
+extern uint64_t __read_mostly fxrng_root_generation;
+
+/*
+ * A routine for generating seed/key material 
+ * Bypasses random(4) for now, but conceivably could be incorporated into that.
+ */
+void read_random_key(void *buf, size_t nbytes, uint64_t *seed_version_out);

Property changes on: head/sys/dev/random/fenestrasX/fx_pub.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+FreeBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Index: head/sys/dev/random/randomdev.c
===================================================================
--- head/sys/dev/random/randomdev.c	(revision 366620)
+++ head/sys/dev/random/randomdev.c	(revision 366621)
@@ -1,433 +1,435 @@
 /*-
  * Copyright (c) 2017 Oliver Pinter
  * Copyright (c) 2000-2015 Mark R V Murray
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer
  *    in this position and unchanged.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  */
 
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/bus.h>
 #include <sys/conf.h>
 #include <sys/fcntl.h>
 #include <sys/filio.h>
 #include <sys/kernel.h>
 #include <sys/kthread.h>
 #include <sys/lock.h>
 #include <sys/module.h>
 #include <sys/malloc.h>
 #include <sys/poll.h>
 #include <sys/proc.h>
 #include <sys/random.h>
 #include <sys/sbuf.h>
 #include <sys/selinfo.h>
 #include <sys/sysctl.h>
 #include <sys/systm.h>
 #include <sys/uio.h>
 #include <sys/unistd.h>
 
 #include <crypto/rijndael/rijndael-api-fst.h>
 #include <crypto/sha2/sha256.h>
 
 #include <dev/random/hash.h>
 #include <dev/random/randomdev.h>
 #include <dev/random/random_harvestq.h>
 
 #define	RANDOM_UNIT	0
 
 /*
  * In loadable random, the core randomdev.c / random(9) routines have static
  * visibility and an alternative name to avoid conflicting with the function
  * pointers of the real names in the core kernel.  random_alg_context_init
  * installs pointers to the loadable static names into the core kernel's
  * function pointers at SI_SUB_RANDOM:SI_ORDER_SECOND.
  */
 #if defined(RANDOM_LOADABLE)
 static int (read_random_uio)(struct uio *, bool);
 static void (read_random)(void *, u_int);
 static bool (is_random_seeded)(void);
 #endif
 
 static d_read_t randomdev_read;
 static d_write_t randomdev_write;
 static d_poll_t randomdev_poll;
 static d_ioctl_t randomdev_ioctl;
 
 static struct cdevsw random_cdevsw = {
 	.d_name = "random",
 	.d_version = D_VERSION,
 	.d_read = randomdev_read,
 	.d_write = randomdev_write,
 	.d_poll = randomdev_poll,
 	.d_ioctl = randomdev_ioctl,
 };
 
 /* For use with make_dev(9)/destroy_dev(9). */
 static struct cdev *random_dev;
 
 #if defined(RANDOM_LOADABLE)
 static void
 random_alg_context_init(void *dummy __unused)
 {
 	_read_random_uio = (read_random_uio);
 	_read_random = (read_random);
 	_is_random_seeded = (is_random_seeded);
 }
 SYSINIT(random_device, SI_SUB_RANDOM, SI_ORDER_SECOND, random_alg_context_init,
     NULL);
 #endif
 
 static struct selinfo rsel;
 
 /*
  * This is the read uio(9) interface for random(4).
  */
 /* ARGSUSED */
 static int
 randomdev_read(struct cdev *dev __unused, struct uio *uio, int flags)
 {
 
 	return ((read_random_uio)(uio, (flags & O_NONBLOCK) != 0));
 }
 
 /*
  * If the random device is not seeded, blocks until it is seeded.
  *
  * Returns zero when the random device is seeded.
  *
  * If the 'interruptible' parameter is true, and the device is unseeded, this
  * routine may be interrupted.  If interrupted, it will return either ERESTART
  * or EINTR.
  */
 #define SEEDWAIT_INTERRUPTIBLE		true
 #define SEEDWAIT_UNINTERRUPTIBLE	false
 static int
 randomdev_wait_until_seeded(bool interruptible)
 {
 	int error, spamcount, slpflags;
 
 	slpflags = interruptible ? PCATCH : 0;
 
 	error = 0;
 	spamcount = 0;
 	while (!p_random_alg_context->ra_seeded()) {
 		/* keep tapping away at the pre-read until we seed/unblock. */
 		p_random_alg_context->ra_pre_read();
 		/* Only bother the console every 10 seconds or so */
 		if (spamcount == 0)
 			printf("random: %s unblock wait\n", __func__);
 		spamcount = (spamcount + 1) % 100;
 		error = tsleep(p_random_alg_context, slpflags, "randseed",
 		    hz / 10);
 		if (error == ERESTART || error == EINTR) {
 			KASSERT(interruptible,
 			    ("unexpected wake of non-interruptible sleep"));
 			break;
 		}
 		/* Squash tsleep timeout condition */
 		if (error == EWOULDBLOCK)
 			error = 0;
 		KASSERT(error == 0, ("unexpected tsleep error %d", error));
 	}
 	return (error);
 }
 
 int
 (read_random_uio)(struct uio *uio, bool nonblock)
 {
 	/* 16 MiB takes about 0.08 s CPU time on my 2017 AMD Zen CPU */
 #define SIGCHK_PERIOD (16 * 1024 * 1024)
 	const size_t sigchk_period = SIGCHK_PERIOD;
 	CTASSERT(SIGCHK_PERIOD % PAGE_SIZE == 0);
 #undef SIGCHK_PERIOD
 
 	uint8_t *random_buf;
 	size_t total_read, read_len;
 	ssize_t bufsize;
 	int error;
 
 
 	KASSERT(uio->uio_rw == UIO_READ, ("%s: bogus write", __func__));
 	KASSERT(uio->uio_resid >= 0, ("%s: bogus negative resid", __func__));
 
 	p_random_alg_context->ra_pre_read();
 	error = 0;
 	/* (Un)Blocking logic */
 	if (!p_random_alg_context->ra_seeded()) {
 		if (nonblock)
 			error = EWOULDBLOCK;
 		else
 			error = randomdev_wait_until_seeded(
 			    SEEDWAIT_INTERRUPTIBLE);
 	}
 	if (error != 0)
 		return (error);
 
 	read_rate_increment(howmany(uio->uio_resid + 1, sizeof(uint32_t)));
 	total_read = 0;
 
 	/* Easy to deal with the trivial 0 byte case. */
 	if (__predict_false(uio->uio_resid == 0))
 		return (0);
 
 	/*
 	 * If memory is plentiful, use maximally sized requests to avoid
 	 * per-call algorithm overhead.  But fall back to a single page
 	 * allocation if the full request isn't immediately available.
 	 */
 	bufsize = MIN(sigchk_period, (size_t)uio->uio_resid);
 	random_buf = malloc(bufsize, M_ENTROPY, M_NOWAIT);
 	if (random_buf == NULL) {
 		bufsize = PAGE_SIZE;
 		random_buf = malloc(bufsize, M_ENTROPY, M_WAITOK);
 	}
 
 	error = 0;
 	while (uio->uio_resid > 0 && error == 0) {
 		read_len = MIN((size_t)uio->uio_resid, bufsize);
 
 		p_random_alg_context->ra_read(random_buf, read_len);
 
 		/*
 		 * uiomove() may yield the CPU before each 'read_len' bytes (up
 		 * to bufsize) are copied out.
 		 */
 		error = uiomove(random_buf, read_len, uio);
 		total_read += read_len;
 
 		/*
 		 * Poll for signals every few MBs to avoid very long
 		 * uninterruptible syscalls.
 		 */
 		if (error == 0 && uio->uio_resid != 0 &&
 		    total_read % sigchk_period == 0) {
 			error = tsleep_sbt(p_random_alg_context, PCATCH,
 			    "randrd", SBT_1NS, 0, C_HARDCLOCK);
 			/* Squash tsleep timeout condition */
 			if (error == EWOULDBLOCK)
 				error = 0;
 		}
 	}
 
 	/*
 	 * Short reads due to signal interrupt should not indicate error.
 	 * Instead, the uio will reflect that the read was shorter than
 	 * requested.
 	 */
 	if (error == ERESTART || error == EINTR)
 		error = 0;
 
 	zfree(random_buf, M_ENTROPY);
 	return (error);
 }
 
 /*-
  * Kernel API version of read_random().  This is similar to read_random_uio(),
  * except it doesn't interface with uio(9).  It cannot assumed that random_buf
  * is a multiple of RANDOM_BLOCKSIZE bytes.
  *
  * If the tunable 'kern.random.initial_seeding.bypass_before_seeding' is set
  * non-zero, silently fail to emit random data (matching the pre-r346250
  * behavior).  If read_random is called prior to seeding and bypassed because
  * of this tunable, the condition is reported in the read-only sysctl
  * 'kern.random.initial_seeding.read_random_bypassed_before_seeding'.
  */
 void
 (read_random)(void *random_buf, u_int len)
 {
 
 	KASSERT(random_buf != NULL, ("No suitable random buffer in %s", __func__));
 	p_random_alg_context->ra_pre_read();
 
 	if (len == 0)
 		return;
 
 	/* (Un)Blocking logic */
 	if (__predict_false(!p_random_alg_context->ra_seeded())) {
 		if (random_bypass_before_seeding) {
 			if (!read_random_bypassed_before_seeding) {
 				if (!random_bypass_disable_warnings)
 					printf("read_random: WARNING: bypassing"
 					    " request for random data because "
 					    "the random device is not yet "
 					    "seeded and the knob "
 					    "'bypass_before_seeding' was "
 					    "enabled.\n");
 				read_random_bypassed_before_seeding = true;
 			}
 			/* Avoid potentially leaking stack garbage */
 			memset(random_buf, 0, len);
 			return;
 		}
 
 		(void)randomdev_wait_until_seeded(SEEDWAIT_UNINTERRUPTIBLE);
 	}
 	read_rate_increment(roundup2(len, sizeof(uint32_t)));
 	p_random_alg_context->ra_read(random_buf, len);
 }
 
 bool
 (is_random_seeded)(void)
 {
 	return (p_random_alg_context->ra_seeded());
 }
 
 static __inline void
 randomdev_accumulate(uint8_t *buf, u_int count)
 {
 	static u_int destination = 0;
 	static struct harvest_event event;
 	static struct randomdev_hash hash;
 	static uint32_t entropy_data[RANDOM_KEYSIZE_WORDS];
 	uint32_t timestamp;
 	int i;
 
 	/* Extra timing here is helpful to scrape scheduler jitter entropy */
 	randomdev_hash_init(&hash);
 	timestamp = (uint32_t)get_cyclecount();
 	randomdev_hash_iterate(&hash, &timestamp, sizeof(timestamp));
 	randomdev_hash_iterate(&hash, buf, count);
 	timestamp = (uint32_t)get_cyclecount();
 	randomdev_hash_iterate(&hash, &timestamp, sizeof(timestamp));
 	randomdev_hash_finish(&hash, entropy_data);
 	for (i = 0; i < RANDOM_KEYSIZE_WORDS; i += sizeof(event.he_entropy)/sizeof(event.he_entropy[0])) {
 		event.he_somecounter = (uint32_t)get_cyclecount();
 		event.he_size = sizeof(event.he_entropy);
 		event.he_source = RANDOM_CACHED;
 		event.he_destination = destination++; /* Harmless cheating */
 		memcpy(event.he_entropy, entropy_data + i, sizeof(event.he_entropy));
 		p_random_alg_context->ra_event_processor(&event);
 	}
 	explicit_bzero(&event, sizeof(event));
 	explicit_bzero(entropy_data, sizeof(entropy_data));
 }
 
 /* ARGSUSED */
 static int
 randomdev_write(struct cdev *dev __unused, struct uio *uio, int flags __unused)
 {
 	uint8_t *random_buf;
 	int c, error = 0;
 	ssize_t nbytes;
 
 	random_buf = malloc(PAGE_SIZE, M_ENTROPY, M_WAITOK);
 	nbytes = uio->uio_resid;
 	while (uio->uio_resid > 0 && error == 0) {
 		c = MIN(uio->uio_resid, PAGE_SIZE);
 		error = uiomove(random_buf, c, uio);
 		if (error)
 			break;
 		randomdev_accumulate(random_buf, c);
 		tsleep(p_random_alg_context, 0, "randwr", hz/10);
 	}
 	if (nbytes != uio->uio_resid && (error == ERESTART || error == EINTR))
 		/* Partial write, not error. */
 		error = 0;
 	free(random_buf, M_ENTROPY);
 	return (error);
 }
 
 /* ARGSUSED */
 static int
 randomdev_poll(struct cdev *dev __unused, int events, struct thread *td __unused)
 {
 
 	if (events & (POLLIN | POLLRDNORM)) {
 		if (p_random_alg_context->ra_seeded())
 			events &= (POLLIN | POLLRDNORM);
 		else
 			selrecord(td, &rsel);
 	}
 	return (events);
 }
 
 /* This will be called by the entropy processor when it seeds itself and becomes secure */
 void
 randomdev_unblock(void)
 {
 
 	selwakeuppri(&rsel, PUSER);
 	wakeup(p_random_alg_context);
 	printf("random: unblocking device.\n");
+#ifndef RANDOM_FENESTRASX
 	/* Do random(9) a favour while we are about it. */
 	(void)atomic_cmpset_int(&arc4rand_iniseed_state, ARC4_ENTR_NONE, ARC4_ENTR_HAVE);
+#endif
 }
 
 /* ARGSUSED */
 static int
 randomdev_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr __unused,
     int flags __unused, struct thread *td __unused)
 {
 	int error = 0;
 
 	switch (cmd) {
 		/* Really handled in upper layer */
 	case FIOASYNC:
 	case FIONBIO:
 		break;
 	default:
 		error = ENOTTY;
 	}
 
 	return (error);
 }
 
 /* ARGSUSED */
 static int
 randomdev_modevent(module_t mod __unused, int type, void *data __unused)
 {
 	int error = 0;
 
 	switch (type) {
 	case MOD_LOAD:
 		printf("random: entropy device external interface\n");
 		random_dev = make_dev_credf(MAKEDEV_ETERNAL_KLD, &random_cdevsw,
 		    RANDOM_UNIT, NULL, UID_ROOT, GID_WHEEL, 0644, "random");
 		make_dev_alias(random_dev, "urandom"); /* compatibility */
 		break;
 	case MOD_UNLOAD:
 		error = EBUSY;
 		break;
 	case MOD_SHUTDOWN:
 		break;
 	default:
 		error = EOPNOTSUPP;
 		break;
 	}
 	return (error);
 }
 
 static moduledata_t randomdev_mod = {
 	"random_device",
 	randomdev_modevent,
 	0
 };
 
 DECLARE_MODULE(random_device, randomdev_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
 MODULE_VERSION(random_device, 1);
 MODULE_DEPEND(random_device, crypto, 1, 1, 1);
 MODULE_DEPEND(random_device, random_harvestq, 1, 1, 1);
Index: head/sys/libkern/arc4random.c
===================================================================
--- head/sys/libkern/arc4random.c	(revision 366620)
+++ head/sys/libkern/arc4random.c	(revision 366621)
@@ -1,219 +1,253 @@
 /*-
  * Copyright (c) 2017 The FreeBSD Foundation
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer
  *    in this position and unchanged.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  */
 
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
 #include <sys/types.h>
 #include <sys/param.h>
 #include <sys/kernel.h>
 #include <sys/libkern.h>
 #include <sys/linker.h>
 #include <sys/lock.h>
 #include <sys/malloc.h>
 #include <sys/mutex.h>
 #include <sys/random.h>
 #include <sys/smp.h>
 #include <sys/time.h>
 
+#include <machine/cpu.h>
+
 #include <crypto/chacha20/chacha.h>
 #include <crypto/sha2/sha256.h>
 #include <dev/random/randomdev.h>
-#include <machine/cpu.h>
+#ifdef RANDOM_FENESTRASX
+#include <dev/random/fenestrasX/fx_pub.h>
+#endif
 
 #define	CHACHA20_RESEED_BYTES	65536
 #define	CHACHA20_RESEED_SECONDS	300
 #define	CHACHA20_KEYBYTES	32
 #define	CHACHA20_BUFFER_SIZE	64
 
 CTASSERT(CHACHA20_KEYBYTES*8 >= CHACHA_MINKEYLEN);
 
+#ifndef RANDOM_FENESTRASX
 int arc4rand_iniseed_state = ARC4_ENTR_NONE;
+#endif
 
 MALLOC_DEFINE(M_CHACHA20RANDOM, "chacha20random", "chacha20random structures");
 
 struct chacha20_s {
 	struct mtx mtx;
 	int numbytes;
 	time_t t_reseed;
 	u_int8_t m_buffer[CHACHA20_BUFFER_SIZE];
 	struct chacha_ctx ctx;
+#ifdef RANDOM_FENESTRASX
+	uint64_t seed_version;
+#endif
 } __aligned(CACHE_LINE_SIZE);
 
 static struct chacha20_s *chacha20inst = NULL;
 
 #define CHACHA20_FOREACH(_chacha20) \
 	for (_chacha20 = &chacha20inst[0]; \
 	     _chacha20 <= &chacha20inst[mp_maxid]; \
 	     _chacha20++)
 
 /*
  * Mix up the current context.
  */
 static void
 chacha20_randomstir(struct chacha20_s *chacha20)
 {
 	struct timeval tv_now;
 	u_int8_t key[CHACHA20_KEYBYTES];
+#ifdef RANDOM_FENESTRASX
+	uint64_t seed_version;
 
+#else
 	if (__predict_false(random_bypass_before_seeding && !is_random_seeded())) {
 		SHA256_CTX ctx;
 		uint64_t cc;
 		uint32_t fver;
 
 		if (!arc4random_bypassed_before_seeding) {
 			arc4random_bypassed_before_seeding = true;
 			if (!random_bypass_disable_warnings)
 				printf("arc4random: WARNING: initial seeding "
 				    "bypassed the cryptographic random device "
 				    "because it was not yet seeded and the "
 				    "knob 'bypass_before_seeding' was "
 				    "enabled.\n");
 		}
 
 		/* Last ditch effort to inject something in a bad condition. */
 		cc = get_cyclecount();
 		SHA256_Init(&ctx);
 		SHA256_Update(&ctx, key, sizeof(key));
 		SHA256_Update(&ctx, &cc, sizeof(cc));
 		fver = __FreeBSD_version;
 		SHA256_Update(&ctx, &fver, sizeof(fver));
 		_Static_assert(sizeof(key) == SHA256_DIGEST_LENGTH,
 		    "make sure 256 bits is still 256 bits");
 		SHA256_Final(key, &ctx);
 	} else {
+#endif
+#ifdef RANDOM_FENESTRASX
+		read_random_key(key, CHACHA20_KEYBYTES, &seed_version);
+#else
 		/*
 		* If the loader(8) did not have an entropy stash from the
 		* previous shutdown to load, then we will block.  The answer is
 		* to make sure there is an entropy stash at shutdown time.
 		*
 		* On the other hand, if the random_bypass_before_seeding knob
 		* was set and we landed in this branch, we know this won't
 		* block because we know the random device is seeded.
 		*/
 		read_random(key, CHACHA20_KEYBYTES);
 	}
+#endif
 	getmicrouptime(&tv_now);
 	mtx_lock(&chacha20->mtx);
 	chacha_keysetup(&chacha20->ctx, key, CHACHA20_KEYBYTES*8);
 	chacha_ivsetup(&chacha20->ctx, (u_char *)&tv_now.tv_sec, (u_char *)&tv_now.tv_usec);
 	/* Reset for next reseed cycle. */
 	chacha20->t_reseed = tv_now.tv_sec + CHACHA20_RESEED_SECONDS;
 	chacha20->numbytes = 0;
+#ifdef RANDOM_FENESTRASX
+	chacha20->seed_version = seed_version;
+#endif
 	mtx_unlock(&chacha20->mtx);
 }
 
 /*
  * Initialize the contexts.
  */
 static void
 chacha20_init(void)
 {
 	struct chacha20_s *chacha20;
 
 	chacha20inst = malloc((mp_maxid + 1) * sizeof(struct chacha20_s),
 			M_CHACHA20RANDOM, M_NOWAIT | M_ZERO);
 	KASSERT(chacha20inst != NULL, ("chacha20_init: memory allocation error"));
 
 	CHACHA20_FOREACH(chacha20) {
 		mtx_init(&chacha20->mtx, "chacha20_mtx", NULL, MTX_DEF);
 		chacha20->t_reseed = -1;
 		chacha20->numbytes = 0;
 		explicit_bzero(chacha20->m_buffer, CHACHA20_BUFFER_SIZE);
 		explicit_bzero(&chacha20->ctx, sizeof(chacha20->ctx));
 	}
 }
 SYSINIT(chacha20, SI_SUB_LOCK, SI_ORDER_ANY, chacha20_init, NULL);
 
 
 static void
 chacha20_uninit(void)
 {
 	struct chacha20_s *chacha20;
 
 	CHACHA20_FOREACH(chacha20)
 		mtx_destroy(&chacha20->mtx);
 	free(chacha20inst, M_CHACHA20RANDOM);
 }
 SYSUNINIT(chacha20, SI_SUB_LOCK, SI_ORDER_ANY, chacha20_uninit, NULL);
 
 
 /*
  * MPSAFE
  */
 void
 arc4rand(void *ptr, u_int len, int reseed)
 {
 	struct chacha20_s *chacha20;
 	struct timeval tv;
 	u_int length;
 	u_int8_t *p;
 
+#ifdef RANDOM_FENESTRASX
+	if (__predict_false(reseed))
+#else
 	if (__predict_false(reseed ||
 	    (arc4rand_iniseed_state == ARC4_ENTR_HAVE &&
 	    atomic_cmpset_int(&arc4rand_iniseed_state, ARC4_ENTR_HAVE, ARC4_ENTR_SEED))))
+#endif
 		CHACHA20_FOREACH(chacha20)
 			chacha20_randomstir(chacha20);
 
 	getmicrouptime(&tv);
 	chacha20 = &chacha20inst[curcpu];
 	/* We may get unlucky and be migrated off this CPU, but that is expected to be infrequent */
 	if ((chacha20->numbytes > CHACHA20_RESEED_BYTES) || (tv.tv_sec > chacha20->t_reseed))
 		chacha20_randomstir(chacha20);
 
-	p = ptr;
 	mtx_lock(&chacha20->mtx);
+#ifdef RANDOM_FENESTRASX
+	if (__predict_false(
+	    atomic_load_acq_64(&fxrng_root_generation) != chacha20->seed_version
+	    )) {
+		mtx_unlock(&chacha20->mtx);
+		chacha20_randomstir(chacha20);
+		mtx_lock(&chacha20->mtx);
+	}
+#endif
+
+	p = ptr;
 	while (len) {
 		length = MIN(CHACHA20_BUFFER_SIZE, len);
 		chacha_encrypt_bytes(&chacha20->ctx, chacha20->m_buffer, p, length);
 		p += length;
 		len -= length;
 		chacha20->numbytes += length;
 		if (chacha20->numbytes > CHACHA20_RESEED_BYTES) {
 			mtx_unlock(&chacha20->mtx);
 			chacha20_randomstir(chacha20);
 			mtx_lock(&chacha20->mtx);
 		}
 	}
 	mtx_unlock(&chacha20->mtx);
 }
 
 uint32_t
 arc4random(void)
 {
 	uint32_t ret;
 
 	arc4rand(&ret, sizeof(ret), 0);
 	return ret;
 }
 
 void
 arc4random_buf(void *ptr, size_t len)
 {
 
 	arc4rand(ptr, len, 0);
 }
Index: head/sys/sys/libkern.h
===================================================================
--- head/sys/sys/libkern.h	(revision 366620)
+++ head/sys/sys/libkern.h	(revision 366621)
@@ -1,227 +1,229 @@
 /*-
  * SPDX-License-Identifier: BSD-3-Clause
  *
  * Copyright (c) 1992, 1993
  *	The Regents of the University of California.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  * 3. Neither the name of the University nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
  *	@(#)libkern.h	8.1 (Berkeley) 6/10/93
  * $FreeBSD$
  */
 
 #ifndef _SYS_LIBKERN_H_
 #define	_SYS_LIBKERN_H_
 
 #include <sys/cdefs.h>
 #include <sys/types.h>
 #ifdef _KERNEL
 #include <sys/systm.h>
 #endif
 
 #ifndef	LIBKERN_INLINE
 #define	LIBKERN_INLINE  static __inline
 #define	LIBKERN_BODY
 #endif
 
 /* BCD conversions. */
 extern u_char const	bcd2bin_data[];
 extern u_char const	bin2bcd_data[];
 extern char const	hex2ascii_data[];
 
 #define	LIBKERN_LEN_BCD2BIN	154
 #define	LIBKERN_LEN_BIN2BCD	100
 #define	LIBKERN_LEN_HEX2ASCII	36
 
 static inline u_char
 bcd2bin(int bcd)
 {
 
 	KASSERT(bcd >= 0 && bcd < LIBKERN_LEN_BCD2BIN,
 	    ("invalid bcd %d", bcd));
 	return (bcd2bin_data[bcd]);
 }
 
 static inline u_char
 bin2bcd(int bin)
 {
 
 	KASSERT(bin >= 0 && bin < LIBKERN_LEN_BIN2BCD,
 	    ("invalid bin %d", bin));
 	return (bin2bcd_data[bin]);
 }
 
 static inline char
 hex2ascii(int hex)
 {
 
 	KASSERT(hex >= 0 && hex < LIBKERN_LEN_HEX2ASCII,
 	    ("invalid hex %d", hex));
 	return (hex2ascii_data[hex]);
 }
 
 static inline bool
 validbcd(int bcd)
 {
 
 	return (bcd == 0 || (bcd > 0 && bcd <= 0x99 && bcd2bin_data[bcd] != 0));
 }
 static __inline int imax(int a, int b) { return (a > b ? a : b); }
 static __inline int imin(int a, int b) { return (a < b ? a : b); }
 static __inline long lmax(long a, long b) { return (a > b ? a : b); }
 static __inline long lmin(long a, long b) { return (a < b ? a : b); }
 static __inline u_int max(u_int a, u_int b) { return (a > b ? a : b); }
 static __inline u_int min(u_int a, u_int b) { return (a < b ? a : b); }
 static __inline quad_t qmax(quad_t a, quad_t b) { return (a > b ? a : b); }
 static __inline quad_t qmin(quad_t a, quad_t b) { return (a < b ? a : b); }
 static __inline u_quad_t uqmax(u_quad_t a, u_quad_t b) { return (a > b ? a : b); }
 static __inline u_quad_t uqmin(u_quad_t a, u_quad_t b) { return (a < b ? a : b); }
 static __inline u_long ulmax(u_long a, u_long b) { return (a > b ? a : b); }
 static __inline u_long ulmin(u_long a, u_long b) { return (a < b ? a : b); }
 static __inline __uintmax_t ummax(__uintmax_t a, __uintmax_t b)
 {
 
 	return (a > b ? a : b);
 }
 static __inline __uintmax_t ummin(__uintmax_t a, __uintmax_t b)
 {
 
 	return (a < b ? a : b);
 }
 static __inline off_t omax(off_t a, off_t b) { return (a > b ? a : b); }
 static __inline off_t omin(off_t a, off_t b) { return (a < b ? a : b); }
 static __inline int abs(int a) { return (a < 0 ? -a : a); }
 static __inline long labs(long a) { return (a < 0 ? -a : a); }
 static __inline quad_t qabs(quad_t a) { return (a < 0 ? -a : a); }
 
+#ifndef RANDOM_FENESTRASX
 #define	ARC4_ENTR_NONE	0	/* Don't have entropy yet. */
 #define	ARC4_ENTR_HAVE	1	/* Have entropy. */
 #define	ARC4_ENTR_SEED	2	/* Reseeding. */
 extern int arc4rand_iniseed_state;
+#endif
 
 /* Prototypes for non-quad routines. */
 struct malloc_type;
 uint32_t arc4random(void);
 void	 arc4random_buf(void *, size_t);
 uint32_t arc4random_uniform(uint32_t);
 void	 arc4rand(void *, u_int, int);
 int	 timingsafe_bcmp(const void *, const void *, size_t);
 void	*bsearch(const void *, const void *, size_t,
 	    size_t, int (*)(const void *, const void *));
 #ifndef	HAVE_INLINE_FFS
 int	 ffs(int);
 #endif
 #ifndef	HAVE_INLINE_FFSL
 int	 ffsl(long);
 #endif
 #ifndef	HAVE_INLINE_FFSLL
 int	 ffsll(long long);
 #endif
 #ifndef	HAVE_INLINE_FLS
 int	 fls(int);
 #endif
 #ifndef	HAVE_INLINE_FLSL
 int	 flsl(long);
 #endif
 #ifndef	HAVE_INLINE_FLSLL
 int	 flsll(long long);
 #endif
 #define	bitcount64(x)	__bitcount64((uint64_t)(x))
 #define	bitcount32(x)	__bitcount32((uint32_t)(x))
 #define	bitcount16(x)	__bitcount16((uint16_t)(x))
 #define	bitcountl(x)	__bitcountl((u_long)(x))
 #define	bitcount(x)	__bitcount((u_int)(x))
 
 int	 fnmatch(const char *, const char *, int);
 int	 locc(int, char *, u_int);
 void	*memchr(const void *s, int c, size_t n);
 void	*memcchr(const void *s, int c, size_t n);
 void	*memmem(const void *l, size_t l_len, const void *s, size_t s_len);
 void	 qsort(void *base, size_t nmemb, size_t size,
 	    int (*compar)(const void *, const void *));
 void	 qsort_r(void *base, size_t nmemb, size_t size, void *thunk,
 	    int (*compar)(void *, const void *, const void *));
 u_long	 random(void);
 int	 scanc(u_int, const u_char *, const u_char *, int);
 int	 strcasecmp(const char *, const char *);
 char	*strcat(char * __restrict, const char * __restrict);
 char	*strchr(const char *, int);
 char	*strchrnul(const char *, int);
 int	 strcmp(const char *, const char *);
 char	*strcpy(char * __restrict, const char * __restrict);
 size_t	 strcspn(const char * __restrict, const char * __restrict) __pure;
 char	*strdup_flags(const char *__restrict, struct malloc_type *, int);
 char	*strdup(const char *__restrict, struct malloc_type *);
 char	*strncat(char *, const char *, size_t);
 char	*strndup(const char *__restrict, size_t, struct malloc_type *);
 size_t	 strlcat(char *, const char *, size_t);
 size_t	 strlcpy(char *, const char *, size_t);
 size_t	 strlen(const char *);
 int	 strncasecmp(const char *, const char *, size_t);
 int	 strncmp(const char *, const char *, size_t);
 char	*strncpy(char * __restrict, const char * __restrict, size_t);
 size_t	 strnlen(const char *, size_t);
 char	*strrchr(const char *, int);
 char	*strsep(char **, const char *delim);
 size_t	 strspn(const char *, const char *);
 char	*strstr(const char *, const char *);
 int	 strvalid(const char *, size_t);
 
 #ifdef KCSAN
 char	*kcsan_strcpy(char *, const char *);
 int	kcsan_strcmp(const char *, const char *);
 size_t	kcsan_strlen(const char *);
 #define	strcpy(d, s) kcsan_strcpy((d), (s))
 #define	strcmp(s1, s2) kcsan_strcmp((s1), (s2))
 #define	strlen(s) kcsan_strlen((s))
 #endif
 
 static __inline char *
 index(const char *p, int ch)
 {
 
 	return (strchr(p, ch));
 }
 
 static __inline char *
 rindex(const char *p, int ch)
 {
 
 	return (strrchr(p, ch));
 }
 
 /* fnmatch() return values. */
 #define	FNM_NOMATCH	1	/* Match failed. */
 
 /* fnmatch() flags. */
 #define	FNM_NOESCAPE	0x01	/* Disable backslash escaping. */
 #define	FNM_PATHNAME	0x02	/* Slash must be matched by slash. */
 #define	FNM_PERIOD	0x04	/* Period must be matched by period. */
 #define	FNM_LEADING_DIR	0x08	/* Ignore /<tail> after Imatch. */
 #define	FNM_CASEFOLD	0x10	/* Case insensitive search. */
 #define	FNM_IGNORECASE	FNM_CASEFOLD
 #define	FNM_FILE_NAME	FNM_PATHNAME
 
 #endif /* !_SYS_LIBKERN_H_ */