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