random + rdrand
This commit is contained in:
@@ -15,6 +15,7 @@ kernel_sources += files(
|
||||
'percpu.c',
|
||||
'pmap.c',
|
||||
'pmap.c',
|
||||
'random.c',
|
||||
'sched.c',
|
||||
'serial.c',
|
||||
'smp.c',
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
#include <zinnia/initgraph.h>
|
||||
#include <zinnia/print.h>
|
||||
#include <zinnia/random.h>
|
||||
#include <x86_64/asm.h>
|
||||
|
||||
#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);
|
||||
@@ -8,6 +8,7 @@ kernel_sources += files(
|
||||
'panic.c',
|
||||
'percpu.c',
|
||||
'print.c',
|
||||
'random.c',
|
||||
'spin.c',
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
#define pr_fmt(fmt) "random: " fmt
|
||||
|
||||
#include <zinnia/atomic.h>
|
||||
#include <zinnia/clock.h>
|
||||
#include <zinnia/print.h>
|
||||
#include <zinnia/random.h>
|
||||
#include <zinnia/spin.h>
|
||||
#include <string.h>
|
||||
|
||||
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");
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <zinnia/initgraph.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
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);
|
||||
Reference in New Issue
Block a user