sbi-hart-mask.rs 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. //! This example demonstrates how the `HartMask` structure operates in a non-`usize` environment.
  2. //! It simulates a 128-bit RISC-V SBI environment where SBI calls only accept 128-bit parameters.
  3. //! To represent a 128-bit SBI hart mask, we use the `HartMask<u128>` type to complete the SBI call procedure.
  4. use sbi_spec::binary::HartMask;
  5. use std::{
  6. sync::Mutex,
  7. thread::{self, JoinHandle},
  8. time::Duration,
  9. };
  10. /// Number of simulated hardware threads (harts) in the environment.
  11. const N_THREADS: usize = 8;
  12. /// Array of thread handles wrapped in a `Mutex` for safe concurrent access.
  13. /// Each element is an `Option<JoinHandle<()>>`, initialized to `None`.
  14. static THREADS: [Mutex<Option<JoinHandle<()>>>; N_THREADS] =
  15. [const { Mutex::new(None) }; N_THREADS];
  16. fn main() {
  17. emulation_init(); // Initialize the simulated SBI environment.
  18. primary_hart_main(); // Execute the primary hart's logic.
  19. emulation_finish(); // Clean up the emulation environment.
  20. }
  21. /// Simulates the main logic executed by the primary hart.
  22. fn primary_hart_main() {
  23. println!("Primary hart is starting");
  24. // Send an Inter-Processor Interrupt (IPI) to all secondary harts.
  25. // On a 128-bit RISC-V SBI platform, the `send_ipi` function only accepts
  26. // `hart_mask` parameters of type `HartMask<u128>`.
  27. sbi::send_ipi(HartMask::all());
  28. println!("Primary hart finished");
  29. }
  30. /// Simulates the main logic executed by a secondary hart.
  31. fn secondary_hart_main(hart_id: u128) {
  32. println!("Secondary hart {} is waiting for interrupt", hart_id);
  33. // Simulate the "Wait For Interrupt" (WFI) operation.
  34. // In a real-world scenario, supervisor software might also use the SBI `hart_suspend` function instead.
  35. wfi();
  36. // If the secondary harts are woken up by the SBI `send_ipi` function, execution resumes here.
  37. println!("Secondary hart {} received interrupt", hart_id);
  38. }
  39. /* -- Implementation of a mock SBI runtime -- */
  40. mod sbi {
  41. use super::{N_THREADS, unpark_thread};
  42. use sbi_spec::binary::{HartMask, SbiRet};
  43. /// Mock function to send an IPI to harts specified in the `hart_mask`.
  44. pub fn send_ipi(hart_mask: HartMask<u128>) -> SbiRet<u128> {
  45. let (mask, base) = hart_mask.into_inner();
  46. // If the `hart_mask` specifies all harts, wake up all threads.
  47. if hart_mask == HartMask::all() {
  48. for hart_id in 0..N_THREADS as u128 {
  49. unpark_thread(hart_id);
  50. }
  51. return SbiRet::success(0);
  52. }
  53. // Or, iterate through each bit in the mask to determine which harts to wake up.
  54. for bit_offset in 0..128 {
  55. if (mask & (1 << bit_offset)) != 0 {
  56. let hart_id = base + bit_offset;
  57. println!("Hart id {}", hart_id);
  58. if hart_id < N_THREADS as u128 {
  59. unpark_thread(hart_id);
  60. }
  61. }
  62. }
  63. SbiRet::success(0)
  64. }
  65. }
  66. /// Initializes the emulation environment by spawning secondary hart threads.
  67. fn emulation_init() {
  68. println!("Emulation start");
  69. // Spawn a thread for each secondary hart.
  70. for i in 0..N_THREADS {
  71. *THREADS[i].lock().unwrap() = Some(thread::spawn(move || secondary_hart_main(i as u128)));
  72. }
  73. // Add a short delay to ensure all threads are properly initialized before the primary hart starts.
  74. thread::sleep(Duration::from_micros(10));
  75. }
  76. /// Simulates the "Wait For Interrupt" (WFI) operation.
  77. fn wfi() {
  78. thread::park(); // Blocks the current thread until it is unparked by another thread.
  79. }
  80. /// Cleans up the emulation environment by stopping all secondary harts.
  81. fn emulation_finish() {
  82. // Add a short delay to ensure all threads have completed their tasks.
  83. thread::sleep(Duration::from_micros(10));
  84. // Iterate through all threads, stop them, and wait for their completion.
  85. for (i, thread) in THREADS.iter().enumerate() {
  86. if let Some(thread) = thread.lock().unwrap().take() {
  87. println!("Hart {} stopped", i);
  88. thread.join().unwrap(); // Wait for the thread to finish execution.
  89. }
  90. }
  91. println!("All harts stopped, emulation finished");
  92. }
  93. /// Unparks (wakes up) a specific hart by its ID.
  94. fn unpark_thread(id: u128) {
  95. assert!(id < N_THREADS as u128, "Invalid hart ID");
  96. // Safely access the thread handle and unpark the thread if it exists.
  97. if let Some(thread) = &*THREADS[id as usize].lock().unwrap() {
  98. thread.thread().unpark(); // Resumes execution of the parked thread.
  99. }
  100. }
  101. /* Code execution result analysis:
  102. The primary hart sends an IPI to all secondary harts using `HartMask::all()`, which
  103. represents a mask where all bits are set to 1. This triggers the `send_ipi` function
  104. to wake up all secondary harts. As a result, the output will be:
  105. - Primary hart is starting
  106. - Secondary hart 0 is waiting for interrupt
  107. - Secondary hart 1 is waiting for interrupt
  108. - ...
  109. - Secondary hart 7 is waiting for interrupt
  110. - Secondary hart 0 received interrupt
  111. - Secondary hart 1 received interrupt
  112. - ...
  113. - Secondary hart 7 received interrupt
  114. - Primary hart finished
  115. - Hart 0 stopped
  116. - Hart 1 stopped
  117. - ...
  118. - Hart 7 stopped
  119. - All harts stopped, emulation finished
  120. To test a scenario where only specific harts receive the IPI, modify the `send_ipi`
  121. call to use a custom `HartMask` with specific bits set. For example:
  122. `HartMask::from_raw(0b1010, 0)` would wake up harts with IDs 1 and 3.
  123. */