mirror of
https://github.com/DragonOS-Community/DragonOS.git
synced 2025-06-12 14:16:47 +00:00
224 lines
6.4 KiB
Rust
224 lines
6.4 KiB
Rust
use core::{fmt::Debug, hint::spin_loop, ptr::NonNull};
|
||
|
||
use acpi::{AcpiHandler, AcpiTables, PlatformInfo};
|
||
use alloc::{string::ToString, sync::Arc};
|
||
use log::{error, info};
|
||
|
||
use crate::{
|
||
arch::MMArch,
|
||
driver::base::firmware::sys_firmware_kset,
|
||
libs::align::{page_align_down, page_align_up, AlignedBox},
|
||
mm::{
|
||
mmio_buddy::{mmio_pool, MMIOSpaceGuard},
|
||
MemoryManagementArch, PhysAddr, VirtAddr,
|
||
},
|
||
};
|
||
use system_error::SystemError;
|
||
|
||
use super::base::kset::KSet;
|
||
|
||
extern crate acpi;
|
||
|
||
pub mod bus;
|
||
mod c_adapter;
|
||
pub mod glue;
|
||
pub mod pmtmr;
|
||
mod sysfs;
|
||
|
||
static mut __ACPI_TABLE: Option<acpi::AcpiTables<AcpiHandlerImpl>> = None;
|
||
/// `/sys/firmware/acpi`的kset
|
||
static mut ACPI_KSET_INSTANCE: Option<Arc<KSet>> = None;
|
||
|
||
static mut RSDP_TMP_BOX: Option<AlignedBox<[u8; 4096], 4096>> = None;
|
||
|
||
#[inline(always)]
|
||
pub fn acpi_manager() -> &'static AcpiManager {
|
||
&AcpiManager
|
||
}
|
||
|
||
#[inline(always)]
|
||
pub fn acpi_kset() -> Arc<KSet> {
|
||
unsafe { ACPI_KSET_INSTANCE.clone().unwrap() }
|
||
}
|
||
|
||
#[derive(Debug)]
|
||
pub struct AcpiManager;
|
||
|
||
impl AcpiManager {
|
||
/// 初始化ACPI
|
||
///
|
||
/// ## 参数
|
||
///
|
||
/// - `rsdp_vaddr1`: RSDP(v1)的虚拟地址
|
||
/// - `rsdp_vaddr2`: RSDP(v2)的虚拟地址
|
||
///
|
||
///
|
||
/// ## 参考资料
|
||
///
|
||
/// https://code.dragonos.org.cn/xref/linux-6.1.9/drivers/acpi/bus.c#1390
|
||
pub fn init(&self, rsdp_vaddr1: u64, rsdp_vaddr2: u64) -> Result<(), SystemError> {
|
||
info!("Initializing Acpi Manager...");
|
||
|
||
// 初始化`/sys/firmware/acpi`的kset
|
||
let kset = KSet::new("acpi".to_string());
|
||
kset.register(Some(sys_firmware_kset()))?;
|
||
unsafe {
|
||
ACPI_KSET_INSTANCE = Some(kset.clone());
|
||
}
|
||
self.map_tables(rsdp_vaddr1, rsdp_vaddr2)?;
|
||
self.bus_init()?;
|
||
info!("Acpi Manager initialized.");
|
||
return Ok(());
|
||
}
|
||
|
||
fn map_tables(&self, rsdp_vaddr1: u64, rsdp_vaddr2: u64) -> Result<(), SystemError> {
|
||
let rsdp_paddr1 = Self::rsdp_paddr(rsdp_vaddr1);
|
||
let res1 = unsafe { acpi::AcpiTables::from_rsdp(AcpiHandlerImpl, rsdp_paddr1.data()) };
|
||
let e1;
|
||
match res1 {
|
||
// 如果rsdpv1能够获取到acpi_table,则就用该表,不用rsdpv2了
|
||
Ok(acpi_table) => {
|
||
Self::set_acpi_table(acpi_table);
|
||
return Ok(());
|
||
}
|
||
Err(e) => {
|
||
e1 = e;
|
||
Self::drop_rsdp_tmp_box();
|
||
}
|
||
}
|
||
|
||
let rsdp_paddr2 = Self::rsdp_paddr(rsdp_vaddr2);
|
||
let res2 = unsafe { acpi::AcpiTables::from_rsdp(AcpiHandlerImpl, rsdp_paddr2.data()) };
|
||
match res2 {
|
||
Ok(acpi_table) => {
|
||
Self::set_acpi_table(acpi_table);
|
||
}
|
||
// 如果rsdpv1和rsdpv2都无法获取到acpi_table,说明有问题,打印报错信息后进入死循环
|
||
Err(e2) => {
|
||
error!("acpi_init(): failed to parse acpi tables, error: (rsdpv1: {:?}) or (rsdpv2: {:?})", e1, e2);
|
||
Self::drop_rsdp_tmp_box();
|
||
loop {
|
||
spin_loop();
|
||
}
|
||
}
|
||
}
|
||
|
||
return Ok(());
|
||
}
|
||
|
||
/// 通过RSDP虚拟地址获取RSDP物理地址
|
||
///
|
||
/// ## 参数
|
||
///
|
||
/// - `rsdp_vaddr`: RSDP的虚拟地址
|
||
///
|
||
/// ## 返回值
|
||
///
|
||
/// RSDP物理地址
|
||
fn rsdp_paddr(rsdp_vaddr: u64) -> PhysAddr {
|
||
unsafe {
|
||
RSDP_TMP_BOX = Some(AlignedBox::new_zeroed().expect("rs_acpi_init(): failed to alloc"))
|
||
};
|
||
let size = core::mem::size_of::<acpi::rsdp::Rsdp>();
|
||
let tmp_data =
|
||
unsafe { core::slice::from_raw_parts(rsdp_vaddr as usize as *const u8, size) };
|
||
unsafe { RSDP_TMP_BOX.as_mut().unwrap()[0..size].copy_from_slice(tmp_data) };
|
||
let rsdp_paddr = unsafe {
|
||
MMArch::virt_2_phys(VirtAddr::new(
|
||
RSDP_TMP_BOX.as_ref().unwrap().as_ptr() as usize
|
||
))
|
||
.unwrap()
|
||
};
|
||
|
||
return rsdp_paddr;
|
||
}
|
||
|
||
fn set_acpi_table(acpi_table: AcpiTables<AcpiHandlerImpl>) {
|
||
unsafe {
|
||
__ACPI_TABLE = Some(acpi_table);
|
||
}
|
||
}
|
||
|
||
fn drop_rsdp_tmp_box() {
|
||
unsafe {
|
||
RSDP_TMP_BOX = None;
|
||
}
|
||
}
|
||
|
||
#[allow(dead_code)]
|
||
pub fn tables(&self) -> Option<&'static acpi::AcpiTables<AcpiHandlerImpl>> {
|
||
unsafe { __ACPI_TABLE.as_ref() }
|
||
}
|
||
|
||
/// 从acpi获取平台的信息
|
||
///
|
||
/// 包括:
|
||
///
|
||
/// - PowerProfile
|
||
/// - InterruptModel
|
||
/// - ProcessorInfo
|
||
/// - PmTimer
|
||
pub fn platform_info(&self) -> Option<PlatformInfo<'_, alloc::alloc::Global>> {
|
||
let r = self.tables()?.platform_info();
|
||
if let Err(ref e) = r {
|
||
error!(
|
||
"AcpiManager::platform_info(): failed to get platform info, error: {:?}",
|
||
e
|
||
);
|
||
return None;
|
||
}
|
||
|
||
return Some(r.unwrap());
|
||
}
|
||
}
|
||
|
||
#[derive(Debug, Clone, Copy)]
|
||
pub struct AcpiHandlerImpl;
|
||
|
||
impl AcpiHandler for AcpiHandlerImpl {
|
||
unsafe fn map_physical_region<T>(
|
||
&self,
|
||
physical_address: usize,
|
||
size: usize,
|
||
) -> acpi::PhysicalMapping<Self, T> {
|
||
let offset = physical_address - page_align_down(physical_address);
|
||
let size_fix = page_align_up(size + offset);
|
||
|
||
let mmio_guard = mmio_pool()
|
||
.create_mmio(size_fix)
|
||
.expect("AcpiHandlerImpl::map_physical_region(): failed to create mmio");
|
||
|
||
mmio_guard
|
||
.map_phys(PhysAddr::new(page_align_down(physical_address)), size_fix)
|
||
.expect("AcpiHandlerImpl::map_physical_region(): failed to map phys");
|
||
let virtual_start = mmio_guard.vaddr().data() + offset;
|
||
|
||
let virtual_start = NonNull::new(virtual_start as *mut T).unwrap();
|
||
|
||
let result: acpi::PhysicalMapping<AcpiHandlerImpl, T> = acpi::PhysicalMapping::new(
|
||
physical_address,
|
||
virtual_start,
|
||
size,
|
||
mmio_guard.size(),
|
||
AcpiHandlerImpl,
|
||
);
|
||
|
||
MMIOSpaceGuard::leak(mmio_guard);
|
||
|
||
return result;
|
||
}
|
||
|
||
fn unmap_physical_region<T>(region: &acpi::PhysicalMapping<Self, T>) {
|
||
let mmio_guard = unsafe {
|
||
MMIOSpaceGuard::from_raw(
|
||
VirtAddr::new(page_align_down(
|
||
region.virtual_start().as_ref() as *const T as usize
|
||
)),
|
||
region.mapped_length(),
|
||
true,
|
||
)
|
||
};
|
||
drop(mmio_guard);
|
||
}
|
||
}
|