hsm.rs 8.3 KB

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