summaryrefslogtreecommitdiff
path: root/na
diff options
context:
space:
mode:
Diffstat (limited to 'na')
-rw-r--r--na/arch/riscv/include/arch/random.h27
-rw-r--r--na/arch/riscv/lib/random.cc21
-rw-r--r--na/include/lume/random.h26
-rw-r--r--na/lib/random.cc116
4 files changed, 190 insertions, 0 deletions
diff --git a/na/arch/riscv/include/arch/random.h b/na/arch/riscv/include/arch/random.h
new file mode 100644
index 0000000..8c8c051
--- /dev/null
+++ b/na/arch/riscv/include/arch/random.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Copyright (C) 2026 Richard Qin */
+
+#pragma once
+
+#include <lume/types.h>
+
+namespace arch {
+
+/*
+ * Get a random 64-bit value from the hardware TRNG.
+ * Returns true if supported and successful, false otherwise.
+ */
+bool get_hw_random(uint64& value);
+
+/*
+ * Get the current high-resolution hardware cycle counter.
+ * Used as an entropy fallback (Jitter/Timer).
+ */
+inline uint64 get_cycles()
+{
+ uint64 cycles;
+ __asm__ volatile("rdcycle %0" : "=r"(cycles));
+ return cycles;
+}
+
+} // namespace arch
diff --git a/na/arch/riscv/lib/random.cc b/na/arch/riscv/lib/random.cc
new file mode 100644
index 0000000..afc0078
--- /dev/null
+++ b/na/arch/riscv/lib/random.cc
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Copyright (C) 2026 Richard Qin */
+
+#include <arch/random.h>
+#include <lume/klog.h>
+
+namespace arch {
+
+bool get_hw_random(uint64& value)
+{
+ /*
+ * Real implementation would check for Zkr extension and execute:
+ * csrr x, seed
+ *
+ * Since QEMU virt doesn't guarantee Zkr, we return false to
+ * force the MI layer to use the timer fallback.
+ */
+ return false;
+}
+
+} // namespace arch
diff --git a/na/include/lume/random.h b/na/include/lume/random.h
new file mode 100644
index 0000000..0705b1b
--- /dev/null
+++ b/na/include/lume/random.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Copyright (C) 2026 Richard Qin */
+
+#pragma once
+
+#include <lume/types.h>
+
+/*
+ * MI: Machine-Independent Random Number Generator API
+ *
+ * Provides early PRNG and eventually a full CSPRNG.
+ */
+
+/*
+ * Initialize early PRNG with a seed (e.g., from FDT).
+ * If seed is 0, attempts to use hardware TRNG or falls back to timer jitter.
+ */
+void random_early_init(uint64 seed);
+
+/* Consume random bits */
+uint64 get_random_u64();
+uint32 get_random_u32();
+void get_random_bytes(void* buf, size_t len);
+
+/* Inject entropy (for future interrupt/device mixing) */
+void add_device_randomness(const void* buf, size_t len);
diff --git a/na/lib/random.cc b/na/lib/random.cc
new file mode 100644
index 0000000..c607e5f
--- /dev/null
+++ b/na/lib/random.cc
@@ -0,0 +1,116 @@
+/* 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 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;
+
+ /* 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;
+ } else {
+ /* Fallback: use boot cycles mixed with a constant */
+ seed = arch::get_cycles() ^ 0xdeadbeefbadc0ffeULL;
+ }
+ }
+
+ 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_initialized = true;
+ kprintf("[random] early PRNG initialized, seed=0x%llx\n", seed);
+}
+
+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();
+}