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