diff --git a/src/arch/x86_64/stop.rs b/src/arch/x86_64/stop.rs index f915d0a..eadc8c3 100644 --- a/src/arch/x86_64/stop.rs +++ b/src/arch/x86_64/stop.rs @@ -27,29 +27,47 @@ pub unsafe extern fn kreset() -> ! { unreachable!(); } -#[no_mangle] -pub unsafe extern fn kstop() -> ! { - println!("kstop"); +#[cfg(feature = "acpi")] +fn userspace_acpi_shutdown() { + log::info!("Notifying any potential ACPI driver"); + // Tell whatever driver that handles ACPI, that it should enter the S5 state (i.e. + // shutdown). + if ! acpi::register_kstop() { + // There was no context to switch to. + log::info!("No ACPI driver was alive to handle shutdown."); + return; + } + log::info!("Waiting one second for ACPI driver to run the shutdown sequence."); + let (initial_s, initial_ns) = time::monotonic(); - // FIXME: RPC into userspace, maybe allowing the kernel ACPI scheme to support e.g. registering - // an event queue, so that a special file can only be read/written when about to shut down. + // Since this driver is a userspace process, and we do not use any magic like directly + // context switching, we have to wait for the userspace driver to complete, with a timeout. + // + // We switch context, and wait for one second. + loop { + // TODO: Switch directly to whichever process is handling the kstop pipe. We would add an + // event flag like EVENT_DIRECT, which has already been suggested for IRQs. + // TODO: Waitpid with timeout? Because, what if the ACPI driver would crash? + let _ = unsafe { context::switch() }; + let (current_s, current_ns) = time::monotonic(); - #[cfg(feature = "acpi")] - { - // Tell whatever driver that handles ACPI, that it should enter the S5 state (i.e. - // shutdown). - acpi::register_kstop(); + let diff_s = current_s - initial_s; + let diff_part_ns = current_ns - initial_ns; + let diff_ns = diff_s * 1_000_000_000 + diff_part_ns; - // Since this driver is a userspace process, and we do not use any magic like directly - // context switching, we have to wait for the userspace driver to complete, with a timeout. - // - // We switch context, and wait for one second. - while time::monotonic().0 < 1 { - if ! context::switch() { - break; - } + if diff_ns > 1_000_000_000 { + log::info!("Timeout reached, thus falling back to other shutdown methods."); + return; } } +} + +#[no_mangle] +pub unsafe extern fn kstop() -> ! { + log::info!("Running kstop()"); + + #[cfg(feature = "acpi")] + userspace_acpi_shutdown(); // Magic shutdown code for bochs and qemu (older versions). for c in "Shutdown".bytes() { diff --git a/src/context/context.rs b/src/context/context.rs index 5d6b444..299e303 100644 --- a/src/context/context.rs +++ b/src/context/context.rs @@ -34,7 +34,7 @@ pub enum Status { Runnable, Blocked, Stopped(usize), - Exited(usize) + Exited(usize), } #[derive(Copy, Clone, Debug)] diff --git a/src/syscall/process.rs b/src/syscall/process.rs index db2d2bd..f62721a 100644 --- a/src/syscall/process.rs +++ b/src/syscall/process.rs @@ -1128,6 +1128,8 @@ pub fn fexec(fd: FileHandle, arg_ptrs: &[[usize; 2]], var_ptrs: &[[usize; 2]]) - pub fn exit(status: usize) -> ! { ptrace::breakpoint_callback(PTRACE_STOP_EXIT, Some(ptrace_event!(PTRACE_STOP_EXIT, status))); + let pid; + { let context_lock = { let contexts = context::contexts(); @@ -1136,7 +1138,7 @@ pub fn exit(status: usize) -> ! { }; let mut close_files = Vec::new(); - let pid = { + pid = { let mut context = context_lock.write(); { let mut lock = context.files.write(); @@ -1148,6 +1150,22 @@ pub fn exit(status: usize) -> ! { context.id }; + // TODO: Find a better way to implement this, perhaps when the init process calls exit. + if pid == ContextId::from(1) { + println!("Main kernel thread exited with status {:X}", status); + + extern { + fn kreset() -> !; + fn kstop() -> !; + } + + if status == SIGTERM { + unsafe { kreset(); } + } else { + unsafe { kstop(); } + } + } + // Files must be closed while context is valid so that messages can be passed for (_fd, file_opt) in close_files.drain(..).enumerate() { if let Some(file) = file_opt { @@ -1214,26 +1232,10 @@ pub fn exit(status: usize) -> ! { // Alert any tracers waiting of this process ptrace::close_tracee(pid); - - if pid == ContextId::from(1) { - println!("Main kernel thread exited with status {:X}", status); - - extern { - fn kreset() -> !; - fn kstop() -> !; - } - - if status == SIGTERM { - unsafe { kreset(); } - } else { - unsafe { kstop(); } - } - } } let _ = unsafe { context::switch() }; - - unreachable!(); + unreachable!() } pub fn getpid() -> Result {