Rust ACPI解析库acpi的使用:实现高级配置与电源管理接口的硬件信息提取和系统控制

Rust ACPI解析库acpi的使用:实现高级配置与电源管理接口的硬件信息提取和系统控制

关于acpi库

acpi是一个用纯Rust编写的库,用于解析ACPI表和AML。它被设计为可以方便地在Rust引导程序和内核中使用。该库分为三个crate:

  • rsdp:解析RSDP并可以在BIOS平台上定位它。它不依赖alloc,因此适合在没有堆分配器的引导程序中使用
  • acpi:解析静态表(有用但功能还不完整)。可以在有分配器的环境中使用,也可以在没有分配器的环境中使用(但功能会减少)
  • aml:解析AML表(可能有用,但距离功能完整还很远)

还有一个acpi-dumper实用程序可以轻松转储平台的ACPI表(目前仅适用于Linux)。

安装

在项目目录中运行以下Cargo命令:

cargo add acpi

或者在Cargo.toml中添加以下行:

acpi = "5.2.0"

使用示例

下面是一个使用acpi库解析ACPI表的完整示例:

use acpi::{AcpiHandler, PhysicalMapping, AcpiTables};
use core::ptr::NonNull;

// 定义一个简单的AcpiHandler实现
struct MyAcpiHandler;

impl AcpiHandler for MyAcpiHandler {
    // 实现将物理地址映射到虚拟地址的方法
    unsafe fn map_physical_region<T>(
        &self,
        physical_address: usize,
        size: usize,
    ) -> PhysicalMapping<Self, T> {
        // 在实际系统中,你需要在这里实现物理地址到虚拟地址的映射
        // 这里只是一个示例实现
        let virtual_ptr = NonNull::new(physical_address as *mut T).unwrap();
        
        PhysicalMapping::new(
            physical_address,
            virtual_ptr,
            size,
            size,
            MyAcpiHandler,
        )
    }

    // 实现取消映射的方法
    fn unmap_physical_region<T>(_region: &PhysicalMapping<Self, T>) {
        // 在实际系统中,你需要在这里取消映射
    }
}

fn main() {
    // 获取RSDP地址 - 在实际系统中,你需要从正确的地址获取
    let rsdp_address = 0x00000000; // 替换为实际的RSDP地址
    
    // 创建我们的AcpiHandler
    let handler = MyAcpiHandler;
    
    // 解析ACPI表
    let tables = unsafe { AcpiTables::from_rsdp(handler, rsdp_address) }.unwrap();
    
    // 获取FADT (Fixed ACPI Description Table)
    let fadt = tables.fadt().unwrap();
    println!("FADT: {:?}", fadt);
    
    // 获取MADT (Multiple APIC Description Table)
    if let Ok(madt) = tables.madt() {
        println!("MADT: {:?}", madt);
        
        // 遍历MADT中的条目
        for entry in madt.entries {
            println!("MADT Entry: {:?}", entry);
        }
    }
    
    // 获取HPET (High Precision Event Timer)表
    if let Ok(hpet) = tables.hpet() {
        println!("HPET: {:?}", hpet);
    }
}

完整示例demo

以下是一个更完整的示例,展示了如何使用acpi库来获取系统电源管理信息:

use acpi::{AcpiHandler, PhysicalMapping, AcpiTables, PlatformInfo};
use core::ptr::NonNull;

// 实现自定义的AcpiHandler
struct KernelAcpiHandler;

impl AcpiHandler for KernelAcpiHandler {
    unsafe fn map_physical_region<T>(
        &self,
        physical_address: usize,
        size: usize,
    ) -> PhysicalMapping<Self, T> {
        // 在实际内核中,这里应该实现物理地址到虚拟地址的映射
        let virtual_ptr = NonNull::new(physical_address as *mut T).unwrap();
        
        PhysicalMapping::new(
            physical_address,
            virtual_ptr,
            size,
            size,
            KernelAcpiHandler,
        )
    }

    fn unmap_physical_region<T>(_region: &PhysicalMapping<Self, T>) {
        // 在实际内核中,这里应该取消映射
    }
}

fn main() -> Result<(), acpi::AcpiError> {
    // 在实际系统中,应该从正确的地址获取RSDP
    let rsdp_address = 0x00000000;
    
    let handler = KernelAcpiHandler;
    
    // 解析ACPI表
    let tables = unsafe { AcpiTables::from_rsdp(handler, rsdp_address)? };
    
    // 获取平台信息
    let platform_info = tables.platform_info()?;
    println!("平台中断模型: {:?}", platform_info.interrupt_model);
    
    // 获取FADT表
    let fadt = tables.fadt()?;
    println!("ACPI硬件信息:");
    println!("  PM定时器块: 0x{:X}", fadt.pm_timer_block);
    println!("  PM1a控制块: 0x{:X}", fadt.pm1a_control_block);
    println!("  PM1b控制块: 0x{:X}", fadt.pm1b_control_block);
    
    // 获取MADT表(APIC信息)
    if let Ok(madt) = tables.madt() {
        println!("发现 {} 个CPU核心", madt.processor_local_apics().count());
        
        // 遍历处理器本地APIC
        for lapic in madt.processor_local_apics() {
            println!("处理器ID: {}, APIC ID: {}, 启用: {}",
                lapic.processor_id,
                lapic.local_apic_id,
                lapic.enabled
            );
        }
    }
    
    // 获取电源管理定时器频率
    if let Some(pm_timer_freq) = fadt.pm_timer_frequency() {
        println!("PM定时器频率: {}Hz", pm_timer_freq);
    }
    
    Ok(())
}

贡献

欢迎贡献!你可以:

  • 编写代码 - ACPI规范非常庞大,肯定有很多我们还不支持的功能!
  • 改进我们的文档!
  • 在内核中使用这些crate并提交错误报告和功能请求!

许可证

该项目采用双重许可:

  • Apache许可证,版本2.0
  • MIT许可证

除非你明确说明,否则根据Apache-2.0许可证提交的任何贡献都将按照上述方式双重许可,无需任何附加条款或条件。


1 回复

Rust ACPI解析库acpi的使用:实现高级配置与电源管理接口的硬件信息提取和系统控制

介绍

acpi是一个Rust库,用于解析和操作ACPI(高级配置与电源管理接口)表。ACPI是操作系统用来发现和配置计算机硬件组件、执行电源管理(如睡眠、休眠)以及监控系统状态的开放标准。

这个库提供了:

  • ACPI表结构的解析
  • 系统描述表(SDT)的遍历
  • 电源管理功能的访问
  • 硬件信息的提取

使用方法

基本设置

首先在Cargo.toml中添加依赖:

[dependencies]
acpi = "4.0"

初始化ACPI

use acpi::{AcpiTables, PlatformInfo};

fn main() {
    // 获取ACPI表
    let tables = unsafe { AcpiTables::from_rsdp(rsdp_address) };
    
    // 或者让库自动查找RSDP
    let tables = unsafe { AcpiTables::search_for_rsdp_bios() }.unwrap();
}

遍历ACPI表

use acpi::AcpiTables;

fn print_acpi_tables(tables: &AcpiTables) {
    for table in tables.iter() {
        println!("Found ACPI table: {:?}", table.signature);
    }
}

解析特定表

use acpi::{AcpiTables, sdt::Sdt};

fn parse_fadt(tables: &AcpiTables) {
    if let Some(fadt) = tables.find_table::<Sdt>(b"FACP") {
        println!("FADT found at: {:?}", fadt);
    }
}

电源管理功能

use acpi::{AcpiTables, fadt::Fadt};

fn check_sleep_states(tables: &AcpiTables) {
    if let Some(fadt) = tables.find_table::<Fadt>(b"FACP") {
        println!("Supported sleep states: S1: {}, S3: {}, S4: {}, S5: {}",
            fadt.s1_supported(),
            fadt.s3_supported(),
            fadt.s4_supported(),
            fadt.s5_supported()
        );
    }
}

访问DSDT

use acpi::{AcpiTables, fadt::Fadt};

fn parse_dsdt(tables: &AcpiTables) {
    if let Some(fadt) = tables.find_table::<Fadt>(b"FACP") {
        let dsdt = tables.load_table(fadt.dsdt_address()).unwrap();
        println!("DSDT: {:?}", dsdt);
    }
}

完整示例:列出所有ACPI表和电源状态

use acpi::{AcpiTables, fadt::Fadt, PlatformInfo};

fn main() {
    // 获取ACPI表
    let tables = unsafe { AcpiTables::search_for_rsdp_bios() }.unwrap();
    
    // 打印所有表
    println!("System ACPI Tables:");
    for table in tables.iter() {
        println!("  - {:?}", table.signature);
    }
    
    // 检查电源状态
    if let Some(fadt) = tables.find_table::<Fadt>(b"FACP") {
        println!("\nSupported Power States:");
        println!("S1 (CPU停止): {}", fadt.s1_supported());
        println!("S2 (CPU断电): {}", fadt.s2_supported());
        println!("S3 (挂起到内存): {}", fadt.s3_supported());
        println!("S4 (挂起到磁盘): {}", fadt.s4_supported());
        println!("S5 (软关机): {}", fadt.s5_supported());
    }
}

完整示例代码

以下是一个更完整的示例,展示了如何使用acpi库提取系统硬件信息和电源管理功能:

use acpi::{AcpiTables, fadt::Fadt, PlatformInfo};
use acpi::madt::Madt;
use acpi::hpet::Hpet;

fn main() {
    // 初始化ACPI表
    let tables = unsafe { AcpiTables::search_for_rsdp_bios() }
        .expect("Failed to find ACPI tables");
    
    // 1. 打印所有ACPI表
    println!("Detected ACPI Tables:");
    for table in tables.iter() {
        println!(" - {:?} ({} bytes)", table.signature, table.length);
    }
    
    // 2. 解析FADT获取电源管理信息
    if let Some(fadt) = tables.find_table::<Fadt>(b"FACP") {
        println!("\nPower Management Features:");
        println!("S1 Sleep Supported: {}", fadt.s1_supported());
        println!("S3 Sleep (Suspend-to-RAM): {}", fadt.s3_supported());
        println!("S4 Sleep (Suspend-to-Disk): {}", fadt.s4_supported());
        println!("S5 Soft Off: {}", fadt.s5_supported());
        println!("Lid Present: {}", fadt.lid_present());
    }
    
    // 3. 解析MADT获取CPU和中断控制器信息
    if let Some(madt) = tables.find_table::<Madt>(b"APIC") {
        println!("\nCPU and Interrupt Controller Information:");
        println!("Local APIC Address: {:#X}", madt.local_apic_address());
        println!("Processor Count: {}", madt.processor_iter().count());
        println!("IOAPIC Count: {}", madt.io_apic_iter().count());
    }
    
    // 4. 解析HPET表获取高精度定时器信息
    if let Some(hpet) = tables.find_table::<Hpet>(b"HPET") {
        println!("\nHigh Precision Event Timer:");
        println!("Base Address: {:#X}", hpet.base_address());
        println!("Minimum Clock Ticks: {}", hpet.minimum_clock_ticks());
        println!("Legacy Replacement: {}", hpet.legacy_replacement());
    }
    
    // 5. 加载并解析DSDT
    if let Some(fadt) = tables.find_table::<Fadt>(b"FACP") {
        let dsdt = tables.load_table(fadt.dsdt_address())
            .expect("Failed to load DSDT");
        println!("\nDSDT Table:");
        println!("Length: {} bytes", dsdt.length());
        // 这里可以添加更详细的DSDT解析代码
    }
}

注意事项

  1. 这个库需要unsafe操作来访问系统ACPI表
  2. 使用前确保了解ACPI规范和安全影响
  3. 某些功能需要特定的平台支持
  4. 在生产环境中使用前应进行充分测试

这个库为系统级开发者提供了强大的ACPI操作能力,特别适合操作系统开发、硬件监控工具和电源管理应用。

回到顶部