1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
/* Copyright (C) 2026 Richard Qin */
#include <lume/random.h>
#include <arch/random.h>
#include <lume/klog.h>
#include <lume/kprintf.h>
/*
* Early Boot PRNG (xoshiro256**)
* State size: 256 bits (4 * 64-bit words)
* Extremely fast, excellent statistical properties.
*/
static uint64 g_state[4];
static bool g_initialized = false;
static uint64 g_init_seed = 0;
static RandomSeedSource g_seed_source = RandomSeedSource::Unknown;
static inline uint64 rotl(const uint64 x, int k) {
return (x << k) | (x >> (64 - k));
}
/* SplitMix64 used to initialize xoshiro state from a single 64-bit seed */
static uint64 splitmix64(uint64& state) {
uint64 z = (state += 0x9e3779b97f4a7c15ULL);
z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL;
z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL;
return z ^ (z >> 31);
}
void random_early_init(uint64 seed)
{
if (g_initialized) return;
RandomSeedSource source = RandomSeedSource::Unknown;
/* If seed is 0 (not provided by bootloader), try hardware */
if (seed == 0) {
uint64 hw_val;
if (arch::get_hw_random(hw_val)) {
seed = hw_val;
source = RandomSeedSource::HwRandom;
} else {
/* Fallback: use boot cycles mixed with a constant */
seed = arch::get_cycles() ^ 0xdeadbeefbadc0ffeULL;
source = RandomSeedSource::CycleFallback;
}
} else {
source = RandomSeedSource::FdtProvided;
}
uint64 sm_state = seed;
g_state[0] = splitmix64(sm_state);
g_state[1] = splitmix64(sm_state);
g_state[2] = splitmix64(sm_state);
g_state[3] = splitmix64(sm_state);
g_init_seed = seed;
g_seed_source = source;
g_initialized = true;
kprintf("[random] early PRNG initialized, seed=0x%llx, source=%s\n",
seed, random_seed_source_name(source));
}
uint64 get_random_u64()
{
if (!g_initialized) {
random_early_init(0);
}
const uint64 result = rotl(g_state[1] * 5, 7) * 9;
const uint64 t = g_state[1] << 17;
g_state[2] ^= g_state[0];
g_state[3] ^= g_state[1];
g_state[1] ^= g_state[2];
g_state[0] ^= g_state[3];
g_state[2] ^= t;
g_state[3] = rotl(g_state[3], 45);
/* Mix with current cycles for slight additional unpredictability */
return result ^ arch::get_cycles();
}
uint32 get_random_u32()
{
return static_cast<uint32>(get_random_u64() >> 32);
}
void get_random_bytes(void* buf, size_t len)
{
uint8* p = static_cast<uint8*>(buf);
while (len >= 8) {
uint64 val = get_random_u64();
for (int i = 0; i < 8; i++) {
p[i] = (val >> (i * 8)) & 0xFF;
}
p += 8;
len -= 8;
}
if (len > 0) {
uint64 val = get_random_u64();
for (size_t i = 0; i < len; i++) {
p[i] = (val >> (i * 8)) & 0xFF;
}
}
}
void add_device_randomness(const void* buf, size_t len)
{
/*
* For now, just mix into the xoshiro state using a simple XOR.
* In the future, this will feed the CSPRNG entropy pool.
*/
const uint8* p = static_cast<const uint8*>(buf);
uint64 mix = arch::get_cycles();
for (size_t i = 0; i < len; i++) {
mix = (mix << 5) ^ (mix >> 59) ^ p[i];
}
g_state[0] ^= mix;
g_state[1] ^= arch::get_cycles();
}
RandomSeedSource random_early_seed_source()
{
return g_seed_source;
}
uint64 random_early_seed_value()
{
return g_init_seed;
}
const char* random_seed_source_name(RandomSeedSource source)
{
switch (source) {
case RandomSeedSource::FdtProvided:
return "fdt";
case RandomSeedSource::HwRandom:
return "hw";
case RandomSeedSource::CycleFallback:
return "cycle";
case RandomSeedSource::Unknown:
default:
return "unknown";
}
}
|