1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright 2020 Conrad Meyer <cem@FreeBSD.org>.  All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#include <sys/param.h>
29#include <sys/kernel.h>
30#include <sys/pcpu.h>
31#include <sys/prng.h>
32#include <sys/smp.h>
33#include <sys/systm.h>
34
35#if !PCG_HAS_128BIT_OPS
36/* On 32-bit platforms, gang together two 32-bit generators. */
37typedef struct {
38	pcg32u_random_t states[2];
39} pcg64u_random_t;
40
41static inline void
42pcg64u_srandom_r(pcg64u_random_t *state64, uint64_t seed)
43{
44	pcg32u_srandom_r(&state64->states[0], seed);
45	pcg32u_srandom_r(&state64->states[1], seed);
46}
47
48static inline uint64_t
49pcg64u_random_r(pcg64u_random_t *state64)
50{
51	return ((((uint64_t)pcg32u_random_r(&state64->states[0])) << 32) |
52	    pcg32u_random_r(&state64->states[1]));
53}
54
55static inline uint64_t
56pcg64u_boundedrand_r(pcg64u_random_t *state64, uint64_t bound)
57{
58	uint64_t threshold = -bound % bound;
59	for (;;) {
60		uint64_t r = pcg64u_random_r(state64);
61		if (r >= threshold)
62			return (r % bound);
63	}
64}
65#endif
66
67DPCPU_DEFINE_STATIC(pcg32u_random_t, pcpu_prng32_state);
68DPCPU_DEFINE_STATIC(pcg64u_random_t, pcpu_prng64_state);
69
70static void
71prng_init(void *dummy __unused)
72{
73	pcg32u_random_t *state;
74	pcg64u_random_t *state64;
75	int i;
76
77	CPU_FOREACH(i) {
78		state = DPCPU_ID_PTR(i, pcpu_prng32_state);
79		pcg32u_srandom_r(state, 1);
80		state64 = DPCPU_ID_PTR(i, pcpu_prng64_state);
81		pcg64u_srandom_r(state64, 1);
82	}
83}
84SYSINIT(prng_init, SI_SUB_CPU, SI_ORDER_ANY, prng_init, NULL);
85
86uint32_t
87prng32(void)
88{
89	uint32_t r;
90
91	critical_enter();
92	r = pcg32u_random_r(DPCPU_PTR(pcpu_prng32_state));
93	critical_exit();
94	return (r);
95}
96
97uint32_t
98prng32_bounded(uint32_t bound)
99{
100	uint32_t r;
101
102	critical_enter();
103	r = pcg32u_boundedrand_r(DPCPU_PTR(pcpu_prng32_state), bound);
104	critical_exit();
105	return (r);
106}
107
108uint64_t
109prng64(void)
110{
111	uint64_t r;
112
113	critical_enter();
114	r = pcg64u_random_r(DPCPU_PTR(pcpu_prng64_state));
115	critical_exit();
116	return (r);
117}
118
119uint64_t
120prng64_bounded(uint64_t bound)
121{
122	uint64_t r;
123
124	critical_enter();
125	r = pcg64u_boundedrand_r(DPCPU_PTR(pcpu_prng64_state), bound);
126	critical_exit();
127	return (r);
128}
129