fix: Fix ACPI shutdown

This commit is contained in:
ParrotXray 2025-10-18 12:59:07 +08:00
parent d11c7842d4
commit 687c59f467
3 changed files with 179 additions and 115 deletions

View File

@ -1,22 +1,19 @@
use acpi::{aml, sdt, AcpiTables, Handle, Handler, PciAddress, PhysicalMapping};
use acpi::platform::{AcpiPlatform, interrupt::InterruptModel, PciConfigRegions};
use acpi::sdt::hpet::HpetInfo;
use acpi::rsdp::Rsdp;
use core::ptr::NonNull;
use core::mem;
use crate::kprintln;
use crate::{log_trace, log_debug, log_info, log_warn, log_error, log_fatal};
use super::power::{extract_power_info, store_power_info};
use super::{AcpiInfo, CureAcpiHandler};
use crate::hal::{cpu, io};
use super::*;
use crate::kprintln;
use crate::{log_debug, log_error, log_fatal, log_info, log_trace, log_warn};
use acpi::platform::{interrupt::InterruptModel, AcpiPlatform, PciConfigRegions};
use acpi::rsdp::Rsdp;
use acpi::sdt::hpet::HpetInfo;
use acpi::{aml, sdt, AcpiTables, Handle, Handler, PciAddress, PhysicalMapping};
use core::{mem, ptr::NonNull};
pub fn init(rsdp_addr: u64, physical_memory_offset: u64) -> Option<AcpiInfo> {
let handler = CureAcpiHandler::new(physical_memory_offset);
let rsdp_mapping = unsafe {
handler.map_physical_region::<Rsdp>(rsdp_addr as usize, mem::size_of::<Rsdp>())
};
let rsdp_mapping =
unsafe { handler.map_physical_region::<Rsdp>(rsdp_addr as usize, size_of::<Rsdp>()) };
let revision = rsdp_mapping.revision();
log_info!("ACPI Revision: {}", revision);
@ -62,8 +59,13 @@ pub fn init(rsdp_addr: u64, physical_memory_offset: u64) -> Option<AcpiInfo> {
let mut io_apics = alloc::vec::Vec::new();
for (i, io_apic) in apic.io_apics.iter().enumerate() {
log_info!("IO APIC {}: ID={}, Address={:#x}, GSI Base={}",
i, io_apic.id, io_apic.address, io_apic.global_system_interrupt_base);
log_info!(
"IO APIC {}: ID={}, Address={:#x}, GSI Base={}",
i,
io_apic.id,
io_apic.address,
io_apic.global_system_interrupt_base
);
io_apics.push((
io_apic.address as u64,
@ -89,7 +91,10 @@ pub fn init(rsdp_addr: u64, physical_memory_offset: u64) -> Option<AcpiInfo> {
log_info!("Base Address: {:#x}", hpet.base_address);
log_info!("Hardware Rev: {}", hpet.hardware_rev);
log_info!("Comparator Count: {}", hpet.num_comparators);
log_info!("Counter Size: {} bit", if hpet.main_counter_is_64bits { 64 } else { 32 });
log_info!(
"Counter Size: {} bit",
if hpet.main_counter_is_64bits { 64 } else { 32 }
);
log_info!("Legacy IRQ Capable: {}", hpet.legacy_irq_capable);
log_info!("PCI Vendor ID: {:#x}", hpet.pci_vendor_id);
true
@ -114,13 +119,12 @@ pub fn init(rsdp_addr: u64, physical_memory_offset: u64) -> Option<AcpiInfo> {
}
kprintln!();
log_info!("Extracting ACPI power management info...");
// if let Some(power_info) = extract_power_info(&platform.tables) {
// store_power_info(power_info);
// } else {
// log_warn!("Could not extract ACPI power info, shutdown may not work");
// }
log_info!("Extracting power management information...");
if let Some(power_info) = extract_power_info(&platform.tables, &handler) {
store_power_info(power_info);
} else {
log_warn!("Could not extract ACPI power info");
}
log_info!("ACPI initialized successfully!");
@ -142,7 +146,20 @@ pub fn print_info(info: &AcpiInfo) {
if let Some(boot_proc) = info.boot_processor {
log_info!("Boot Processor: UID {}", boot_proc);
}
log_info!("APIC: {}", if info.has_apic { "Available " } else { "Not available" });
log_info!("HPET: {}", if info.has_hpet { "Available " } else { "Not available" });
log_info!(
"APIC: {}",
if info.has_apic {
"Available "
} else {
"Not available"
}
);
log_info!(
"HPET: {}",
if info.has_hpet {
"Available "
} else {
"Not available"
}
);
}

View File

@ -1,14 +1,9 @@
pub mod power;
pub mod init;
pub mod power;
use acpi::{aml, sdt, AcpiTables, Handle, Handler, PciAddress, PhysicalMapping};
use acpi::sdt::hpet::HpetInfo;
use core::ptr::NonNull;
use core::mem;
use crate::kprintln;
use crate::{log_trace, log_debug, log_info, log_warn, log_error, log_fatal};
use crate::hal::{cpu, io};
use acpi::{aml, Handle, Handler, PciAddress, PhysicalMapping};
use core::ptr::NonNull;
#[derive(Clone, Copy)]
pub struct CureAcpiHandler {
@ -25,7 +20,7 @@ pub struct AcpiInfo {
pub io_apics: alloc::vec::Vec<(u64, u8, u32)>, // (address, id, gsi_base)
}
/// ACPI 關機所需的信息
/// Information required for ACPI shutdown
pub struct AcpiPowerInfo {
pub pm1a_control_block: u32,
pub pm1b_control_block: u32,
@ -184,7 +179,6 @@ impl Handler for CureAcpiHandler {
// TODO: 實作微秒級延遲
// 簡單的忙等待實作
cpu::cpu_pause(_microseconds * 1000);
}
fn sleep(&self, _milliseconds: u64) {
@ -208,4 +202,4 @@ impl Handler for CureAcpiHandler {
fn release(&self, _handle: Handle) {
// TODO: 實作 Mutex 釋放
}
}
}

View File

@ -1,15 +1,18 @@
use acpi::{aml, sdt, AcpiTables, Handle, Handler, PciAddress, PhysicalMapping};
use acpi::platform::{AcpiPlatform, interrupt::InterruptModel, PciConfigRegions};
use core::mem;
use crate::kprintln;
use crate::{log_trace, log_debug, log_info, log_warn, log_error, log_fatal};
use super::{AcpiPowerInfo, CureAcpiHandler, ACPI_POWER_INFO};
use crate::hal::{cpu, io};
use crate::kprintln;
use crate::mm::vma;
use super::*;
use crate::{log_debug, log_error, log_fatal, log_info, log_trace, log_warn};
use acpi::platform::{interrupt::InterruptModel, AcpiPlatform, PciConfigRegions};
use acpi::sdt::{SdtHeader, Signature};
use acpi::{aml, sdt, AcpiTables, Handle, Handler, PciAddress, PhysicalMapping};
use core::mem;
/// Extract shutdown information from ACPI table
pub fn extract_power_info(tables: &AcpiTables<CureAcpiHandler>) -> Option<AcpiPowerInfo> {
pub fn extract_power_info(
tables: &AcpiTables<CureAcpiHandler>,
handler: &CureAcpiHandler,
) -> Option<AcpiPowerInfo> {
log_info!("Extracting ACPI power management info...");
let fadt = match tables.find_table::<sdt::fadt::Fadt>() {
@ -20,18 +23,12 @@ pub fn extract_power_info(tables: &AcpiTables<CureAcpiHandler>) -> Option<AcpiPo
}
};
log_debug!("FADT found");
// Get the control block address and DSDT address from FADT
unsafe {
// Get the raw pointer of FADT to read the fields manually
let fadt_ptr = (&*fadt as *const sdt::fadt::Fadt) as *const u8;
// FADT structure offset
let pm1a_control_block = core::ptr::read_unaligned(fadt_ptr.add(64) as *const u32);
let pm1b_control_block = core::ptr::read_unaligned(fadt_ptr.add(68) as *const u32);
// Read DSDT address
let fadt_revision = core::ptr::read_unaligned(fadt_ptr.add(8) as *const u8);
let dsdt_address = if fadt_revision >= 2 {
let x_dsdt = core::ptr::read_unaligned(fadt_ptr.add(140) as *const u64);
@ -50,8 +47,7 @@ pub fn extract_power_info(tables: &AcpiTables<CureAcpiHandler>) -> Option<AcpiPo
}
log_debug!("DSDT address: {:#x}", dsdt_address);
// Parse the _S5 object
let (slp_typa, slp_typb) = match parse_s5_object(dsdt_address) {
let (slp_typa, slp_typb) = match parse_s5_object(dsdt_address, handler) {
Some(values) => values,
None => {
log_warn!("Could not parse _S5 object, using default values");
@ -82,88 +78,124 @@ pub fn extract_power_info(tables: &AcpiTables<CureAcpiHandler>) -> Option<AcpiPo
/// ...
/// })
/// ```
fn parse_s5_object(dsdt_phys_addr: u64) -> Option<(u16, u16)> {
let dsdt_virt_addr = vma::phys_to_virt(dsdt_phys_addr);
fn parse_s5_object(dsdt_phys_addr: u64, handler: &CureAcpiHandler) -> Option<(u16, u16)> {
log_debug!("Parsing DSDT at physical address {:#x}", dsdt_phys_addr);
// Map the DSDT header first to get the length
let dsdt_header_mapping = unsafe {
handler.map_physical_region::<SdtHeader>(
dsdt_phys_addr as usize,
size_of::<SdtHeader>(),
)
};
if dsdt_header_mapping.signature != Signature::DSDT {
log_error!("Invalid DSDT signature");
return None;
}
let dsdt_length = dsdt_header_mapping.length as usize;
log_debug!("DSDT length: {} bytes", dsdt_length);
// Release the header mapping
drop(dsdt_header_mapping);
// 映射完整的 DSDT
let dsdt_mapping =
unsafe { handler.map_physical_region::<u8>(dsdt_phys_addr as usize, dsdt_length) };
unsafe {
let dsdt_ptr = dsdt_virt_addr.as_ptr::<u8>();
let dsdt_ptr = dsdt_mapping.virtual_start.as_ptr();
let dsdt_data = core::slice::from_raw_parts(dsdt_ptr, dsdt_length);
// DSDT header
let signature = core::slice::from_raw_parts(dsdt_ptr, 4);
if signature != b"DSDT" {
log_error!("Invalid DSDT signature");
return None;
}
// Get DSDT length
let length = core::ptr::read_unaligned(dsdt_ptr.add(4) as *const u32);
log_debug!("DSDT length: {} bytes", length);
// Search for the "_S5_" string in DSDT
let dsdt_data = core::slice::from_raw_parts(dsdt_ptr, length as usize);
// Byte representation of "_S5_" in AML
// 在 DSDT 中搜索 "_S5_"
let s5_name = b"_S5_";
for i in 0..(dsdt_data.len() - 4) {
if &dsdt_data[i..i+4] == s5_name {
if &dsdt_data[i..i + 4] == s5_name {
log_debug!("Found _S5 at offset {:#x}", i);
// Parse the _S5 package
// Typical AML bytecode:
// Name(_S5, Package() {...})
// Or: 08 5F 53 35 5F 12 [pkg_length] [num_elements] ...
let debug_range = i..core::cmp::min(i + 32, dsdt_data.len());
log_debug!("_S5 region bytes: {:02x?}", &dsdt_data[debug_range]);
let mut offset = i + 4;
let mut offset = i + 4; // Skip "_S5_"
// Skip possible NameOp (0x08)
if offset < dsdt_data.len() && dsdt_data[offset] == 0x08 {
offset += 1;
}
// Find PackageOp (0x12)
while offset < dsdt_data.len() && dsdt_data[offset] != 0x12 {
offset += 1;
if offset - i > 16 {
// Skip any intermediate bytes and go straight to PackageOp
let search_limit = offset + 8;
while offset < search_limit && offset < dsdt_data.len() {
if dsdt_data[offset] == 0x12 {
log_debug!("Found PackageOp at offset {:#x}", offset);
break;
}
offset += 1;
}
if offset >= dsdt_data.len() {
if offset >= dsdt_data.len() || dsdt_data[offset] != 0x12 {
log_warn!("PackageOp not found after _S5");
continue;
}
offset += 1; // 跳過 PackageOp
offset += 1; // Skip PackageOp (0x12)
log_debug!("Found _S5 at offset {:#x}", i);
log_debug!("Found _S5 at offset {:#x}", i);
// Parse PkgLength
let pkg_length = parse_pkg_length(&dsdt_data[offset..]);
offset += get_pkg_length_size(&dsdt_data[offset..]);
let pkg_length_size = get_pkg_length_size(&dsdt_data[offset..]);
log_debug!("PkgLength: {}, size: {}", pkg_length, pkg_length_size);
offset += pkg_length_size;
// NumElements
if offset >= dsdt_data.len() {
log_warn!("Unexpected end of data");
continue;
}
let num_elements = dsdt_data[offset];
offset += 1;
log_debug!("Package length: {}, elements: {}", pkg_length, num_elements);
log_debug!("Package elements: {}", num_elements);
if num_elements < 2 {
log_warn!("_S5 package has less than 2 elements");
continue;
}
log_debug!(
"Current offset: {:#x}, next bytes: {:02x?}",
offset,
&dsdt_data[offset..core::cmp::min(offset + 8, dsdt_data.len())]
);
// Extract SLP_TYPa
log_debug!(
"Reading SLP_TYPa at offset {:#x}, byte: {:#x}",
offset,
dsdt_data[offset]
);
let slp_typa = parse_aml_integer(&dsdt_data[offset..]).unwrap_or(0);
offset += get_aml_integer_size(&dsdt_data[offset..]);
log_debug!("SLP_TYPa parsed: {:#x}", slp_typa);
let typa_size = get_aml_integer_size(&dsdt_data[offset..]);
offset += typa_size;
// Extract SLP_TYPb
log_debug!(
"Reading SLP_TYPb at offset {:#x}, byte: {:#x}",
offset,
dsdt_data[offset]
);
let slp_typb = parse_aml_integer(&dsdt_data[offset..]).unwrap_or(0);
log_debug!("SLP_TYPb parsed: {:#x}", slp_typb);
log_info!("Parsed _S5: SLP_TYPa={:#x}, SLP_TYPb={:#x}", slp_typa, slp_typb);
log_info!(
"Parsed _S5: SLP_TYPa={:#x}, SLP_TYPb={:#x}",
slp_typa,
slp_typb
);
return Some((slp_typa as u16, slp_typb as u16));
}
}
log_error!("_S5 object not found in DSDT");
None
}
@ -181,17 +213,21 @@ fn parse_pkg_length(data: &[u8]) -> usize {
match byte_count {
0 => (lead_byte & 0x3F) as usize,
1 => {
if data.len() < 2 { return 0; }
if data.len() < 2 {
return 0;
}
((lead_byte & 0x0F) as usize) | ((data[1] as usize) << 4)
}
2 => {
if data.len() < 3 { return 0; }
((lead_byte & 0x0F) as usize)
| ((data[1] as usize) << 4)
| ((data[2] as usize) << 12)
if data.len() < 3 {
return 0;
}
((lead_byte & 0x0F) as usize) | ((data[1] as usize) << 4) | ((data[2] as usize) << 12)
}
3 => {
if data.len() < 4 { return 0; }
if data.len() < 4 {
return 0;
}
((lead_byte & 0x0F) as usize)
| ((data[1] as usize) << 4)
| ((data[2] as usize) << 12)
@ -221,23 +257,34 @@ fn parse_aml_integer(data: &[u8]) -> Option<u64> {
match data[0] {
0x00 => Some(0), // ZeroOp
0x01 => Some(1), // OneOp
0x0A => { // BytePrefix
if data.len() < 2 { return None; }
0x0A => {
// BytePrefix
if data.len() < 2 {
return None;
}
Some(data[1] as u64)
}
0x0B => { // WordPrefix
if data.len() < 3 { return None; }
0x0B => {
// WordPrefix
if data.len() < 3 {
return None;
}
Some(u16::from_le_bytes([data[1], data[2]]) as u64)
}
0x0C => { // DWordPrefix
if data.len() < 5 { return None; }
0x0C => {
// DWordPrefix
if data.len() < 5 {
return None;
}
Some(u32::from_le_bytes([data[1], data[2], data[3], data[4]]) as u64)
}
0x0E => { // QWordPrefix
if data.len() < 9 { return None; }
0x0E => {
// QWordPrefix
if data.len() < 9 {
return None;
}
Some(u64::from_le_bytes([
data[1], data[2], data[3], data[4],
data[5], data[6], data[7], data[8],
data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8],
]))
}
_ => None,
@ -277,8 +324,11 @@ pub fn acpi_shutdown() -> ! {
unsafe {
let slp_cmd_a = (power_info.slp_typa << 10) | power_info.slp_en;
log_info!("Writing {:#x} to PM1a_CNT ({:#x})",
slp_cmd_a, power_info.pm1a_control_block);
log_info!(
"Writing {:#x} to PM1a_CNT ({:#x})",
slp_cmd_a,
power_info.pm1a_control_block
);
// Write to PM1a control register
io::io_port_ww(power_info.pm1a_control_block as u16, slp_cmd_a);
@ -286,8 +336,11 @@ pub fn acpi_shutdown() -> ! {
// If PM1b exists, also write
if power_info.pm1b_control_block != 0 {
let slp_cmd_b = (power_info.slp_typb << 10) | power_info.slp_en;
log_info!("Writing {:#x} to PM1b_CNT ({:#x})",
slp_cmd_b, power_info.pm1b_control_block);
log_info!(
"Writing {:#x} to PM1b_CNT ({:#x})",
slp_cmd_b,
power_info.pm1b_control_block
);
io::io_port_ww(power_info.pm1b_control_block as u16, slp_cmd_b);
}
@ -333,4 +386,4 @@ pub fn store_power_info(info: AcpiPowerInfo) {
ACPI_POWER_INFO = Some(info);
}
log_info!("ACPI power info stored successfully");
}
}