WIP: Fix userspace ACPI shutdown.
This commit is contained in:
@@ -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() {
|
||||
|
||||
@@ -34,7 +34,7 @@ pub enum Status {
|
||||
Runnable,
|
||||
Blocked,
|
||||
Stopped(usize),
|
||||
Exited(usize)
|
||||
Exited(usize),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
|
||||
@@ -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<ContextId> {
|
||||
|
||||
Reference in New Issue
Block a user