diff --git a/os/src/boards/qemu.rs b/os/src/boards/qemu.rs index 0c3ba66d..a2212e32 100644 --- a/os/src/boards/qemu.rs +++ b/os/src/boards/qemu.rs @@ -1,9 +1,10 @@ pub const CLOCK_FREQ: usize = 12500000; pub const MMIO: &[(usize, usize)] = &[ - (0x1000_0000, 0x1000), - (0x1000_1000, 0x1000), - (0xC00_0000, 0x40_0000), + (0x1000_0000, 0x1000), // VIRT_UART0 in virt machine + (0x1000_1000, 0x1000), // VIRT_VIRTIO in virt machine + (0x0C00_0000, 0x40_0000), // VIRT_PLIC in virt machine + (0x0010_0000, 0x00_2000), // VIRT_TEST/RTC in virt machine ]; pub type BlockDeviceImpl = crate::drivers::block::VirtIOBlock; @@ -43,3 +44,84 @@ pub fn irq_handler() { } plic.complete(0, IntrTargetPriority::Supervisor, intr_src_id); } + + +use core::arch::asm; + +const EXIT_SUCCESS: u32 = 0x5555; // Equals `exit(0)`. qemu successful exit + +const EXIT_FAILURE_FLAG: u32 = 0x3333; +const EXIT_FAILURE: u32 = exit_code_encode(1); // Equals `exit(1)`. qemu failed exit +const EXIT_RESET: u32 = 0x7777; // qemu reset + +pub trait QEMUExit { + /// Exit with specified return code. + /// + /// Note: For `X86`, code is binary-OR'ed with `0x1` inside QEMU. + fn exit(&self, code: u32) -> !; + + /// Exit QEMU using `EXIT_SUCCESS`, aka `0`, if possible. + /// + /// Note: Not possible for `X86`. + fn exit_success(&self) -> !; + + /// Exit QEMU using `EXIT_FAILURE`, aka `1`. + fn exit_failure(&self) -> !; +} + + +/// RISCV64 configuration +pub struct RISCV64 { + /// Address of the sifive_test mapped device. + addr: u64, +} + +/// Encode the exit code using EXIT_FAILURE_FLAG. +const fn exit_code_encode(code: u32) -> u32 { + (code << 16) | EXIT_FAILURE_FLAG +} + +impl RISCV64 { + /// Create an instance. + pub const fn new(addr: u64) -> Self { + RISCV64 { addr } + } +} + +impl QEMUExit for RISCV64 { + /// Exit qemu with specified exit code. + fn exit(&self, code: u32) -> ! { + // If code is not a special value, we need to encode it with EXIT_FAILURE_FLAG. + let code_new = match code { + EXIT_SUCCESS | EXIT_FAILURE | EXIT_RESET => code, + _ => exit_code_encode(code), + }; + + unsafe { + asm!( + "sw {0}, 0({1})", + in(reg)code_new, in(reg)self.addr + ); + + // For the case that the QEMU exit attempt did not work, transition into an infinite + // loop. Calling `panic!()` here is unfeasible, since there is a good chance + // this function here is the last expression in the `panic!()` handler + // itself. This prevents a possible infinite loop. + loop { + asm!("wfi", options(nomem, nostack)); + } + } + } + + fn exit_success(&self) -> ! { + self.exit(EXIT_SUCCESS); + } + + fn exit_failure(&self) -> ! { + self.exit(EXIT_FAILURE); + } +} + +const VIRT_TEST: u64 =0x100000; + +pub const QEMU_EXIT_HANDLE: RISCV64 = RISCV64::new(VIRT_TEST); diff --git a/os/src/sbi.rs b/os/src/sbi.rs index 45f28112..8e0c8567 100644 --- a/os/src/sbi.rs +++ b/os/src/sbi.rs @@ -39,7 +39,9 @@ pub fn console_getchar() -> usize { sbi_call(SBI_CONSOLE_GETCHAR, 0, 0, 0) } +use crate::board::QEMUExit; pub fn shutdown(exit_code: usize) -> ! { - sbi_call(SBI_SHUTDOWN, exit_code, 0, 0); + //sbi_call(SBI_SHUTDOWN, exit_code, 0, 0); + crate::board::QEMU_EXIT_HANDLE.exit_failure(); panic!("It should shutdown!"); } diff --git a/os/src/task/mod.rs b/os/src/task/mod.rs index 07ebc81a..3c762b77 100644 --- a/os/src/task/mod.rs +++ b/os/src/task/mod.rs @@ -57,6 +57,8 @@ pub fn block_current_and_run_next() { schedule(task_cx_ptr); } +use crate::board::QEMUExit; + pub fn exit_current_and_run_next(exit_code: i32) { let task = take_current_task().unwrap(); let mut task_inner = task.inner_exclusive_access(); @@ -79,9 +81,11 @@ pub fn exit_current_and_run_next(exit_code: i32) { exit_code ); if exit_code != 0 { - crate::sbi::shutdown(255); //255 == -1 for err hint + //crate::sbi::shutdown(255); //255 == -1 for err hint + crate::board::QEMU_EXIT_HANDLE.exit_failure(); } else { - crate::sbi::shutdown(0); //0 for success hint + //crate::sbi::shutdown(0); //0 for success hint + crate::board::QEMU_EXIT_HANDLE.exit_success(); } } remove_from_pid2process(pid);