diff --git a/arch/x86_64/meson.build b/arch/x86_64/meson.build index b5dd629..b73eb4f 100644 --- a/arch/x86_64/meson.build +++ b/arch/x86_64/meson.build @@ -15,6 +15,7 @@ kernel_sources += files( 'percpu.c', 'pmap.c', 'pmap.c', + 'random.c', 'sched.c', 'serial.c', 'smp.c', diff --git a/arch/x86_64/random.c b/arch/x86_64/random.c new file mode 100644 index 0000000..ec38b9c --- /dev/null +++ b/arch/x86_64/random.c @@ -0,0 +1,36 @@ +#include +#include +#include +#include + +#define CPUID_1C_RDRAND (1u << 30) +#define RDRAND_RETRIES 10 + +static bool rdrand_get_u64(struct random_source*, uint64_t* out) { + for (int i = 0; i < RDRAND_RETRIES; i++) { + uint64_t value; + uint8_t ok; + asm volatile("rdrand %0\n setc %1" : "=r"(value), "=qm"(ok) : : "cc"); + if (ok) { + *out = value; + return true; + } + } + return false; +} + +static struct random_source rdrand_source = { + .name = "x86_64.rdrand", + .quality = 100, + .get_u64 = rdrand_get_u64, +}; + +static void rdrand_init(struct boot_info*) { + if (asm_cpuid(1, 0).ecx & CPUID_1C_RDRAND) { + random_register(&rdrand_source); + } else { + log_warn("random: This CPU doesn't support RDRAND, ignoring\n"); + } +} +INIT_TASK(rdrand_stage, "arch.x86_64.rdrand", rdrand_init); +INIT_ENTAILS(rdrand_stage, random_stage); diff --git a/core/meson.build b/core/meson.build index 853f9e3..41f7281 100644 --- a/core/meson.build +++ b/core/meson.build @@ -8,6 +8,7 @@ kernel_sources += files( 'panic.c', 'percpu.c', 'print.c', + 'random.c', 'spin.c', ) diff --git a/core/random.c b/core/random.c new file mode 100644 index 0000000..467c898 --- /dev/null +++ b/core/random.c @@ -0,0 +1,61 @@ +#define pr_fmt(fmt) "random: " fmt + +#include +#include +#include +#include +#include +#include + +static _Atomic(uint64_t) state = 0x9E37'79B9'7F4A'7C15; + +static bool dumb_random(struct random_source*, uint64_t* out) { + uint64_t z = atomic_fetch_add(&state, 0x9E37'79B9'7F4A'7C15, ATOMIC_RELAXED); + z ^= clock_get_elapsed_ns() * 0x2545'F491'4F6C'DD1D; + z = (z ^ (z >> 30)) * 0xBF58'476D'1CE4'E5B9; + z = (z ^ (z >> 27)) * 0x94D0'49BB'1331'11EB; + *out = z ^ (z >> 31); + return true; +} + +static struct random_source fallback_source = { + .name = "fallback", + .quality = 0, + .get_u64 = dumb_random, +}; + +static _Atomic(struct random_source*) active_source = &fallback_source; +static struct spinlock lock = {}; + +bool random_register(struct random_source* source) { + spin_lock(&lock); + + struct random_source* active = atomic_load(&active_source, ATOMIC_RELAXED); + if (source->quality <= active->quality) { + spin_unlock(&lock); + return false; + } + atomic_store(&active_source, source, ATOMIC_RELEASE); + + spin_unlock(&lock); + + log("Switching to random source \"%s\"\n", source->name); + + return true; +} + +void random_fill(void* buf, size_t len) { + struct random_source* source = atomic_load(&active_source, ATOMIC_ACQUIRE); + uint8_t* out = buf; + while (len) { + uint64_t value; + source->get_u64(source, &value); + + const size_t chunk = len < sizeof(value) ? len : sizeof(value); + memcpy(out, &value, chunk); + out += chunk; + len -= chunk; + } +} + +INIT_TASK(random_stage, "core.random"); diff --git a/include/zinnia/random.h b/include/zinnia/random.h new file mode 100644 index 0000000..07f4029 --- /dev/null +++ b/include/zinnia/random.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include +#include + +struct random_source { + const char* name; + uint8_t quality; + + bool (*get_u64)(struct random_source* source, uint64_t* out); +}; + +// Registers a source of randomness. Returns true if it is now the active source. +bool random_register(struct random_source* source); + +void random_fill(void* buf, size_t len); + +INIT_EXTERN(random_stage);