Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions riscv/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

### Added

- Add `seed` CSR
- Add new feature `defmt` which will optionally derive `defmt::Format` on certain types.
- Add `stimecmp`/`stimecmph` CSRs
- Add `tselect`, `tdata2` and `tdata3` Sdtrig CSRs as raw `read_write_csr_as_usize!` accessors, and `tdata1` and `tcontrol` as typed CSRs with field accessors.
Expand Down
3 changes: 3 additions & 0 deletions riscv/src/register.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,9 @@ pub mod stimecmph;
// Virtual supervisor indirect access
pub mod vsiselect;

// Entropy source
pub mod seed;

#[cfg(test)]
mod tests;

Expand Down
137 changes: 137 additions & 0 deletions riscv/src/register/seed.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
//! `seed` register — Entropy Source (0x015)
//!
//! This CSR is part of the RISC-V Scalar Cryptography & Entropy Source extension.
//! It provides an interface to an NIST SP 800-90B or BSI AIS-31 compliant physical entropy source,
//! offering 16 bits of entropy at a time.
//!
//! Access from outside of M-mode is controlled by the `mseccfg` CSR.

csr! {
/// Entropy Source Register
Seed,
0xC000_FFFF
}

/// Reads the CSR.
///
/// **WARNING**: panics on non-`riscv` targets.
#[inline]
pub fn read() -> Seed {
try_read().unwrap()
}

/// Attempts to reads the CSR.
#[inline]
pub fn try_read() -> crate::result::Result<Seed> {
// `seed` can't actually be written to, but read-only CSR instructions will fault
// because the write is required to signal polling and flushing of the entropy source.
// For this reason, we need to manually implement reads rather than using the read_only_csr! macro.
match () {
#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
() => {
let r: usize;
unsafe {
core::arch::asm!(concat!("csrrw {0}, 0x015, x0"), out(reg) r);
}
Ok(Seed::from_bits(r))
}
#[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))]
() => Err(crate::result::Error::Unimplemented),
}
}

/// Entropy source operational state.
#[repr(usize)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Opst {
/// Built-in self-test in progress; no entropy available yet.
Bist = 0b00,
/// Waiting for entropy
Wait = 0b01,
/// 16 bits of entropy are available from an NIST SP 800-90B or BSI AIS-31 compliant entropy source (bits 0..15).
Es16(u16) = 0b10,
/// Unrecoverable self-test error. The entropy source is dead.
Dead = 0b11,
}

// Because the lower 16 bits are only meaningful if opst is Es16, we manually implement the enum instead of
// using csr_field_enum! to prevent people from accidentally using insecure entropy bits.
impl Opst {
/// Attempts to convert a [`usize`] into a valid variant.
pub const fn from_usize(val: usize) -> crate::result::Result<Self> {
match (val & Seed::OPST_MASK) >> Seed::OPST_SHIFT {
0b00 => Ok(Self::Bist),
0b01 => Ok(Self::Wait),
0b10 => Ok(Self::Es16(val as u16)),
0b11 => Ok(Self::Dead),
_ => Err(crate::result::Error::InvalidVariant(val)),
}
}
/// Converts the variant into a [`usize`].
pub const fn into_usize(self) -> usize {
match self {
Opst::Bist => 0b00,
Opst::Wait => 0b01,
Opst::Es16(_) => 0b10,
Opst::Dead => 0b11,
}
}
}

impl From<Opst> for usize {
fn from(val: Opst) -> Self {
val.into_usize()
}
}
impl TryFrom<usize> for Opst {
type Error = crate::result::Error;
fn try_from(val: usize) -> crate::result::Result<Self> {
Self::from_usize(val)
}
}

impl Seed {
pub const OPST_SHIFT: usize = 30;
pub const OPST_WIDTH: usize = 2;
pub const OPST_MASK: usize = 0xC000_0000;

pub const ENTROPY_SHIFT: usize = 0;
pub const ENTROPY_WIDTH: usize = 0x10;
pub const ENTROPY_MASK: usize = 0xFFFF;

/// Operational state of the entropy source (bits 30..31).
#[inline]
pub fn opst(&self) -> Opst {
self.try_opst().unwrap()
}
/// Operational state of the entropy source (bits 30..31).
#[inline]
pub const fn try_opst(&self) -> crate::result::Result<Opst> {
Opst::from_usize(self.bits)
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_seed() {
let seed = Seed::from_bits((0b00 << 30) | 0x1234);
assert_eq!(seed.opst(), Opst::Bist);

let seed = Seed::from_bits((0b01 << 30) | 0x1234);
assert_eq!(seed.opst(), Opst::Wait);
let seed = Seed::from_bits((0b10 << 30) | 0x1234);
assert_eq!(seed.opst(), Opst::Es16(0x1234));

let seed = Seed::from_bits((0b11 << 30) | 0x1234);
assert_eq!(seed.opst(), Opst::Dead);
}

#[test]
fn test_seed_bitmask() {
let seed = Seed::from_bits(usize::MAX);
assert_eq!(seed.bits(), 0xC000_FFFFusize);
}
}
Loading