Kernel/riscv64: Use the Zkr extension for entropy, if supported

This commit is contained in:
Sönke Holz authored and Sönke Holz committed 2026-04-12 14:07:26 +02:00
1 parent 3e0ef9397a
commit 048ba5b4d0
5 files changed
+110 -7

No files matched your search

+34
View File
@@ -28,6 +28,9 @@ enum class Address : u16 {
VTYPE = 0xc21,
VLENB = 0xc22,
// Unprivileged Entropy Source Extension CSR
SEED = 0x015,
// Unprivileged Counters/Timers
CYCLE = 0xc00,
TIME = 0xc01,
@@ -235,6 +238,37 @@ struct SSTATUS {
};
static_assert(AssertSize<SSTATUS, 8>());
// https://docs.riscv.org/reference/isa/unpriv/scalar-crypto.html#crypto_scalar_es
struct SEED {
enum class Status : u64 {
BIST = 0b00,
WAIT = 0b01,
ES16 = 0b10,
DEAD = 0b11,
};
u64 entropy : 16;
u64 : 8;
u64 : 6;
Status OPST : 2;
u64 : 32;
static ALWAYS_INLINE SEED read()
{
// "Attempts to access the seed CSR using a read-only CSR-access instruction (CSRRS/CSRRC with rs1=x0 or CSRRSI/CSRRCI with uimm=0)
// raise an illegal-instruction exception; any other CSR-access instruction may be used to access seed.
// The write value (in rs1 or uimm) must be ignored by implementations. The purpose of the write is to signal polling and flushing.
//
// Software normally uses the instruction csrrw rd, seed, x0 to read the seed CSR."
FlatPtr value { 0 };
asm volatile("csrrw %0, seed, x0" : "=r"(value));
return bit_cast<SEED>(value);
}
};
static_assert(AssertSize<SEED, 8>());
// 4.1.8 Supervisor Cause Register (scause)
constexpr u64 SCAUSE_INTERRUPT_MASK = 1LU << 63;
+30
View File
@@ -0,0 +1,30 @@
/*
* Copyright (c) 2026, Sönke Holz <[email protected]>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <Kernel/Arch/Processor.h>
#include <Kernel/Arch/riscv64/Entropy.h>
namespace Kernel::RISCV64 {
// https://docs.riscv.org/reference/isa/unpriv/scalar-crypto.html#crypto_scalar_es
ErrorOr<u16> poll_seed_csr_for_entropy()
{
VERIFY(Processor::current().has_feature(CPUFeature::Zkr));
for (;;) {
auto seed_csr_value = RISCV64::CSR::SEED::read();
if (seed_csr_value.OPST == RISCV64::CSR::SEED::Status::DEAD) {
dmesgln("RISC-V seed CSR reported an unrecoverable self-test error!");
return EIO;
}
if (seed_csr_value.OPST == RISCV64::CSR::SEED::Status::ES16)
return static_cast<u16>(seed_csr_value.entropy);
Processor::wait_check();
}
}
}
+16
View File
@@ -0,0 +1,16 @@
/*
* Copyright (c) 2026, Sönke Holz <[email protected]>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Forward.h>
#include <AK/Types.h>
namespace Kernel::RISCV64 {
ErrorOr<u16> poll_seed_csr_for_entropy();
}
+1
View File
@@ -580,6 +580,7 @@ elseif("${SERENITY_ARCH}" STREQUAL "riscv64")
Arch/riscv64/CurrentTime.cpp
Arch/riscv64/DebugOutput.cpp
Arch/riscv64/Delay.cpp
Arch/riscv64/Entropy.cpp
Arch/riscv64/InterruptManagement.cpp
Arch/riscv64/Interrupts.cpp
Arch/riscv64/Interrupts/PLIC.cpp
+29 -7
View File
@@ -12,6 +12,8 @@
# include <Kernel/Arch/x86_64/Time/RTC.h>
#elif ARCH(AARCH64)
# include <Kernel/Arch/aarch64/ASM_wrapper.h>
#elif ARCH(RISCV64)
# include <Kernel/Arch/riscv64/Entropy.h>
#endif
#include <Kernel/Devices/Generic/RandomDevice.h>
#include <Kernel/Sections.h>
@@ -78,13 +80,33 @@ UNMAP_AFTER_INIT KernelRng::KernelRng()
}
}
#elif ARCH(RISCV64)
// Fallback to TimeManagement as entropy
dmesgln("KernelRng: Using bad entropy source TimeManagement");
auto current_time = static_cast<u64>(TimeManagement::now().milliseconds_since_epoch());
for (size_t i = 0; i < pool_count * reseed_threshold; ++i) {
add_random_event(current_time, i % 32);
current_time *= 0x574au;
current_time += 0x40b2u;
auto try_zkr_entropy = [this] -> bool {
if (!Processor::current().has_feature(CPUFeature::Zkr))
return false;
dmesgln("KernelRng: Using Zkr extension as entropy source");
for (size_t i = 0; i < pool_count * reseed_threshold; ++i) {
auto entropy_or_error = RISCV64::poll_seed_csr_for_entropy();
if (entropy_or_error.is_error())
return false;
add_random_event(entropy_or_error.value(), i % 32);
}
return true;
};
if (!try_zkr_entropy()) {
// Fallback to TimeManagement as entropy
dmesgln("KernelRng: Using bad entropy source TimeManagement");
auto current_time = static_cast<u64>(TimeManagement::now().milliseconds_since_epoch());
for (size_t i = 0; i < pool_count * reseed_threshold; ++i) {
add_random_event(current_time, i % 32);
current_time *= 0x574au;
current_time += 0x40b2u;
}
}
#else
dmesgln("KernelRng: No entropy source available!");