Browse Source

feat(prototyper): add PMU extension and implement `sbi_pmu_num_counters`

Signed-off-by: guttatus <[email protected]>
guttatus 1 month ago
parent
commit
bdcb063ba8

+ 1 - 0
prototyper/prototyper/Cargo.toml

@@ -25,6 +25,7 @@ uart_xilinx = { git = "https://github.com/duskmoon314/uart-rs/" }
 xuantie-riscv = { git= "https://github.com/rustsbi/xuantie" }
 bouffalo-hal = { git = "https://github.com/rustsbi/bouffalo-hal", rev = "968b949", features = ["bl808"] }
 static-toml = "1"
+seq-macro = "0.3.5"
 
 [[bin]]
 name = "rustsbi-prototyper"

+ 2 - 2
prototyper/prototyper/src/macros.rs

@@ -28,13 +28,13 @@ macro_rules! has_csr {
     ($($x: expr)*) => {{
             use core::arch::asm;
             use riscv::register::mtvec;
-            use crate::sbi::early_trap::expected_trap;
+            use crate::sbi::early_trap::light_expected_trap;
             let res: usize;
             unsafe {
                 // Backup old mtvec
                 let mtvec = mtvec::read().bits();
                 // Write expected_trap
-                mtvec::write(expected_trap as _, mtvec::TrapMode::Direct);
+                mtvec::write(light_expected_trap as _, mtvec::TrapMode::Direct);
                 asm!("addi a0, zero, 0",
                     "addi a1, zero, 0",
                     "csrr a2, {}",

+ 14 - 7
prototyper/prototyper/src/main.rs

@@ -21,12 +21,14 @@ mod sbi;
 
 use core::arch::{asm, naked_asm};
 
+use sbi::features::hart_mhpm_mask;
+
 use crate::platform::PLATFORM;
 use crate::riscv::csr::menvcfg;
 use crate::riscv::current_hartid;
-use crate::sbi::extensions::{
-    Extension, PrivilegedVersion, hart_extension_probe, hart_privileged_version,
-    privileged_version_detection,
+use crate::sbi::features::{
+    Extension, PrivilegedVersion, hart_extension_probe, hart_features_detection,
+    hart_privileged_version,
 };
 use crate::sbi::hart_context::NextStage;
 use crate::sbi::heap::sbi_heap_init;
@@ -66,10 +68,15 @@ extern "C" fn rust_main(_hart_id: usize, opaque: usize, nonstandard_a2: usize) {
         let hart_id = current_hartid();
         info!("{:<30}: {}", "Boot HART ID", hart_id);
 
-        // Detection Priv Version
-        privileged_version_detection();
+        // Detection Hart Features
+        hart_features_detection();
         let priv_version = hart_privileged_version(hart_id);
-        info!("{:<30}: {:?}", "Boot HART Privileged Version", priv_version);
+        let mhpm_mask = hart_mhpm_mask(hart_id);
+        info!(
+            "{:<30}: {:?}",
+            "Boot HART Privileged Version:", priv_version
+        );
+        info!("{:<30}: 0x{:08x}", "Boot HART MHPM Mask:", mhpm_mask);
 
         // Start kernel.
         local_remote_hsm().start(NextStage {
@@ -95,7 +102,7 @@ extern "C" fn rust_main(_hart_id: usize, opaque: usize, nonstandard_a2: usize) {
 
         firmware::set_pmp(unsafe { PLATFORM.info.memory_range.as_ref().unwrap() });
         // Detection Priv Version
-        privileged_version_detection();
+        hart_features_detection();
     }
     // Clear all pending IPIs.
     ipi::clear_all();

+ 2 - 2
prototyper/prototyper/src/platform/mod.rs

@@ -22,7 +22,7 @@ use crate::platform::console::{
 use crate::platform::reset::SIFIVETEST_COMPATIBLE;
 use crate::sbi::SBI;
 use crate::sbi::console::SbiConsole;
-use crate::sbi::extensions;
+use crate::sbi::features::extension_detection;
 use crate::sbi::hsm::SbiHsm;
 use crate::sbi::ipi::SbiIpi;
 use crate::sbi::logger;
@@ -146,7 +146,7 @@ impl Platform {
         }
 
         // TODO: Need a better extension initialization method
-        extensions::init(&tree.cpus.cpu);
+        extension_detection(&tree.cpus.cpu);
 
         // Find which hart is enabled by fdt
         let mut cpu_list: CpuEnableList = [false; NUM_HART_MAX];

+ 66 - 0
prototyper/prototyper/src/riscv/csr.rs

@@ -8,6 +8,72 @@ pub const CSR_TIME: u32 = 0xc01;
 pub const CSR_TIMEH: u32 = 0xc81;
 /// Supervisor timer compare value.
 pub const CSR_STIMECMP: u32 = 0x14D;
+pub const CSR_MCOUNTEREN: u32 = 0x306;
+pub const CSR_MCOUNTINHIBIT: u32 = 0x320;
+pub const CSR_MENVCFG: u32 = 0x30a;
+/* Machine Counters/Timers */
+pub const CSR_MCYCLE: u32 = 0xb00;
+pub const CSR_MINSTRET: u32 = 0xb02;
+pub const CSR_MHPMCOUNTER3: u32 = 0xb03;
+pub const CSR_MHPMCOUNTER4: u32 = 0xb04;
+pub const CSR_MHPMCOUNTER5: u32 = 0xb05;
+pub const CSR_MHPMCOUNTER6: u32 = 0xb06;
+pub const CSR_MHPMCOUNTER7: u32 = 0xb07;
+pub const CSR_MHPMCOUNTER8: u32 = 0xb08;
+pub const CSR_MHPMCOUNTER9: u32 = 0xb09;
+pub const CSR_MHPMCOUNTER10: u32 = 0xb0a;
+pub const CSR_MHPMCOUNTER11: u32 = 0xb0b;
+pub const CSR_MHPMCOUNTER12: u32 = 0xb0c;
+pub const CSR_MHPMCOUNTER13: u32 = 0xb0d;
+pub const CSR_MHPMCOUNTER14: u32 = 0xb0e;
+pub const CSR_MHPMCOUNTER15: u32 = 0xb0f;
+pub const CSR_MHPMCOUNTER16: u32 = 0xb10;
+pub const CSR_MHPMCOUNTER17: u32 = 0xb11;
+pub const CSR_MHPMCOUNTER18: u32 = 0xb12;
+pub const CSR_MHPMCOUNTER19: u32 = 0xb13;
+pub const CSR_MHPMCOUNTER20: u32 = 0xb14;
+pub const CSR_MHPMCOUNTER21: u32 = 0xb15;
+pub const CSR_MHPMCOUNTER22: u32 = 0xb16;
+pub const CSR_MHPMCOUNTER23: u32 = 0xb17;
+pub const CSR_MHPMCOUNTER24: u32 = 0xb18;
+pub const CSR_MHPMCOUNTER25: u32 = 0xb19;
+pub const CSR_MHPMCOUNTER26: u32 = 0xb1a;
+pub const CSR_MHPMCOUNTER27: u32 = 0xb1b;
+pub const CSR_MHPMCOUNTER28: u32 = 0xb1c;
+pub const CSR_MHPMCOUNTER29: u32 = 0xb1d;
+pub const CSR_MHPMCOUNTER30: u32 = 0xb1e;
+pub const CSR_MHPMCOUNTER31: u32 = 0xb1f;
+pub const CSR_MCYCLEH: u32 = 0xb80;
+pub const CSR_MINSTRETH: u32 = 0xb82;
+pub const CSR_MHPMCOUNTER3H: u32 = 0xb83;
+pub const CSR_MHPMCOUNTER4H: u32 = 0xb84;
+pub const CSR_MHPMCOUNTER5H: u32 = 0xb85;
+pub const CSR_MHPMCOUNTER6H: u32 = 0xb86;
+pub const CSR_MHPMCOUNTER7H: u32 = 0xb87;
+pub const CSR_MHPMCOUNTER8H: u32 = 0xb88;
+pub const CSR_MHPMCOUNTER9H: u32 = 0xb89;
+pub const CSR_MHPMCOUNTER10H: u32 = 0xb8a;
+pub const CSR_MHPMCOUNTER11H: u32 = 0xb8b;
+pub const CSR_MHPMCOUNTER12H: u32 = 0xb8c;
+pub const CSR_MHPMCOUNTER13H: u32 = 0xb8d;
+pub const CSR_MHPMCOUNTER14H: u32 = 0xb8e;
+pub const CSR_MHPMCOUNTER15H: u32 = 0xb8f;
+pub const CSR_MHPMCOUNTER16H: u32 = 0xb90;
+pub const CSR_MHPMCOUNTER17H: u32 = 0xb91;
+pub const CSR_MHPMCOUNTER18H: u32 = 0xb92;
+pub const CSR_MHPMCOUNTER19H: u32 = 0xb93;
+pub const CSR_MHPMCOUNTER20H: u32 = 0xb94;
+pub const CSR_MHPMCOUNTER21H: u32 = 0xb95;
+pub const CSR_MHPMCOUNTER22H: u32 = 0xb96;
+pub const CSR_MHPMCOUNTER23H: u32 = 0xb97;
+pub const CSR_MHPMCOUNTER24H: u32 = 0xb98;
+pub const CSR_MHPMCOUNTER25H: u32 = 0xb99;
+pub const CSR_MHPMCOUNTER26H: u32 = 0xb9a;
+pub const CSR_MHPMCOUNTER27H: u32 = 0xb9b;
+pub const CSR_MHPMCOUNTER28H: u32 = 0xb9c;
+pub const CSR_MHPMCOUNTER29H: u32 = 0xb9d;
+pub const CSR_MHPMCOUNTER30H: u32 = 0xb9e;
+pub const CSR_MHPMCOUNTER31H: u32 = 0xb9f;
 
 /// Machine environment configuration register (menvcfg) bit fields.
 pub mod menvcfg {

+ 100 - 1
prototyper/prototyper/src/sbi/early_trap.rs

@@ -1,4 +1,6 @@
+use core::arch::asm;
 use core::arch::naked_asm;
+use riscv::register::mtvec;
 
 /// When you expected some insts will cause trap, use this.
 /// If trap happened, a0 will set to 1, otherwise will be 0.
@@ -7,7 +9,7 @@ use core::arch::naked_asm;
 // TODO: Support save trap info.
 #[naked]
 #[repr(align(16))]
-pub(crate) unsafe extern "C" fn expected_trap() {
+pub(crate) unsafe extern "C" fn light_expected_trap() {
     unsafe {
         naked_asm!(
             "add a0, zero, zero",
@@ -20,3 +22,100 @@ pub(crate) unsafe extern "C" fn expected_trap() {
         )
     }
 }
+
+#[repr(C)]
+pub struct TrapInfo {
+    pub mepc: usize,
+    pub mcause: usize,
+    pub mtval: usize,
+}
+
+impl Default for TrapInfo {
+    fn default() -> Self {
+        Self {
+            mepc: 0,
+            mcause: 0,
+            mtval: 0,
+        }
+    }
+}
+
+#[naked]
+#[repr(align(16))]
+pub(crate) unsafe extern "C" fn expected_trap() {
+    unsafe {
+        naked_asm!(
+            "csrr a4, mepc",
+            "sd a4, 0*8(a3)",
+            "csrr a4, mcause",
+            "sd a4, 1*8(a3)",
+            "csrr a4, mtval",
+            "sd a4, 2*8(a3)",
+            "csrr a4, mepc",
+            "addi a4, a4, 4",
+            "csrw mepc, a4",
+            "mret",
+        )
+    }
+}
+
+pub(crate) unsafe fn csr_read_allow<const CSR_NUM: u32>(trap_info: *mut TrapInfo) -> usize {
+    let tinfo = trap_info as usize;
+    let mut ret: usize;
+    // Backup old mtvec
+    let mtvec = mtvec::read().bits();
+    unsafe {
+        core::ptr::write_volatile(&mut (*trap_info).mcause, usize::MAX);
+        // Write expected_trap
+        mtvec::write(expected_trap as _, mtvec::TrapMode::Direct);
+
+        asm!(
+            "add a3, {tinfo}, zero",
+            "add a4, {tinfo}, zero",
+            "csrr {ret}, {csr}",
+            tinfo = in(reg) tinfo,
+            ret = out(reg) ret,
+            csr = const CSR_NUM,
+            options(nostack, preserves_flags)
+        );
+        asm!("csrw mtvec, {}", in(reg) mtvec);
+    }
+    ret
+}
+
+pub(crate) unsafe fn csr_write_allow<const CSR_NUM: u32>(trap_info: *mut TrapInfo, value: usize) {
+    let tinfo = trap_info as usize;
+    // Backup old mtvec
+    let mtvec = mtvec::read().bits();
+    unsafe {
+        core::ptr::write_volatile(&mut (*trap_info).mcause, usize::MAX);
+        // Write expected_trap
+        mtvec::write(expected_trap as _, mtvec::TrapMode::Direct);
+
+        asm!(
+            "add a3, {tinfo}, zero",
+            "add a4, {tinfo}, zero",
+            "csrw {csr}, {value}",
+            tinfo = in(reg) tinfo,
+            csr = const CSR_NUM,
+            value = in(reg) value,
+            options(nostack, preserves_flags)
+        );
+        asm!("csrw mtvec, {}", in(reg) mtvec);
+    }
+}
+
+pub(crate) unsafe fn csr_swap<const CSR_NUM: u32>(val: usize) -> usize {
+    let ret: usize;
+
+    unsafe {
+        asm!(
+            "csrrw {ret}, {csr}, {val}",
+            csr = const CSR_NUM,
+            val = in(reg) val,
+            ret = out(reg) ret,
+            options(nostack, preserves_flags)
+        );
+    }
+    ret
+}

+ 61 - 41
prototyper/prototyper/src/sbi/extensions.rs → prototyper/prototyper/src/sbi/features.rs

@@ -1,11 +1,18 @@
+use seq_macro::seq;
 use serde_device_tree::buildin::NodeSeq;
 
+use crate::riscv::csr::*;
 use crate::riscv::current_hartid;
-use crate::sbi::trap_stack::ROOT_STACK;
+use crate::sbi::early_trap::{TrapInfo, csr_read_allow, csr_write_allow};
+use crate::sbi::trap_stack::hart_context;
+
+use super::early_trap::csr_swap;
 
 pub struct HartFeatures {
     extension: [bool; Extension::COUNT],
     privileged_version: PrivilegedVersion,
+    mhpm_mask: u32,
+    mhpm_bits: u32,
 }
 
 #[derive(Copy, Clone)]
@@ -37,26 +44,22 @@ impl Extension {
     }
 }
 
+/// access hart feature
 pub fn hart_extension_probe(hart_id: usize, ext: Extension) -> bool {
-    unsafe {
-        ROOT_STACK
-            .get_mut(hart_id)
-            .map(|x| x.hart_context().features.extension[ext.index()])
-            .unwrap()
-    }
+    hart_context(hart_id).features.extension[ext.index()]
 }
 
 pub fn hart_privileged_version(hart_id: usize) -> PrivilegedVersion {
-    unsafe {
-        ROOT_STACK
-            .get_mut(hart_id)
-            .map(|x| x.hart_context().features.privileged_version)
-            .unwrap()
-    }
+    hart_context(hart_id).features.privileged_version
 }
 
+pub fn hart_mhpm_mask(hart_id: usize) -> u32 {
+    hart_context(hart_id).features.mhpm_mask
+}
+
+/// Hart features detection
 #[cfg(not(feature = "nemu"))]
-pub fn init(cpus: &NodeSeq) {
+pub fn extension_detection(cpus: &NodeSeq) {
     use crate::devicetree::Cpu;
     for cpu_iter in cpus.iter() {
         let cpu = cpu_iter.deserialize::<Cpu>();
@@ -74,23 +77,13 @@ pub fn init(cpus: &NodeSeq) {
                 hart_exts[ext.index()] = isa.contains(ext.as_str());
             })
         }
-
-        unsafe {
-            ROOT_STACK
-                .get_mut(hart_id)
-                .map(|stack| stack.hart_context().features.extension = hart_exts)
-                .unwrap()
-        }
+        hart_context(hart_id).features.extension = hart_exts;
     }
 }
 
-pub fn privileged_version_detection() {
+fn privileged_version_detection() {
     let mut current_priv_ver = PrivilegedVersion::Unknown;
     {
-        const CSR_MCOUNTEREN: u64 = 0x306;
-        const CSR_MCOUNTINHIBIT: u64 = 0x320;
-        const CSR_MENVCFG: u64 = 0x30a;
-
         if has_csr!(CSR_MCOUNTEREN) {
             current_priv_ver = PrivilegedVersion::Version1_10;
             if has_csr!(CSR_MCOUNTINHIBIT) {
@@ -101,12 +94,46 @@ pub fn privileged_version_detection() {
             }
         }
     }
-    unsafe {
-        ROOT_STACK
-            .get_mut(current_hartid())
-            .map(|stack| stack.hart_context().features.privileged_version = current_priv_ver)
-            .unwrap()
+    hart_context(current_hartid()).features.privileged_version = current_priv_ver;
+}
+
+fn mhpm_detection() {
+    // The standard specifies that mcycle,minstret,mtime must be implemented
+    let mut current_mhpm_mask: u32 = 0b111;
+    let mut trap_info: TrapInfo = TrapInfo::default();
+
+    fn check_mhpm_csr<const CSR_NUM: u32>(trap_info: *mut TrapInfo, mhpm_mask: &mut u32) {
+        unsafe {
+            let old_value = csr_read_allow::<CSR_NUM>(trap_info);
+            if (*trap_info).mcause == usize::MAX {
+                csr_write_allow::<CSR_NUM>(trap_info, 1);
+                if (*trap_info).mcause == usize::MAX && csr_swap::<CSR_NUM>(old_value) == 1 {
+                    (*mhpm_mask) |= 1 << (CSR_NUM - CSR_MCYCLE);
+                }
+            }
+        }
+    }
+
+    macro_rules! m_check_mhpm_csr {
+        ($csr_num:expr, $trap_info:expr, $value:expr) => {
+            check_mhpm_csr::<$csr_num>($trap_info, $value)
+        };
     }
+
+    // CSR_MHPMCOUNTER3:   0xb03
+    // CSR_MHPMCOUNTER31:  0xb1f
+    seq!(csr_num in 0xb03..=0xb1f{
+        m_check_mhpm_csr!(csr_num, &mut trap_info, &mut current_mhpm_mask);
+    });
+
+    hart_context(current_hartid()).features.mhpm_mask = current_mhpm_mask;
+    // TODO: at present, rustsbi prptotyper only supports 64bit.
+    hart_context(current_hartid()).features.mhpm_bits = 64;
+}
+
+pub fn hart_features_detection() {
+    privileged_version_detection();
+    mhpm_detection();
 }
 
 #[cfg(feature = "nemu")]
@@ -114,16 +141,9 @@ pub fn init(cpus: &NodeSeq) {
     for hart_id in 0..cpus.len() {
         let mut hart_exts = [false; Extension::COUNT];
         hart_exts[Extension::Sstc.index()] = true;
-        unsafe {
-            ROOT_STACK
-                .get_mut(hart_id)
-                .map(|stack| {
-                    stack.hart_context().features = HartFeatures {
-                        extension: hart_exts,
-                        privileged_version: PrivilegedVersion::Version1_12,
-                    }
-                })
-                .unwrap()
+        hart_context(hart_id).features = HartFeatures {
+            extension: hart_exts,
+            privileged_version: PrivilegedVersion::Version1_12,
         }
     }
 }

+ 1 - 1
prototyper/prototyper/src/sbi/hart_context.rs

@@ -1,4 +1,4 @@
-use crate::sbi::extensions::HartFeatures;
+use crate::sbi::features::HartFeatures;
 use crate::sbi::hsm::HsmCell;
 use crate::sbi::rfence::RFenceCell;
 use core::ptr::NonNull;

+ 1 - 1
prototyper/prototyper/src/sbi/ipi.rs

@@ -1,7 +1,7 @@
 use crate::platform::PLATFORM;
 use crate::riscv::csr::stimecmp;
 use crate::riscv::current_hartid;
-use crate::sbi::extensions::{Extension, hart_extension_probe};
+use crate::sbi::features::{Extension, hart_extension_probe};
 use crate::sbi::hsm::remote_hsm;
 use crate::sbi::rfence;
 use crate::sbi::trap_stack::ROOT_STACK;

+ 2 - 1
prototyper/prototyper/src/sbi/mod.rs

@@ -3,11 +3,12 @@ use rustsbi::RustSBI;
 pub mod console;
 pub mod hsm;
 pub mod ipi;
+pub mod pmu;
 pub mod reset;
 pub mod rfence;
 
 pub mod early_trap;
-pub mod extensions;
+pub mod features;
 pub mod fifo;
 pub mod hart_context;
 pub mod heap;

+ 70 - 0
prototyper/prototyper/src/sbi/pmu.rs

@@ -0,0 +1,70 @@
+use crate::{riscv::current_hartid, sbi::features::hart_mhpm_mask};
+use rustsbi::{Pmu, SbiRet};
+use sbi_spec::{binary::SharedPtr, pmu::shmem_size::SIZE};
+
+use super::trap_stack::hart_context;
+
+const HARDWARE_COUNTER_MAX: usize = 32;
+const FIRMWARE_COUNTER_MAX: usize = 16;
+
+pub struct PmuState {
+    active_event: [i64; HARDWARE_COUNTER_MAX + FIRMWARE_COUNTER_MAX],
+    fw_counter_state: usize,
+    fw_counter: [i64; FIRMWARE_COUNTER_MAX],
+}
+
+struct SbiPmu;
+
+impl Pmu for SbiPmu {
+    fn num_counters(&self) -> usize {
+        hart_mhpm_mask(current_hartid()).count_ones() as usize + FIRMWARE_COUNTER_MAX
+    }
+
+    fn counter_get_info(&self, counter_idx: usize) -> SbiRet {
+        todo!()
+    }
+
+    fn counter_config_matching(
+        &self,
+        counter_idx_base: usize,
+        counter_idx_mask: usize,
+        config_flags: usize,
+        event_idx: usize,
+        event_data: u64,
+    ) -> SbiRet {
+        todo!()
+    }
+
+    fn counter_start(
+        &self,
+        counter_idx_base: usize,
+        counter_idx_mask: usize,
+        start_flags: usize,
+        initial_value: u64,
+    ) -> SbiRet {
+        todo!()
+    }
+
+    fn counter_stop(
+        &self,
+        counter_idx_base: usize,
+        counter_idx_mask: usize,
+        stop_flags: usize,
+    ) -> SbiRet {
+        todo!()
+    }
+
+    fn counter_fw_read(&self, counter_idx: usize) -> SbiRet {
+        todo!()
+    }
+
+    fn counter_fw_read_hi(&self, counter_idx: usize) -> SbiRet {
+        todo!()
+    }
+
+    fn snapshot_set_shmem(&self, shmem: SharedPtr<[u8; SIZE]>, flags: usize) -> SbiRet {
+        // Optional function, `not_supported` is returned if not implemented.
+        let _ = (shmem, flags);
+        SbiRet::not_supported()
+    }
+}

+ 4 - 0
prototyper/prototyper/src/sbi/trap_stack.rs

@@ -42,6 +42,10 @@ pub(crate) fn prepare_for_trap() {
     };
 }
 
+pub fn hart_context(hart_id: usize) -> &'static mut HartContext {
+    unsafe { ROOT_STACK.get_mut(hart_id).unwrap().hart_context() }
+}
+
 /// Stack type for each hart.
 ///
 /// Memory layout: