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