Skip to main content

sbi_testing/
hsm.rs

1//! Hart state monitor extension test suite.
2
3use core::sync::atomic::{AtomicU32, Ordering};
4use sbi::{HartMask, SbiRet};
5use sbi_spec::hsm::hart_state;
6
7/// Hart state monitor extension test cases.
8#[derive(Clone, Debug)]
9pub enum Case<'a> {
10    /// Can't proceed test for Hart state monitor extension does not exist.
11    NotExist,
12    /// Test begin.
13    Begin,
14    /// Test failed for hart started before test begin.
15    ///
16    /// The returned value includes which hart led to this test failure.
17    HartStartedBeforeTest(usize),
18    /// Test failed for no other harts are available to be tested.
19    NoStoppedHart,
20    /// Test process for begin test hart state monitor on one batch.
21    BatchBegin(&'a [usize]),
22    /// Test process for target hart to be tested has started.
23    HartStarted(usize),
24    /// Test failed for can't start target hart with [`SbiRet`] error.
25    HartStartFailed {
26        /// The target hart ID that has failed to start.
27        hartid: usize,
28        /// The `SbiRet` value for the failed hart start SBI call.
29        ret: SbiRet,
30    },
31    /// Test process for target hart to be tested has non-retentively suspended.
32    HartSuspendedNonretentive(usize),
33    /// Test process for target hart to be tested has resumed.
34    HartResumed(usize),
35    /// Test process for target hart to be tested has retentively suspended.
36    HartSuspendedRetentive(usize),
37    /// Test process for target hart to be tested has stopped.
38    HartStopped(usize),
39    /// Remote RFence succeeded on a started remote hart.
40    RemoteRFencePass(usize),
41    /// Remote RFence failed on a started remote hart.
42    RemoteRFenceFailed(usize, SbiRet),
43    /// Test process for harts on current batch has passed the tests.
44    BatchPass(&'a [usize]),
45    /// All test cases on hart state monitor module finished.
46    Pass,
47}
48
49/// Test hart state monitor extension on given harts.
50///
51/// The test case output is to be handled in `f`.
52pub fn test(
53    primary_hart_id: usize,
54    mut hart_mask: usize,
55    hart_mask_base: usize,
56    mut f: impl FnMut(Case),
57) {
58    // 不支持 HSM 扩展
59    if sbi::probe_extension(sbi::Hsm).is_unavailable() {
60        f(Case::NotExist);
61        return;
62    }
63    f(Case::Begin);
64    let rfnc_available = sbi::probe_extension(sbi::Fence).is_available();
65
66    let mut batch = [0usize; TEST_BATCH_SIZE];
67    let mut batch_count = 0;
68    let mut batch_size = 0;
69    let mut hartid = hart_mask_base;
70    while hart_mask != 0 {
71        if hartid != primary_hart_id {
72            // 副核在测试前必须处于停止状态
73            if sbi::hart_get_status(hartid) == STOPPED {
74                batch[batch_size] = hartid;
75                batch_size += 1;
76                // 收集一个批次,执行测试
77                if batch_size == TEST_BATCH_SIZE {
78                    if test_batch(&batch, rfnc_available, &mut f) {
79                        batch_count += 1;
80                        batch_size = 0;
81                    } else {
82                        return;
83                    }
84                }
85            }
86            // 副核不在停止状态
87            else {
88                f(Case::HartStartedBeforeTest(hartid));
89            }
90        }
91        let distance = hart_mask.trailing_zeros() + 1;
92        hart_mask >>= distance;
93        hartid += distance as usize;
94    }
95    // 为不满一批次的核执行测试
96    if batch_size > 0 {
97        if test_batch(&batch[..batch_size], rfnc_available, &mut f) {
98            f(Case::Pass);
99        }
100    }
101    // 所有批次通过测试
102    else if batch_count > 0 {
103        f(Case::Pass);
104    }
105    // 没有找到能参与测试的副核
106    else {
107        f(Case::NoStoppedHart)
108    }
109}
110
111const STARTED: SbiRet = SbiRet::success(hart_state::STARTED);
112const STOPPED: SbiRet = SbiRet::success(hart_state::STOPPED);
113const SUSPENDED: SbiRet = SbiRet::success(hart_state::SUSPENDED);
114
115const TEST_BATCH_SIZE: usize = 4;
116static mut STACK: [ItemPerHart; TEST_BATCH_SIZE] = [ItemPerHart::ZERO; TEST_BATCH_SIZE];
117
118#[repr(C, align(512))]
119struct ItemPerHart {
120    stage: AtomicU32,
121    signal: AtomicU32,
122    stack: [u8; 504],
123}
124
125const STAGE_IDLE: u32 = 0;
126const STAGE_STARTED: u32 = 1;
127const STAGE_RESUMED: u32 = 2;
128
129impl ItemPerHart {
130    #[allow(clippy::declare_interior_mutable_const)]
131    const ZERO: Self = Self {
132        stage: AtomicU32::new(STAGE_IDLE),
133        signal: AtomicU32::new(0),
134        stack: [0; 504],
135    };
136
137    #[inline]
138    fn reset(&mut self) -> *const ItemPerHart {
139        self.stage.store(STAGE_IDLE, Ordering::Relaxed);
140        self as _
141    }
142
143    #[inline]
144    fn wait_start(&self) {
145        while self.stage.load(Ordering::Relaxed) != STAGE_STARTED {
146            core::hint::spin_loop();
147        }
148    }
149
150    #[inline]
151    fn wait_resume(&self) {
152        while self.stage.load(Ordering::Relaxed) != STAGE_RESUMED {
153            core::hint::spin_loop();
154        }
155    }
156
157    #[inline]
158    fn send_signal(&self) {
159        self.signal.store(1, Ordering::Release);
160    }
161
162    #[inline]
163    fn wait_signal(&self) {
164        while self
165            .signal
166            .compare_exchange(1, 0, Ordering::Relaxed, Ordering::Relaxed)
167            .is_err()
168        {
169            core::hint::spin_loop();
170        }
171    }
172}
173
174/// 测试一批核
175fn test_batch(batch: &[usize], rfnc_available: bool, mut f: impl FnMut(Case)) -> bool {
176    f(Case::BatchBegin(batch));
177    // 初始这些核都是停止状态,测试 start
178    for (i, hartid) in batch.iter().copied().enumerate() {
179        let ptr = unsafe { STACK[i].reset() };
180        let ret = sbi::hart_start(hartid, test_entry as *const () as _, ptr as _);
181        if ret.is_err() {
182            f(Case::HartStartFailed { hartid, ret });
183            return false;
184        }
185    }
186    // 测试不可恢复休眠
187    for (i, hartid) in batch.iter().copied().enumerate() {
188        let item = unsafe { &mut STACK[i] };
189        // 等待完成启动
190        while sbi::hart_get_status(hartid) != STARTED {
191            core::hint::spin_loop();
192        }
193        f(Case::HartStarted(hartid));
194        if rfnc_available {
195            let rfence_ret = sbi::remote_fence_i(HartMask::from_mask_base(1, hartid));
196            if rfence_ret.is_ok() {
197                f(Case::RemoteRFencePass(hartid));
198            } else {
199                f(Case::RemoteRFenceFailed(hartid, rfence_ret));
200            }
201        }
202        // 等待信号
203        item.wait_start();
204        // 发出信号
205        item.send_signal();
206        // 等待完成休眠
207        while sbi::hart_get_status(hartid) != SUSPENDED {
208            core::hint::spin_loop();
209        }
210        f(Case::HartSuspendedNonretentive(hartid));
211    }
212    // 全部唤醒
213    let mut mask = 1usize;
214    for hartid in &batch[1..] {
215        mask |= 1 << (hartid - batch[0]);
216    }
217    sbi::send_ipi(HartMask::from_mask_base(mask, batch[0]));
218    // 测试可恢复休眠
219    for (i, hartid) in batch.iter().copied().enumerate() {
220        let item = unsafe { &mut STACK[i] };
221        // 等待完成恢复
222        while sbi::hart_get_status(hartid) != STARTED {
223            core::hint::spin_loop();
224        }
225        f(Case::HartResumed(hartid));
226        // 等待信号
227        item.wait_resume();
228        // 发出信号
229        item.send_signal();
230        // 等待完成休眠
231        while sbi::hart_get_status(hartid) != SUSPENDED {
232            core::hint::spin_loop();
233        }
234        f(Case::HartSuspendedRetentive(hartid));
235        // 单独恢复
236        sbi::send_ipi(HartMask::from_mask_base(1, hartid));
237        // 等待关闭
238        while sbi::hart_get_status(hartid) != STOPPED {
239            core::hint::spin_loop();
240        }
241        f(Case::HartStopped(hartid));
242    }
243    f(Case::BatchPass(batch));
244    true
245}
246
247/// 测试用启动入口
248#[unsafe(naked)]
249unsafe extern "C" fn test_entry(hartid: usize, opaque: *mut ItemPerHart) -> ! {
250    core::arch::naked_asm!(
251        "csrw sie, zero",   // 关中断
252        "call {set_stack}", // 设置栈
253        "j    {rust_main}", // 进入 rust
254        set_stack = sym set_stack,
255        rust_main = sym rust_main,
256    )
257}
258
259#[unsafe(naked)]
260unsafe extern "C" fn set_stack(hart_id: usize, ptr: *const ItemPerHart) {
261    core::arch::naked_asm!("addi sp, a1, 512", "ret");
262}
263
264#[inline(never)]
265extern "C" fn rust_main(hart_id: usize, opaque: *mut ItemPerHart) -> ! {
266    let item = unsafe { &mut *opaque };
267    match item.stage.compare_exchange(
268        STAGE_IDLE,
269        STAGE_STARTED,
270        Ordering::AcqRel,
271        Ordering::Acquire,
272    ) {
273        Ok(_) => {
274            item.wait_signal();
275            let ret =
276                sbi::hart_suspend(sbi::NonRetentive, test_entry as *const () as _, opaque as _);
277            unreachable!("suspend [{hart_id}] but {ret:?}")
278        }
279        Err(STAGE_STARTED) => {
280            item.stage.store(STAGE_RESUMED, Ordering::Release);
281            item.wait_signal();
282            let _ = sbi::hart_suspend(sbi::Retentive, test_entry as *const () as _, opaque as _);
283            let ret = sbi::hart_stop();
284            unreachable!("suspend [{hart_id}] but {ret:?}")
285        }
286        Err(_) => unreachable!(),
287    }
288}