Nothing to see here, move along
1pub mod dmar;
2pub mod madt;
3pub mod mcfg;
4
5use x86_64::structures::paging::OffsetPageTable;
6
7use crate::mem::addr;
8use crate::mem::phys::BitmapFrameAllocator;
9use crate::mem::virt;
10use crate::static_vec::StaticVec;
11
12const RSDP_SIGNATURE: &[u8; 8] = b"RSD PTR ";
13
14#[repr(C, packed)]
15struct Rsdp {
16 signature: [u8; 8],
17 checksum: u8,
18 oem_id: [u8; 6],
19 revision: u8,
20 rsdt_address: u32,
21}
22
23#[repr(C, packed)]
24struct Xsdp {
25 rsdp: Rsdp,
26 length: u32,
27 xsdt_address: u64,
28 extended_checksum: u8,
29 _reserved: [u8; 3],
30}
31
32#[repr(C, packed)]
33pub(crate) struct AcpiSdtHeader {
34 pub signature: [u8; 4],
35 pub length: u32,
36 pub revision: u8,
37 pub checksum: u8,
38 pub oem_id: [u8; 6],
39 pub oem_table_id: [u8; 8],
40 pub oem_revision: u32,
41 pub creator_id: u32,
42 pub creator_revision: u32,
43}
44
45pub struct AcpiInfo {
46 pub ioapic_addr: u64,
47 #[allow(dead_code)]
48 pub ioapic_gsi_base: u32,
49 pub irq_overrides: StaticVec<IrqOverride, 16>,
50 pub mcfg_entries: StaticVec<mcfg::McfgEntry, 4>,
51 pub dmar_info: Option<dmar::DmarInfo>,
52}
53
54#[derive(Clone, Copy)]
55pub struct IrqOverride {
56 pub source_irq: u8,
57 pub gsi: crate::arch::ioapic::Gsi,
58 #[allow(dead_code)]
59 pub flags: u16,
60}
61
62impl AcpiInfo {
63 pub fn gsi_for_irq(&self, irq: u8) -> crate::arch::ioapic::Gsi {
64 self.irq_overrides
65 .iter()
66 .find(|o| o.source_irq == irq)
67 .map(|o| o.gsi)
68 .unwrap_or(crate::arch::ioapic::Gsi::new(irq as u32))
69 }
70}
71
72fn ensure_mapped(
73 phys: u64,
74 mapper: &mut OffsetPageTable,
75 allocator: &mut BitmapFrameAllocator,
76 hhdm_offset: u64,
77) -> Result<(), crate::error::KernelError> {
78 let page_aligned = phys & !0xFFF;
79 virt::map_mmio(mapper, allocator, page_aligned, hhdm_offset)
80}
81
82struct XsdtEntry {
83 table_phys: u64,
84 sig: [u8; 4],
85 length: u32,
86 virt_addr: u64,
87}
88
89pub fn parse(
90 rsdp_addr: u64,
91 mapper: &mut OffsetPageTable,
92 allocator: &mut BitmapFrameAllocator,
93 hhdm_offset: u64,
94) -> Option<AcpiInfo> {
95 if ensure_mapped(rsdp_addr, mapper, allocator, hhdm_offset).is_err() {
96 crate::kprintln!(" ACPI: failed to map RSDP page");
97 return None;
98 }
99
100 let rsdp_virt = addr::phys_to_virt(x86_64::PhysAddr::new(rsdp_addr));
101 let rsdp_ptr = rsdp_virt.as_ptr::<Rsdp>();
102
103 let signature = unsafe { core::ptr::addr_of!((*rsdp_ptr).signature).read_unaligned() };
104 if signature != *RSDP_SIGNATURE {
105 crate::kprintln!(" ACPI: invalid RSDP signature");
106 return None;
107 }
108
109 let rsdp_checksum = (0u8..20).fold(0u8, |sum, i| {
110 sum.wrapping_add(unsafe { *(rsdp_virt.as_ptr::<u8>()).add(i as usize) })
111 });
112 if rsdp_checksum != 0 {
113 crate::kprintln!(" ACPI: RSDP checksum invalid");
114 return None;
115 }
116
117 let revision = unsafe { core::ptr::addr_of!((*rsdp_ptr).revision).read_unaligned() };
118 if revision < 2 {
119 crate::kprintln!(" ACPI: RSDP revision 0 (no XSDT)");
120 return None;
121 }
122
123 let xsdp_checksum = (0u8..36).fold(0u8, |sum, i| {
124 sum.wrapping_add(unsafe { *(rsdp_virt.as_ptr::<u8>()).add(i as usize) })
125 });
126 if xsdp_checksum != 0 {
127 crate::kprintln!(" ACPI: XSDP extended checksum invalid");
128 return None;
129 }
130
131 let xsdt_phys = {
132 let xsdp_ptr = rsdp_virt.as_ptr::<Xsdp>();
133 unsafe { core::ptr::addr_of!((*xsdp_ptr).xsdt_address).read_unaligned() }
134 };
135
136 if ensure_mapped(xsdt_phys, mapper, allocator, hhdm_offset).is_err() {
137 crate::kprintln!(" ACPI: failed to map XSDT page");
138 return None;
139 }
140
141 let xsdt_virt = addr::phys_to_virt(x86_64::PhysAddr::new(xsdt_phys));
142 let xsdt_hdr_ptr = xsdt_virt.as_ptr::<AcpiSdtHeader>();
143 let entries_offset = core::mem::size_of::<AcpiSdtHeader>();
144 let xsdt_length =
145 unsafe { core::ptr::addr_of!((*xsdt_hdr_ptr).length).read_unaligned() } as usize;
146
147 if xsdt_length < entries_offset {
148 crate::kprintln!(" ACPI: XSDT length too small");
149 return None;
150 }
151
152 let xsdt_end_phys = xsdt_phys + xsdt_length as u64;
153 let xsdt_start_page = xsdt_phys & !0xFFF;
154 let xsdt_end_page = (xsdt_end_phys.saturating_sub(1)) & !0xFFF;
155 let xsdt_map_ok = (xsdt_start_page..=xsdt_end_page)
156 .step_by(4096)
157 .skip(1)
158 .try_fold((), |(), page| {
159 ensure_mapped(page, mapper, allocator, hhdm_offset)
160 });
161 if xsdt_map_ok.is_err() {
162 crate::kprintln!(" ACPI: failed to map XSDT span");
163 return None;
164 }
165
166 let xsdt_checksum = (0..xsdt_length).fold(0u8, |sum, i| {
167 sum.wrapping_add(unsafe { *(xsdt_virt.as_ptr::<u8>()).add(i) })
168 });
169 if xsdt_checksum != 0 {
170 crate::kprintln!(" ACPI: XSDT checksum invalid");
171 return None;
172 }
173
174 let entry_count = (xsdt_length - entries_offset) / 8;
175
176 crate::kprintln!(" ACPI: XSDT at {:#x}, {} entries", xsdt_phys, entry_count);
177
178 let entries_ptr = unsafe { (xsdt_virt.as_ptr::<u8>()).add(entries_offset) as *const u64 };
179
180 let seed = AcpiInfo {
181 ioapic_addr: 0,
182 ioapic_gsi_base: 0,
183 irq_overrides: StaticVec::new(),
184 mcfg_entries: StaticVec::new(),
185 dmar_info: None,
186 };
187
188 let result = (0..entry_count)
189 .map(|i| unsafe { core::ptr::read_unaligned(entries_ptr.add(i)) })
190 .filter_map(|table_phys| {
191 ensure_mapped(table_phys, mapper, allocator, hhdm_offset).ok()?;
192 let table_virt = addr::phys_to_virt(x86_64::PhysAddr::new(table_phys));
193 let hdr_ptr = table_virt.as_ptr::<AcpiSdtHeader>();
194 let sig = unsafe { core::ptr::addr_of!((*hdr_ptr).signature).read_unaligned() };
195 let length = unsafe { core::ptr::addr_of!((*hdr_ptr).length).read_unaligned() };
196
197 let table_end = table_phys + length as u64;
198 let start_page = table_phys & !0xFFF;
199 let end_page = (table_end.saturating_sub(1)) & !0xFFF;
200 let span_ok = (start_page..=end_page)
201 .step_by(4096)
202 .skip(1)
203 .try_fold((), |(), page| {
204 ensure_mapped(page, mapper, allocator, hhdm_offset)
205 });
206 span_ok.ok()?;
207
208 Some(XsdtEntry {
209 table_phys,
210 sig,
211 length,
212 virt_addr: table_virt.as_u64(),
213 })
214 })
215 .fold(seed, |acc, entry| match &entry.sig {
216 b"APIC" => {
217 crate::kprintln!(" ACPI: found MADT at {:#x}", entry.table_phys);
218 let madt = madt::parse_madt(entry.virt_addr, entry.length);
219 AcpiInfo {
220 ioapic_addr: madt.ioapic_addr,
221 ioapic_gsi_base: madt.ioapic_gsi_base,
222 irq_overrides: madt.irq_overrides,
223 ..acc
224 }
225 }
226 b"MCFG" => {
227 crate::kprintln!(" ACPI: found MCFG at {:#x}", entry.table_phys);
228 let mcfg_entries = mcfg::parse_mcfg(entry.virt_addr, entry.length);
229 AcpiInfo {
230 mcfg_entries,
231 ..acc
232 }
233 }
234 b"DMAR" => {
235 crate::kprintln!(" ACPI: found DMAR at {:#x}", entry.table_phys);
236 let dmar = dmar::parse_dmar(entry.virt_addr, entry.length);
237 AcpiInfo {
238 dmar_info: Some(dmar),
239 ..acc
240 }
241 }
242 _ => acc,
243 });
244
245 match result.ioapic_addr {
246 0 => {
247 crate::kprintln!(" ACPI: no MADT found");
248 None
249 }
250 _ => Some(result),
251 }
252}