strat9_kernel/acpi/
mod.rs1pub mod bgrt;
16pub mod dmar;
17pub mod fadt;
18pub mod hpet;
19pub mod madt;
20pub mod mcfg;
21pub mod rsdt;
22pub mod sdt;
23pub mod slit;
24pub mod waet;
25
26use crate::{memory, sync::SpinLock};
27use alloc::{collections::BTreeMap, vec::Vec};
28use core::sync::atomic::{AtomicBool, AtomicU64, Ordering};
29use sdt::Sdt;
30
31static RSDP_VADDR: AtomicU64 = AtomicU64::new(0);
33
34static RSDP_REVISION: AtomicU64 = AtomicU64::new(0);
36
37#[repr(C, packed)]
39struct Rsdp {
40 signature: [u8; 8],
41 checksum: u8,
42 oem_id: [u8; 6],
43 revision: u8,
44 rsdt_address: u32,
45}
46
47#[repr(C, packed)]
49struct Rsdp2 {
50 base: Rsdp,
51 length: u32,
52 xsdt_address: u64,
53 extended_checksum: u8,
54 _reserved: [u8; 3],
55}
56
57pub struct AcpiTables {
59 tables: BTreeMap<[u8; 4], Vec<*const Sdt>>,
60}
61
62unsafe impl Send for AcpiTables {}
63unsafe impl Sync for AcpiTables {}
64
65static ACPI_TABLES: SpinLock<AcpiTables> = SpinLock::new(AcpiTables {
66 tables: BTreeMap::new(),
67});
68
69static ACPI_INITIALIZED: AtomicBool = AtomicBool::new(false);
71
72pub fn revision() -> u8 {
74 RSDP_REVISION.load(Ordering::Relaxed) as u8
75}
76
77pub fn is_available() -> bool {
79 ACPI_INITIALIZED.load(Ordering::Relaxed)
80}
81
82pub fn rsdp_address() -> u64 {
84 RSDP_VADDR.load(Ordering::Relaxed)
85}
86
87pub fn get_bgrt() -> Option<&'static bgrt::Bgrt> {
89 bgrt::Bgrt::get()
90}
91
92pub fn get_slit() -> Option<&'static slit::Slit> {
94 slit::Slit::get()
95}
96
97pub fn get_hpet() -> Option<&'static hpet::HpetAcpiTable> {
99 hpet::HpetAcpiTable::get()
100}
101
102pub fn get_fadt() -> Option<&'static fadt::Fadt> {
104 fadt::Fadt::get()
105}
106
107pub fn get_madt() -> Option<&'static madt::MadtAcpiTable> {
109 madt::MadtAcpiTable::get()
110}
111
112pub fn get_mcfg() -> Option<&'static mcfg::Mcfg> {
114 mcfg::Mcfg::get()
115}
116
117pub fn init(rsdp_vaddr: u64) -> Result<bool, &'static str> {
119 if rsdp_vaddr == 0 {
120 log::warn!("ACPI: No RSDP provided by bootloader");
121 return Ok(false);
122 }
123
124 let rsdp = rsdp_vaddr as *const Rsdp;
125
126 let sig = unsafe { (*rsdp).signature };
128 if &sig != b"RSD PTR " {
129 return Err("ACPI: Invalid RSDP signature");
130 }
131
132 if !validate_checksum(rsdp as *const u8, 20) {
134 return Err("ACPI: RSDP checksum failed");
135 }
136
137 let revision = unsafe { (*rsdp).revision };
138
139 if revision >= 2 {
141 let rsdp2 = rsdp_vaddr as *const Rsdp2;
142 let length = unsafe { (*rsdp2).length } as usize;
143 if !validate_checksum(rsdp as *const u8, length) {
144 return Err("ACPI: RSDP extended checksum failed");
145 }
146 }
147
148 RSDP_VADDR.store(rsdp_vaddr, Ordering::Relaxed);
149 RSDP_REVISION.store(revision as u64, Ordering::Relaxed);
150
151 log::info!("ACPI: RSDP validated (revision {})", revision);
152
153 discover_tables(rsdp_vaddr, revision)?;
155
156 ACPI_INITIALIZED.store(true, Ordering::SeqCst);
158
159 Ok(true)
160}
161
162fn validate_checksum(ptr: *const u8, len: usize) -> bool {
164 let mut sum: u8 = 0;
165 for i in 0..len {
166 sum = sum.wrapping_add(unsafe { *ptr.add(i) });
167 }
168 sum == 0
169}
170
171fn discover_tables(rsdp_vaddr: u64, revision: u8) -> Result<(), &'static str> {
173 let rxsdt = rsdt::RsdtXsdt::from_rsdp(rsdp_vaddr, revision)
174 .ok_or("ACPI: Failed to find RSDT/XSDT from RSDP")?;
175 let root_sdt = rxsdt.sdt();
176 if root_sdt.length < core::mem::size_of::<Sdt>() as u32 {
177 return Err("ACPI: Root SDT has invalid length");
178 }
179 let root_phys = memory::virt_to_phys(root_sdt as *const Sdt as u64);
180 let root_len = root_sdt.length;
181 memory::paging::ensure_identity_map_range(root_phys, root_len as u64);
182 let root_sig = root_sdt.signature;
183 let root_sig_str = core::str::from_utf8(&root_sig).unwrap_or("????");
184 log::info!(
185 "ACPI: root table {} phys={:#x} len={}",
186 root_sig_str,
187 root_phys,
188 root_len
189 );
190
191 let mut acpi_tables = ACPI_TABLES.lock();
192 let mut discovered = 0usize;
193
194 for sdt_phys in rxsdt.addresses() {
195 if sdt_phys == 0 {
196 continue;
197 }
198
199 let (signature, sdt) = validate_sdt_at_phys(sdt_phys)?;
200
201 acpi_tables
203 .tables
204 .entry(signature)
205 .or_insert_with(Vec::new)
206 .push(sdt);
207 discovered += 1;
208
209 log::debug!(
210 "ACPI: Discovered table {:?} at phys {:#x}",
211 core::str::from_utf8(&signature).unwrap_or("????"),
212 sdt_phys
213 );
214 }
215
216 let unique = acpi_tables.tables.len();
217 log::info!(
218 "ACPI: discovered {} table entries ({} unique signatures)",
219 discovered,
220 unique
221 );
222
223 Ok(())
224}
225
226fn validate_sdt_at_phys(sdt_phys: u64) -> Result<([u8; 4], *const Sdt), &'static str> {
228 memory::paging::ensure_identity_map_range(sdt_phys, core::mem::size_of::<Sdt>() as u64);
230 let sdt_virt = memory::phys_to_virt(sdt_phys);
231 let sdt = sdt_virt as *const Sdt;
232 let length = unsafe { (*sdt).length as usize };
233 if length < core::mem::size_of::<Sdt>() {
234 return Err("ACPI: SDT length smaller than header");
235 }
236 memory::paging::ensure_identity_map_range(sdt_phys, length as u64);
237 if !validate_checksum(sdt as *const u8, length) {
238 return Err("ACPI: SDT checksum failed");
239 }
240 let signature = unsafe { (*sdt).signature };
241 Ok((signature, sdt))
242}
243
244pub fn find_table(signature: &[u8; 4]) -> Option<*const Sdt> {
246 let acpi_tables = ACPI_TABLES.lock();
247 acpi_tables
248 .tables
249 .get(signature)
250 .and_then(|tables| tables.first().copied())
251}
252
253pub fn find_tables(signature: &[u8; 4]) -> Option<Vec<*const Sdt>> {
255 let acpi_tables = ACPI_TABLES.lock();
256 acpi_tables.tables.get(signature).cloned()
257}
258
259pub fn get_table<T>(signature: &[u8; 4]) -> Option<&'static T> {
261 let ptr = find_table(signature)?;
262 let sdt = unsafe { &*ptr };
263 if (sdt.length as usize) < core::mem::size_of::<T>() {
264 return None;
265 }
266 Some(unsafe { &*(ptr as *const T) })
267}