Refactor ACPI, implement poweroff correctly using the DSDT in ACPI

This commit is contained in:
Jeremy Soller
2017-03-19 16:45:19 -06:00
parent e726234bc6
commit 228cd79cd4
5 changed files with 156 additions and 70 deletions

View File

@@ -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))
}
}

View File

@@ -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)]

View File

@@ -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.

View File

@@ -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)]

View File

@@ -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(); }