@@ -54,11 +54,14 @@ pub struct QemuHsm {
54
54
last_command : Arc < spin:: Mutex < HashMap < usize , HsmCommand > > > ,
55
55
}
56
56
57
- // RustSBI-QEMU HSM command, these commands apply to a given hart.
57
+ // RustSBI-QEMU HSM command, these commands apply to a remote given hart.
58
58
//
59
59
// Should be stored with hart id before software interrupt is invoked.
60
60
// After software interrupt is received, the target hart should handle with HSM command structure
61
61
// and run corresponding HSM procedures.
62
+ //
63
+ // By current version of SBI specification, suspend command only apply to current hart,
64
+ // thus RustSBI does not use remote HSM command in this case.
62
65
#[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
63
66
pub enum HsmCommand {
64
67
Start ( usize , usize ) ,
@@ -160,7 +163,7 @@ impl rustsbi::Hsm for QemuHsm {
160
163
Ordering :: Acquire ,
161
164
) ;
162
165
// check current hart state
163
- if current_state == Err ( HsmState :: Started as u8 ) {
166
+ if current_state. is_err ( ) {
164
167
return SbiRet :: failed ( ) // illegal state
165
168
}
166
169
// fill in the parameter
@@ -190,7 +193,28 @@ impl rustsbi::Hsm for QemuHsm {
190
193
// Resuming from a retentive suspend state is straight forward and the supervisor-mode software
191
194
// will see SBI suspend call return without any failures.
192
195
SUSPEND_RETENTIVE => {
193
- pause ( ) ; // pause and wait for machine level ipi
196
+ // try to set current target hart state to stop pending
197
+ let hart_id = riscv:: register:: mhartid:: read ( ) ;
198
+ let mut state_lock = self . state . lock ( ) ;
199
+ let current_state = state_lock. entry ( hart_id)
200
+ . or_insert ( AtomicU8 :: new ( HsmState :: Stopped as u8 ) )
201
+ . compare_exchange (
202
+ HsmState :: Started as u8 ,
203
+ HsmState :: SuspendPending as u8 ,
204
+ Ordering :: AcqRel ,
205
+ Ordering :: Acquire ,
206
+ ) ;
207
+ // check current hart state
208
+ if current_state. is_err ( ) {
209
+ return SbiRet :: failed ( ) // illegal state
210
+ }
211
+ drop ( state_lock) ;
212
+ // actual suspend begin
213
+ suspend_current_hart ( & self ) ; // pause and wait for machine level ipi
214
+ // mark current hart as started
215
+ let mut state_lock = self . state . lock ( ) ;
216
+ state_lock. entry ( hart_id) . insert ( AtomicU8 :: new ( HsmState :: Started as u8 ) ) ;
217
+ drop ( state_lock) ;
194
218
SbiRet :: ok ( 0 )
195
219
} ,
196
220
// Resuming from a non-retentive suspend state is relatively more involved and requires software
@@ -205,11 +229,33 @@ impl rustsbi::Hsm for QemuHsm {
205
229
// | a0 | hartid
206
230
// | a1 | `opaque` parameter
207
231
SUSPEND_NON_RETENTIVE => {
208
- pause ( ) ;
232
+ // try to set current target hart state to stop pending
233
+ let hart_id = riscv:: register:: mhartid:: read ( ) ;
234
+ let mut state_lock = self . state . lock ( ) ;
235
+ let current_state = state_lock. entry ( hart_id)
236
+ . or_insert ( AtomicU8 :: new ( HsmState :: Stopped as u8 ) )
237
+ . compare_exchange (
238
+ HsmState :: Started as u8 ,
239
+ HsmState :: SuspendPending as u8 ,
240
+ Ordering :: AcqRel ,
241
+ Ordering :: Acquire ,
242
+ ) ;
243
+ // check current hart state
244
+ if current_state. is_err ( ) {
245
+ return SbiRet :: failed ( ) // illegal state
246
+ }
247
+ drop ( state_lock) ;
248
+ // retentive suspend
249
+ suspend_current_hart ( & self ) ;
250
+ // begin wake process
209
251
unsafe {
210
252
riscv:: register:: satp:: write ( 0 ) ;
211
253
riscv:: register:: sstatus:: clear_sie ( ) ;
212
254
}
255
+ // when wake, mark current hart as started
256
+ let mut state_lock = self . state . lock ( ) ;
257
+ state_lock. entry ( hart_id) . insert ( AtomicU8 :: new ( HsmState :: Started as u8 ) ) ;
258
+ drop ( state_lock) ;
213
259
match ( ) {
214
260
#[ cfg( any( target_arch = "riscv32" , target_arch = "riscv64" ) ) ]
215
261
( ) => unsafe {
@@ -238,6 +284,39 @@ impl rustsbi::Hsm for QemuHsm {
238
284
const SUSPEND_RETENTIVE : u32 = 0x00000000 ;
239
285
const SUSPEND_NON_RETENTIVE : u32 = 0x80000000 ;
240
286
287
+ // Suspend current hart and record resume state when wake
288
+ pub fn suspend_current_hart ( hsm : & QemuHsm ) {
289
+ use riscv:: asm:: wfi;
290
+ use riscv:: register:: { mie, mip, mhartid} ;
291
+ use crate :: clint:: Clint ;
292
+ let hart_id = mhartid:: read ( ) ;
293
+ let clint = Clint :: new ( 0x2000000 as * mut u8 ) ;
294
+ clint. clear_soft ( hart_id) ; // Clear IPI
295
+ unsafe { mip:: clear_msoft ( ) } ; // clear machine software interrupt flag
296
+ let prev_msoft = mie:: read ( ) . msoft ( ) ;
297
+ unsafe { mie:: set_msoft ( ) } ; // Start listening for software interrupts
298
+ // mark current state as suspended
299
+ let mut state_lock = hsm. state . lock ( ) ;
300
+ state_lock. entry ( hart_id) . insert ( AtomicU8 :: new ( HsmState :: Suspended as u8 ) ) ;
301
+ drop ( state_lock) ;
302
+ // actual suspended process
303
+ loop {
304
+ unsafe { wfi ( ) } ;
305
+ if mip:: read ( ) . msoft ( ) {
306
+ break ;
307
+ }
308
+ }
309
+ // mark current state as resume pending
310
+ let mut state_lock = hsm. state . lock ( ) ;
311
+ state_lock. entry ( hart_id) . insert ( AtomicU8 :: new ( HsmState :: ResumePending as u8 ) ) ;
312
+ drop ( state_lock) ;
313
+ // resume
314
+ if !prev_msoft {
315
+ unsafe { mie:: clear_msoft ( ) } ; // Stop listening for software interrupts
316
+ }
317
+ clint. clear_soft ( hart_id) ; // Clear IPI
318
+ }
319
+
241
320
// Pause current hart, wake through inter-processor interrupt
242
321
pub fn pause ( ) {
243
322
use riscv:: asm:: wfi;
@@ -247,6 +326,7 @@ pub fn pause() {
247
326
let hartid = mhartid:: read ( ) ;
248
327
let clint = Clint :: new ( 0x2000000 as * mut u8 ) ;
249
328
clint. clear_soft ( hartid) ; // Clear IPI
329
+ mip:: clear_msoft ( ) ; // clear machine software interrupt flag
250
330
let prev_msoft = mie:: read ( ) . msoft ( ) ;
251
331
mie:: set_msoft ( ) ; // Start listening for software interrupts
252
332
loop {
0 commit comments