Skip to content

Commit 034cc60

Browse files
committed
Fix hart start SBI call
1 parent f8fe941 commit 034cc60

File tree

4 files changed

+149
-40
lines changed

4 files changed

+149
-40
lines changed

rustsbi-qemu/src/execute.rs

Lines changed: 12 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -39,34 +39,22 @@ pub fn execute_supervisor(supervisor_mepc: usize, a0: usize, a1: usize, hsm: Qem
3939
mie::clear_mtimer();
4040
},
4141
GeneratorState::Yielded(MachineTrap::MachineSoft()) => match hsm.last_command() {
42-
Some(HsmCommand::Start(start_addr, opaque)) => {
43-
unsafe {
44-
riscv::register::satp::write(0);
45-
riscv::register::sstatus::clear_sie();
46-
}
47-
hsm.record_current_start_finished();
48-
match () {
49-
#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
50-
() => unsafe {
51-
asm!(
52-
"csrr a0, mhartid",
53-
"jr {start_addr}",
54-
start_addr = in(reg) start_addr,
55-
in("a1") opaque,
56-
options(noreturn)
57-
)
58-
},
59-
#[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))]
60-
() => {
61-
drop((start_addr, opaque));
62-
unimplemented!("not RISC-V instruction set architecture")
63-
}
64-
};
65-
},
42+
Some(HsmCommand::Start(_start_paddr, _opaque)) => panic!("rustsbi-qemu: illegal state"),
6643
Some(HsmCommand::Stop) => {
6744
// no hart stop command in qemu, record stop state and pause
6845
hsm.record_current_stop_finished();
6946
pause();
47+
if let Some(HsmCommand::Start(start_paddr, opaque)) = hsm.last_command() {
48+
unsafe {
49+
riscv::register::satp::write(0);
50+
riscv::register::sstatus::clear_sie();
51+
}
52+
hsm.record_current_start_finished();
53+
let ctx = rt.context_mut();
54+
ctx.mstatus = riscv::register::mstatus::read(); // get from modified sstatus
55+
ctx.a0 = opaque;
56+
ctx.mepc = start_paddr;
57+
}
7058
},
7159
None => panic!("rustsbi-qemu: machine soft interrupt with no hart state monitor command"),
7260
},

rustsbi-qemu/src/qemu_hsm.rs

Lines changed: 84 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,14 @@ pub struct QemuHsm {
5454
last_command: Arc<spin::Mutex<HashMap<usize, HsmCommand>>>,
5555
}
5656

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.
5858
//
5959
// Should be stored with hart id before software interrupt is invoked.
6060
// After software interrupt is received, the target hart should handle with HSM command structure
6161
// 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.
6265
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
6366
pub enum HsmCommand {
6467
Start(usize, usize),
@@ -160,7 +163,7 @@ impl rustsbi::Hsm for QemuHsm {
160163
Ordering::Acquire,
161164
);
162165
// check current hart state
163-
if current_state == Err(HsmState::Started as u8) {
166+
if current_state.is_err() {
164167
return SbiRet::failed() // illegal state
165168
}
166169
// fill in the parameter
@@ -190,7 +193,28 @@ impl rustsbi::Hsm for QemuHsm {
190193
// Resuming from a retentive suspend state is straight forward and the supervisor-mode software
191194
// will see SBI suspend call return without any failures.
192195
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);
194218
SbiRet::ok(0)
195219
},
196220
// Resuming from a non-retentive suspend state is relatively more involved and requires software
@@ -205,11 +229,33 @@ impl rustsbi::Hsm for QemuHsm {
205229
// | a0 | hartid
206230
// | a1 | `opaque` parameter
207231
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
209251
unsafe {
210252
riscv::register::satp::write(0);
211253
riscv::register::sstatus::clear_sie();
212254
}
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);
213259
match () {
214260
#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
215261
() => unsafe {
@@ -238,6 +284,39 @@ impl rustsbi::Hsm for QemuHsm {
238284
const SUSPEND_RETENTIVE: u32 = 0x00000000;
239285
const SUSPEND_NON_RETENTIVE: u32 = 0x80000000;
240286

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+
241320
// Pause current hart, wake through inter-processor interrupt
242321
pub fn pause() {
243322
use riscv::asm::wfi;
@@ -247,6 +326,7 @@ pub fn pause() {
247326
let hartid = mhartid::read();
248327
let clint = Clint::new(0x2000000 as *mut u8);
249328
clint.clear_soft(hartid); // Clear IPI
329+
mip::clear_msoft(); // clear machine software interrupt flag
250330
let prev_msoft = mie::read().msoft();
251331
mie::set_msoft(); // Start listening for software interrupts
252332
loop {

test-kernel/src/main.rs

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,31 +26,45 @@ pub extern "C" fn rust_main(hartid: usize, dtb_pa: usize) -> ! {
2626
);
2727
test_base_extension();
2828
test_sbi_ins_emulation();
29+
unsafe { stvec::write(start_trap as usize, TrapMode::Direct) };
30+
println!(">> Test-kernel: Trigger illegal exception");
31+
unsafe { asm!("csrw mcycle, x0") }; // mcycle cannot be written, this is always a 4-byte illegal instruction
2932
}
3033
if hartid == 0 {
31-
let sbi_ret = sbi::hart_stop(1);
32-
println!(">> Stop hart 1, return value {:?}", sbi_ret);
34+
let sbi_ret = sbi::hart_stop(3);
35+
println!(">> Stop hart 3, return value {:?}", sbi_ret);
3336
for i in 0..4 {
3437
let sbi_ret = sbi::hart_get_status(i);
3538
println!(">> Hart {} state return value: {:?}", i, sbi_ret);
3639
}
37-
} else {
40+
} else if hartid == 1 || hartid == 2 {
3841
let sbi_ret = sbi::hart_suspend(0x00000000, 0, 0);
3942
println!(">> Start test for hart {}, suspend return value {:?}", hartid, sbi_ret);
43+
} else { // hartid == 3
44+
loop {}
4045
}
41-
unsafe { stvec::write(start_trap as usize, TrapMode::Direct) };
42-
println!(">> Test-kernel: Trigger illegal exception");
43-
unsafe { asm!("csrw mcycle, x0") }; // mcycle cannot be written, this is always a 4-byte illegal instruction
44-
if hartid != 3 {
46+
if hartid == 0 || hartid == 1 {
4547
println!("<< Test-kernel: test for hart {} success, wake another hart", hartid);
4648
let bv: usize = 0b10;
4749
let sbi_ret = sbi::send_ipi(&bv as *const _ as usize, hartid); // wake hartid + 1
4850
println!(">> Wake, sbi return value {:?}", sbi_ret);
4951
loop {} // wait for machine shutdown
50-
} else {
51-
println!("<< Test-kernel: All hart SBI test SUCCESS, shutdown");
52-
sbi::shutdown()
53-
}
52+
} else if hartid == 2 {
53+
let param = 0x12345678;
54+
println!(">> Start hart 3 with parameter {:#x}", param);
55+
/* start_addr should be physical address, and here pa == va */
56+
let sbi_ret = sbi::hart_start(3, hart_3_start as usize, param);
57+
println!(">> SBI return value: {:?}", sbi_ret);
58+
loop {} // wait for machine shutdown
59+
} else { // hartid == 3
60+
unreachable!()
61+
}
62+
}
63+
64+
extern "C" fn hart_3_start(param: usize) {
65+
println!("<< The parameter passed to hart start is: {:#x}", param);
66+
println!("<< Test-kernel: All hart SBI test SUCCESS, shutdown");
67+
sbi::shutdown()
5468
}
5569

5670
fn test_base_extension() {

test-kernel/src/sbi.rs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#![allow(unused)]
2+
use core::fmt;
23

34
pub const EXTENSION_BASE: usize = 0x10;
45
pub const EXTENSION_TIMER: usize = 0x54494D45;
@@ -15,7 +16,6 @@ const FUNCTION_BASE_GET_MVENDORID: usize = 0x4;
1516
const FUNCTION_BASE_GET_MARCHID: usize = 0x5;
1617
const FUNCTION_BASE_GET_MIMPID: usize = 0x6;
1718

18-
#[derive(Debug)]
1919
#[repr(C)]
2020
pub struct SbiRet {
2121
/// Error number
@@ -24,6 +24,33 @@ pub struct SbiRet {
2424
pub value: usize,
2525
}
2626

27+
const SBI_SUCCESS: usize = 0;
28+
const SBI_ERR_FAILED: usize = usize::from_ne_bytes(isize::to_ne_bytes(-1));
29+
const SBI_ERR_NOT_SUPPORTED: usize = usize::from_ne_bytes(isize::to_ne_bytes(-2));
30+
const SBI_ERR_INVALID_PARAM: usize = usize::from_ne_bytes(isize::to_ne_bytes(-3));
31+
const SBI_ERR_DENIED: usize = usize::from_ne_bytes(isize::to_ne_bytes(-4));
32+
const SBI_ERR_INVALID_ADDRESS: usize = usize::from_ne_bytes(isize::to_ne_bytes(-5));
33+
const SBI_ERR_ALREADY_AVAILABLE: usize = usize::from_ne_bytes(isize::to_ne_bytes(-6));
34+
const SBI_ERR_ALREADY_STARTED: usize = usize::from_ne_bytes(isize::to_ne_bytes(-7));
35+
const SBI_ERR_ALREADY_STOPPED: usize = usize::from_ne_bytes(isize::to_ne_bytes(-8));
36+
37+
impl fmt::Debug for SbiRet {
38+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39+
match self.error {
40+
SBI_SUCCESS => write!(f, "{:?}", self.value),
41+
SBI_ERR_FAILED => write!(f, "<SBI call failed>"),
42+
SBI_ERR_NOT_SUPPORTED => write!(f, "<SBI feature not supported>"),
43+
SBI_ERR_INVALID_PARAM => write!(f, "<SBI invalid parameter>"),
44+
SBI_ERR_DENIED => write!(f, "<SBI denied>"),
45+
SBI_ERR_INVALID_ADDRESS => write!(f, "<SBI invalid address>"),
46+
SBI_ERR_ALREADY_AVAILABLE => write!(f, "<SBI already available>"),
47+
SBI_ERR_ALREADY_STARTED => write!(f, "<SBI already started>"),
48+
SBI_ERR_ALREADY_STOPPED => write!(f, "<SBI already stopped>"),
49+
unknown => write!(f, "[SBI Unknown error: {}]", unknown),
50+
}
51+
}
52+
}
53+
2754
#[inline]
2855
pub fn get_spec_version() -> usize {
2956
sbi_call_0(EXTENSION_BASE, FUNCTION_BASE_GET_SPEC_VERSION).value

0 commit comments

Comments
 (0)