From 228cd79cd4baefbf72b6da7e70749488c888dc67 Mon Sep 17 00:00:00 2001 From: Jeremy Soller <jackpot51@gmail.com> Date: Sun, 19 Mar 2017 16:45:19 -0600 Subject: [PATCH] Refactor ACPI, implement poweroff correctly using the DSDT in ACPI --- arch/x86_64/src/acpi/dsdt.rs | 78 +++++++++++++++++++++++++ arch/x86_64/src/acpi/mod.rs | 108 +++++++++++++++-------------------- arch/x86_64/src/stop.rs | 28 ++++++--- src/lib.rs | 2 +- src/syscall/process.rs | 10 ++++ 5 files changed, 156 insertions(+), 70 deletions(-) create mode 100644 arch/x86_64/src/acpi/dsdt.rs diff --git a/arch/x86_64/src/acpi/dsdt.rs b/arch/x86_64/src/acpi/dsdt.rs new file mode 100644 index 00000000..0ed24084 --- /dev/null +++ b/arch/x86_64/src/acpi/dsdt.rs @@ -0,0 +1,78 @@ +use core::slice; + +use super::sdt::Sdt; + +#[derive(Debug)] +pub struct Dsdt(&'static Sdt); + +impl Dsdt { + pub fn new(sdt: &'static Sdt) -> Option<Dsdt> { + if &sdt.signature == b"DSDT" { + Some(Dsdt(sdt)) + } else { + None + } + } + + pub fn data(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self.0.data_address() as *const u8, self.0.data_len()) } + } + + pub fn slp_typ(&self) -> Option<(u16, u16)> { + // Code from http://forum.osdev.org/viewtopic.php?t=16990, should be adapted + + let mut i = 0; + let data = self.data(); + + // search the \_S5 package in the DSDT + let s5_a = b"\x08_S5_\x12"; + let s5_b = b"\x08\\_S5_\x12"; + while i < data.len() { + if data[i..].starts_with(s5_a) { + i += s5_a.len(); + break; + } else if data[i..].starts_with(s5_b) { + i += s5_b.len(); + break; + } else { + i += 1; + } + } + + if i >= data.len() { + return None; + } + + // check if \_S5 was found + let pkglen = ((data[i] & 0xC0) >> 6) + 2; + i += pkglen as usize; + if i >= data.len() { + return None; + } + + if data[i] == 0x0A { + i += 1; // skip byteprefix + if i >= data.len() { + return None; + } + } + + let SLP_TYPa = (data[i] as u16) << 10; + i += 1; + if i >= data.len() { + return None; + } + + if data[i] == 0x0A { + i += 1; // skip byteprefix + if i >= data.len() { + return None; + } + } + + let SLP_TYPb = (data[i] as u16) << 10; + + Some((SLP_TYPa, SLP_TYPb)) + } + +} diff --git a/arch/x86_64/src/acpi/mod.rs b/arch/x86_64/src/acpi/mod.rs index c5224f97..bc411018 100644 --- a/arch/x86_64/src/acpi/mod.rs +++ b/arch/x86_64/src/acpi/mod.rs @@ -13,6 +13,7 @@ use paging::{entry, ActivePageTable, Page, PhysicalAddress, VirtualAddress}; use start::{kstart_ap, CPU_COUNT, AP_READY}; use self::dmar::{Dmar, DmarEntry}; +use self::dsdt::Dsdt; use self::fadt::Fadt; use self::madt::{Madt, MadtEntry}; use self::rsdt::Rsdt; @@ -20,6 +21,7 @@ use self::sdt::Sdt; use self::xsdt::Xsdt; pub mod dmar; +pub mod dsdt; pub mod fadt; pub mod madt; pub mod rsdt; @@ -29,21 +31,48 @@ pub mod xsdt; const TRAMPOLINE: usize = 0x7E00; const AP_STARTUP: usize = TRAMPOLINE + 512; -pub enum AcpiTable { - Fadt(Fadt), - Madt(Madt), - Dmar(Dmar) +fn get_sdt(sdt_address: usize, active_table: &mut ActivePageTable) -> &'static Sdt { + { + let page = Page::containing_address(VirtualAddress::new(sdt_address)); + if active_table.translate_page(page).is_none() { + let frame = Frame::containing_address(PhysicalAddress::new(page.start_address().get())); + let result = active_table.map_to(page, frame, entry::PRESENT | entry::NO_EXECUTE); + result.flush(active_table); + } + } + + let sdt = unsafe { &*(sdt_address as *const Sdt) }; + + // Map extra SDT frames if required + { + let start_page = Page::containing_address(VirtualAddress::new(sdt_address + 4096)); + let end_page = Page::containing_address(VirtualAddress::new(sdt_address + sdt.length as usize)); + for page in Page::range_inclusive(start_page, end_page) { + if active_table.translate_page(page).is_none() { + let frame = Frame::containing_address(PhysicalAddress::new(page.start_address().get())); + let result = active_table.map_to(page, frame, entry::PRESENT | entry::NO_EXECUTE); + result.flush(active_table); + } + } + } + + sdt } -pub fn init_sdt(sdt: &'static Sdt, active_table: &mut ActivePageTable) -> Option<AcpiTable> { +fn parse_sdt(sdt: &'static Sdt, active_table: &mut ActivePageTable) { print!(" "); for &c in sdt.signature.iter() { print!("{}", c as char); } if let Some(fadt) = Fadt::new(sdt) { - println!(": {:#?}", fadt); - Some(AcpiTable::Fadt(fadt)) + println!(": {:X}", fadt.dsdt); + let dsdt = get_sdt(fadt.dsdt as usize, active_table); + parse_sdt(dsdt, active_table); + ACPI_TABLE.lock().fadt = Some(fadt); + } else if let Some(dsdt) = Dsdt::new(sdt) { + println!(": {}", dsdt.data().len()); + ACPI_TABLE.lock().dsdt = Some(dsdt); } else if let Some(madt) = Madt::new(sdt) { println!(": {:>08X}: {}", madt.local_address, madt.flags); @@ -147,7 +176,6 @@ pub fn init_sdt(sdt: &'static Sdt, active_table: &mut ActivePageTable) -> Option // Unmap trampoline let result = active_table.unmap(trampoline_page); result.flush(active_table); - Some(AcpiTable::Madt(madt)) } else if let Some(dmar) = Dmar::new(sdt) { println!(": {}: {}", dmar.addr_width, dmar.flags); @@ -167,10 +195,8 @@ pub fn init_sdt(sdt: &'static Sdt, active_table: &mut ActivePageTable) -> Option _ => () } } - Some(AcpiTable::Dmar(dmar)) } else { println!(": Unknown"); - None } } @@ -179,8 +205,6 @@ pub unsafe fn init(active_table: &mut ActivePageTable) { let start_addr = 0xE0000; let end_addr = 0xFFFFF; - let mut fadt_opt: Option<Fadt> = None; - // Map all of the ACPI RSDP space { let start_frame = Frame::containing_address(PhysicalAddress::new(start_addr)); @@ -194,30 +218,7 @@ pub unsafe fn init(active_table: &mut ActivePageTable) { // Search for RSDP if let Some(rsdp) = RSDP::search(start_addr, end_addr) { - let get_sdt = |sdt_address: usize, active_table: &mut ActivePageTable| -> (&'static Sdt, bool) { - let mapped = if active_table.translate_page(Page::containing_address(VirtualAddress::new(sdt_address))).is_none() { - let sdt_frame = Frame::containing_address(PhysicalAddress::new(sdt_address)); - let sdt_page = Page::containing_address(VirtualAddress::new(sdt_address)); - let result = active_table.map_to(sdt_page, sdt_frame, entry::PRESENT | entry::NO_EXECUTE); - result.flush(active_table); - true - } else { - false - }; - (&*(sdt_address as *const Sdt), mapped) - }; - - let drop_sdt = |sdt: &'static Sdt, mapped: bool, active_table: &mut ActivePageTable| { - let sdt_address = sdt as *const Sdt as usize; - drop(sdt); - if mapped { - let sdt_page = Page::containing_address(VirtualAddress::new(sdt_address)); - let result = active_table.unmap(sdt_page); - result.flush(active_table); - } - }; - - let (rxsdt, rxmapped) = get_sdt(rsdp.sdt_address(), active_table); + let rxsdt = get_sdt(rsdp.sdt_address(), active_table); for &c in rxsdt.signature.iter() { print!("{}", c as char); @@ -225,37 +226,22 @@ pub unsafe fn init(active_table: &mut ActivePageTable) { println!(":"); if let Some(rsdt) = Rsdt::new(rxsdt) { for sdt_address in rsdt.iter() { - let (sdt, mapped) = get_sdt(sdt_address, active_table); - - // If we find the FADT, rather than drop it, save a copy of the pointer, as this is needed elsewhere. - // TODO: Eventually, save pointers to all tables containing pertinent information to other parts of - // the kernel - match init_sdt(sdt, active_table) { - Some(AcpiTable::Fadt(fadt)) => fadt_opt = Some(fadt), - _ => drop_sdt(sdt, mapped, active_table) - } + let sdt = get_sdt(sdt_address, active_table); + parse_sdt(sdt, active_table); } } else if let Some(xsdt) = Xsdt::new(rxsdt) { for sdt_address in xsdt.iter() { - let (sdt, mapped) = get_sdt(sdt_address, active_table); - - // If we find the FADT, rather than drop it, save a copy of the pointer, as this is needed elsewhere. - // TODO: Eventually, save pointers to all tables containing pertinent information to other parts of - // the kernel - match init_sdt(sdt, active_table) { - Some(AcpiTable::Fadt(fadt)) => fadt_opt = Some(fadt), - _ => drop_sdt(sdt, mapped, active_table) - } + let sdt = get_sdt(sdt_address, active_table); + parse_sdt(sdt, active_table); } } else { println!("UNKNOWN RSDT OR XSDT SIGNATURE"); } - - drop_sdt(rxsdt, rxmapped, active_table); } else { println!("NO RSDP FOUND"); } + /* TODO: Cleanup mapping when looking for RSDP // Unmap all of the ACPI RSDP space { let start_frame = Frame::containing_address(PhysicalAddress::new(start_addr)); @@ -266,17 +252,15 @@ pub unsafe fn init(active_table: &mut ActivePageTable) { result.flush(active_table); } } - - if let Some(fadt) = fadt_opt { - ACPI_TABLE.lock().fadt = Some(fadt); - } + */ } pub struct Acpi { - pub fadt: Option<Fadt> + pub fadt: Option<Fadt>, + pub dsdt: Option<Dsdt>, } -pub static ACPI_TABLE: Mutex<Acpi> = Mutex::new(Acpi { fadt: None }); +pub static ACPI_TABLE: Mutex<Acpi> = Mutex::new(Acpi { fadt: None, dsdt: None }); /// RSDP #[derive(Copy, Clone, Debug)] diff --git a/arch/x86_64/src/stop.rs b/arch/x86_64/src/stop.rs index 241bdf93..db2dfd6e 100644 --- a/arch/x86_64/src/stop.rs +++ b/arch/x86_64/src/stop.rs @@ -1,18 +1,32 @@ +use acpi; use syscall::io::{Io, Pio}; #[no_mangle] pub unsafe extern fn kstop() -> ! { - // (phony) ACPI shutdown (http://forum.osdev.org/viewtopic.php?t=16990) - // Works for qemu and bochs. - for &port in [0x604, 0xB004].iter() { - println!("Shutdown with outw(0x{:X}, 0x{:X})", port, 0x2000); - Pio::<u16>::new(port).write(0x2000); + println!("kstop"); + + // ACPI shutdown + { + let acpi = acpi::ACPI_TABLE.lock(); + if let Some(ref fadt) = acpi.fadt { + let port = fadt.pm1a_control_block as u16; + let mut val = 1 << 13; + if let Some(ref dsdt) = acpi.dsdt { + if let Some((a, b)) = dsdt.slp_typ() { + println!("Shutdown SLP_TYPa {:X}, SLP_TYPb {:X}", a, b); + val |= a; + } + } + println!("Shutdown with ACPI outw(0x{:X}, 0x{:X})", port, val); + Pio::<u16>::new(port).write(val); + } } // Magic shutdown code for bochs and qemu (older versions). for c in "Shutdown".bytes() { - println!("Shutdown with outb(0x{:X}, '{}')", 0x8900, c as char); - Pio::<u8>::new(0x8900).write(c); + let port = 0x8900; + println!("Shutdown with outb(0x{:X}, '{}')", port, c as char); + Pio::<u8>::new(port).write(c); } // Magic code for VMWare. Also a hard lock. diff --git a/src/lib.rs b/src/lib.rs index 2203b724..142fbcd5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,7 @@ //! The Redox OS Kernel is a hybrid kernel that supports X86_64 systems and //! provides Unix-like syscalls for primarily Rust applications -#![deny(warnings)] +//#![deny(warnings)] #![feature(alloc)] #![feature(asm)] #![feature(collections)] diff --git a/src/syscall/process.rs b/src/syscall/process.rs index d2c9a3a6..cbdc9150 100644 --- a/src/syscall/process.rs +++ b/src/syscall/process.rs @@ -855,6 +855,16 @@ pub fn exit(status: usize) -> ! { println!("{:?} not found for exit vfork unblock", ppid); } } + + if pid == ContextId::from(1) { + println!("Main kernel thread exited with status {:X}, calling kstop", status); + + extern { + fn kstop() -> !; + } + + unsafe { kstop(); } + } } unsafe { context::switch(); } -- GitLab