ostd 0.18.0

Rust OS framework that facilitates the development of and innovation in OS kernels
// SPDX-License-Identifier: MPL-2.0

//! Platform-specific code for the RISC-V platform.

#![expect(dead_code)]

pub mod boot;
pub mod cpu;
pub mod device;
pub(crate) mod io;
pub(crate) mod iommu;
pub mod irq;
pub(crate) mod mm;
mod power;
pub(crate) mod serial;
pub(crate) mod task;
mod timer;
pub mod trap;

#[cfg(feature = "cvm_guest")]
pub(crate) fn init_cvm_guest() {
    // Unimplemented, no-op
}

/// Architecture-specific initialization on the bootstrapping processor.
///
/// It should be called when the heap and frame allocators are available.
///
/// # Safety
///
/// 1. This function must be called only once in the boot context of the
///    bootstrapping processor.
/// 2. This function must be called after the kernel page table is activated on
///    the bootstrapping processor.
pub(crate) unsafe fn late_init_on_bsp() {
    // SAFETY: This is only called once on this BSP in the boot context.
    unsafe { trap::init_on_cpu() };

    // SAFETY: The caller ensures that this function is only called once on BSP,
    // after the kernel page table is activated.
    let io_mem_builder = unsafe { io::construct_io_mem_allocator_builder() };

    // SAFETY: This function is called once and at most once at a proper timing
    // in the boot context of the BSP, with no external interrupt-related
    // operations having been performed.
    unsafe { irq::chip::init_on_bsp(&io_mem_builder) };

    // SAFETY: This is called on the BSP and before any IPI-related operation is
    // performed.
    unsafe { irq::ipi::init_on_bsp() };

    // SAFETY: This function is called once and at most once at a proper timing
    // in the boot context of the BSP, with no timer-related operations having
    // been performed.
    unsafe { timer::init_on_bsp() };

    // SAFETY: We're on the BSP and we're ready to boot all APs.
    unsafe { crate::boot::smp::boot_all_aps() };

    // SAFETY:
    // 1. All the system device memory have been removed from the builder.
    // 2. RISC-V platforms do not have port I/O.
    unsafe { crate::io::init(io_mem_builder) };

    power::init();
}

/// Initializes application-processor-specific state.
///
/// On RISC-V, Application Processors (APs) are harts that are not the
/// bootstrapping hart.
///
/// # Safety
///
/// 1. This function must be called only once on each application processor.
/// 2. This function must be called after the BSP's call to [`late_init_on_bsp`]
///    and before any other architecture-specific code in this module is called
///    on this AP.
pub(crate) unsafe fn init_on_ap() {
    // SAFETY: The safety is upheld by the caller.
    unsafe { trap::init_on_cpu() };

    // SAFETY: The safety is upheld by the caller.
    unsafe { irq::chip::init_on_ap() };

    // SAFETY: This is called before any harts can send IPIs to this AP.
    unsafe { irq::ipi::init_on_ap() };

    // SAFETY: The caller ensures that this is only called once on this AP.
    unsafe { timer::init_on_ap() };
}

/// Returns the frequency of TSC. The unit is Hz.
pub fn tsc_freq() -> u64 {
    timer::get_timebase_freq()
}

/// Reads the current value of the processor's time-stamp counter (TSC).
pub fn read_tsc() -> u64 {
    riscv::register::time::read64()
}

/// Reads a hardware generated 64-bit random value.
///
/// Returns `None` if no random value was generated.
///
/// This implementation uses the RISC-V Entropy Source Extension (Zkr) which
/// provides access to a physical entropy source via the `seed` CSR.
///
/// Reference:
/// - "RISC-V Cryptography Extensions Volume I: Scalar & Entropy Source Instructions"
///   - <https://docs.riscv.org/reference/isa/extensions/crypto-scalar/_attachments/riscv-crypto-spec-scalar.pdf>
///   - Section 2.9: Zkr - Entropy Source Extension
///   - Section 4.1: The `seed` CSR
pub fn read_random() -> Option<u64> {
    use cpu::extension::{IsaExtensions, has_extensions};

    // Check if the Zkr (Entropy Source) extension is available.
    if !has_extensions(IsaExtensions::ZKR) {
        return None;
    }

    const RETRY_LIMIT: usize = 10;

    const OPST_SHIFT: usize = 30;
    const OPST_MASK: usize = 0b11 << OPST_SHIFT;
    const ENTROPY_MASK: usize = 0xFFFF;

    const OPST_ES16: usize = 0b10; // Status indicating entropy is available
    const OPST_WAIT: usize = 0b01; // Status indicating entropy source is busy (waiting)
    const OPST_BIST: usize = 0b00; // Status indicating entropy source is in self-test
    const OPST_DEAD: usize = 0b11; // Status indicating entropy source has failed

    // Read 16-bit entropy from hardware.
    let read_entropy = || -> Option<u16> {
        for _ in 0..RETRY_LIMIT {
            let seed_val: usize;
            // SAFETY: We've checked that the Zkr extension is available.
            // Therefore, this is a valid instruction to access the `seed` CSR.
            unsafe {
                // The Zkr spec requires using `csrrw` to access `seed` CSR,
                // which signals polling and flushing. See the spec for more
                // information.
                core::arch::asm!(
                    "csrrw {0}, seed, zero",
                    out(reg) seed_val,
                    options(nomem, nostack)
                );
            }

            // Extract status bits (bits 31:30).
            let status = (seed_val & OPST_MASK) >> OPST_SHIFT;

            if status == OPST_ES16 {
                // Entropy is available: extract 16 bits (bits 15:0).
                return Some((seed_val & ENTROPY_MASK) as u16);
            } else if status == OPST_DEAD {
                // Entropy source failed permanently.
                return None;
            } else if status == OPST_WAIT || status == OPST_BIST {
                // Entropy source busy or running self-test: retry.
                core::hint::spin_loop();
            }
        }
        None
    };

    // Collect 4 chunks of 16-bit entropy to form a 64-bit random value.
    let result = (read_entropy()? as u64)
        | ((read_entropy()? as u64) << 16)
        | ((read_entropy()? as u64) << 32)
        | ((read_entropy()? as u64) << 48);

    Some(result)
}

pub(crate) fn enable_cpu_features() {
    cpu::extension::init();
}