1use core::sync::atomic::{AtomicBool, Ordering};
11
12use spin::Mutex;
13
14use crate::error::{KernelError, KernelResult};
15
16const RSDP_SIGNATURE: &[u8; 8] = b"RSD PTR ";
21const RSDT_SIGNATURE: &[u8; 4] = b"RSDT";
22const XSDT_SIGNATURE: &[u8; 4] = b"XSDT";
23const MADT_SIGNATURE: &[u8; 4] = b"APIC";
24const MCFG_SIGNATURE: &[u8; 4] = b"MCFG";
25const DMAR_SIGNATURE: &[u8; 4] = b"DMAR";
26const SRAT_SIGNATURE: &[u8; 4] = b"SRAT";
27const SLIT_SIGNATURE: &[u8; 4] = b"SLIT";
28
29const MADT_LOCAL_APIC: u8 = 0;
34const MADT_IO_APIC: u8 = 1;
35const MADT_INTERRUPT_SOURCE_OVERRIDE: u8 = 2;
36const MADT_LOCAL_APIC_NMI: u8 = 4;
37
38const MAX_CPUS: usize = 16;
43const MAX_IO_APICS: usize = 4;
44const MAX_ISO: usize = 24;
45const MAX_MCFG_ENTRIES: usize = 4;
46
47#[derive(Debug, Clone, Copy)]
53pub struct MadtLocalApic {
54 pub acpi_processor_id: u8,
56 pub apic_id: u8,
58 pub flags: u32,
60}
61
62impl MadtLocalApic {
63 pub fn is_usable(&self) -> bool {
65 (self.flags & 0x01) != 0 || (self.flags & 0x02) != 0
66 }
67}
68
69#[derive(Debug, Clone, Copy)]
71pub struct MadtIoApic {
72 pub id: u8,
74 pub address: u32,
76 pub gsi_base: u32,
78}
79
80#[derive(Debug, Clone, Copy)]
85pub struct MadtIso {
86 pub bus: u8,
88 pub irq_source: u8,
90 pub gsi: u32,
92 pub flags: u16,
94}
95
96impl MadtIso {
97 pub fn is_active_low(&self) -> bool {
99 (self.flags & 0x03) == 0x03
100 }
101
102 pub fn is_level_triggered(&self) -> bool {
104 ((self.flags >> 2) & 0x03) == 0x03
105 }
106}
107
108#[derive(Debug, Clone, Copy)]
110pub struct McfgEntry {
111 pub base_address: u64,
113 pub segment_group: u16,
115 pub start_bus: u8,
117 pub end_bus: u8,
119}
120
121#[derive(Debug)]
123pub struct AcpiInfo {
124 pub oem_id: [u8; 6],
126 pub local_apic_address: u32,
128 pub madt_flags: u32,
130 pub local_apics: [Option<MadtLocalApic>; MAX_CPUS],
132 pub local_apic_count: usize,
134 pub io_apics: [Option<MadtIoApic>; MAX_IO_APICS],
136 pub io_apic_count: usize,
138 pub isos: [Option<MadtIso>; MAX_ISO],
140 pub iso_count: usize,
142 pub mcfg_entries: [Option<McfgEntry>; MAX_MCFG_ENTRIES],
144 pub mcfg_count: usize,
146 pub has_madt: bool,
148 pub has_mcfg: bool,
150 pub has_dmar: bool,
152 pub dmar_address: u64,
154 pub dmar_length: u32,
156 pub revision: u8,
158 pub has_srat: bool,
160 pub srat_address: u64,
162 pub srat_length: u32,
164 pub has_slit: bool,
166 pub slit_address: u64,
168 pub slit_length: u32,
170}
171
172impl AcpiInfo {
173 const fn new() -> Self {
174 Self {
175 oem_id: [0; 6],
176 local_apic_address: 0xFEE0_0000, madt_flags: 0,
178 local_apics: [None; MAX_CPUS],
179 local_apic_count: 0,
180 io_apics: [None; MAX_IO_APICS],
181 io_apic_count: 0,
182 isos: [None; MAX_ISO],
183 iso_count: 0,
184 mcfg_entries: [None; MAX_MCFG_ENTRIES],
185 mcfg_count: 0,
186 has_madt: false,
187 has_mcfg: false,
188 has_dmar: false,
189 dmar_address: 0,
190 dmar_length: 0,
191 revision: 0,
192 has_srat: false,
193 srat_address: 0,
194 srat_length: 0,
195 has_slit: false,
196 slit_address: 0,
197 slit_length: 0,
198 }
199 }
200
201 pub fn io_apic_address(&self) -> u32 {
203 self.io_apics[0].map_or(0xFEC0_0000, |a| a.address)
204 }
205
206 pub fn irq_to_gsi(&self, irq: u8) -> (u32, bool, bool) {
209 for i in 0..self.iso_count {
210 if let Some(ref iso) = self.isos[i] {
211 if iso.irq_source == irq {
212 return (iso.gsi, iso.is_active_low(), iso.is_level_triggered());
213 }
214 }
215 }
216 (irq as u32, false, false)
218 }
219
220 pub fn cpu_count(&self) -> usize {
222 let mut count = 0;
223 for i in 0..self.local_apic_count {
224 if let Some(ref lapic) = self.local_apics[i] {
225 if lapic.is_usable() {
226 count += 1;
227 }
228 }
229 }
230 count
231 }
232}
233
234static ACPI_INITIALIZED: AtomicBool = AtomicBool::new(false);
239static ACPI_INFO: Mutex<Option<AcpiInfo>> = Mutex::new(None);
240
241pub fn is_initialized() -> bool {
243 ACPI_INITIALIZED.load(Ordering::Acquire)
244}
245
246pub fn with_acpi_info<R, F: FnOnce(&AcpiInfo) -> R>(f: F) -> Option<R> {
248 let guard = ACPI_INFO.lock();
249 guard.as_ref().map(f)
250}
251
252#[repr(C, packed)]
258struct Rsdp {
259 signature: [u8; 8],
260 checksum: u8,
261 oem_id: [u8; 6],
262 revision: u8,
263 rsdt_address: u32,
264}
265
266#[repr(C, packed)]
268struct Rsdp2 {
269 base: Rsdp,
270 length: u32,
271 xsdt_address: u64,
272 extended_checksum: u8,
273 _reserved: [u8; 3],
274}
275
276#[repr(C, packed)]
278struct AcpiSdtHeader {
279 signature: [u8; 4],
280 length: u32,
281 revision: u8,
282 checksum: u8,
283 oem_id: [u8; 6],
284 oem_table_id: [u8; 8],
285 oem_revision: u32,
286 creator_id: u32,
287 creator_revision: u32,
288}
289
290#[repr(C, packed)]
292struct MadtHeader {
293 sdt: AcpiSdtHeader,
294 local_apic_address: u32,
295 flags: u32,
296}
297
298#[repr(C, packed)]
300struct MadtEntryHeader {
301 entry_type: u8,
302 length: u8,
303}
304
305#[repr(C, packed)]
307struct MadtLocalApicEntry {
308 header: MadtEntryHeader,
309 acpi_processor_id: u8,
310 apic_id: u8,
311 flags: u32,
312}
313
314#[repr(C, packed)]
316struct MadtIoApicEntry {
317 header: MadtEntryHeader,
318 id: u8,
319 _reserved: u8,
320 address: u32,
321 gsi_base: u32,
322}
323
324#[repr(C, packed)]
326struct MadtIsoEntry {
327 header: MadtEntryHeader,
328 bus: u8,
329 source: u8,
330 gsi: u32,
331 flags: u16,
332}
333
334#[repr(C, packed)]
336struct MadtLocalApicNmiEntry {
337 header: MadtEntryHeader,
338 acpi_processor_id: u8,
339 flags: u16,
340 lint: u8,
341}
342
343#[repr(C, packed)]
345struct McfgAllocation {
346 base_address: u64,
347 segment_group: u16,
348 start_bus: u8,
349 end_bus: u8,
350 _reserved: u32,
351}
352
353fn validate_checksum(addr: usize, len: usize) -> bool {
359 let mut sum: u8 = 0;
360 for i in 0..len {
361 let byte_addr = match addr.checked_add(i) {
362 Some(a) => a,
363 None => return false,
364 };
365 sum = sum.wrapping_add(unsafe { *(byte_addr as *const u8) });
368 }
369 sum == 0
370}
371
372fn parse_madt(header_vaddr: usize, info: &mut AcpiInfo) {
378 let madt = unsafe { &*(header_vaddr as *const MadtHeader) };
382 let table_len = { madt.sdt.length } as usize;
383
384 info.local_apic_address = madt.local_apic_address;
385 info.madt_flags = madt.flags;
386 info.has_madt = true;
387
388 println!(
389 "[ACPI] MADT: LAPIC addr={:#x}, flags={:#x}, len={}",
390 info.local_apic_address, info.madt_flags, table_len
391 );
392
393 let entries_start = header_vaddr + core::mem::size_of::<MadtHeader>();
395 let entries_end = header_vaddr + table_len;
396 let mut offset = entries_start;
397
398 while offset + 2 <= entries_end {
399 let entry_header = unsafe { &*(offset as *const MadtEntryHeader) };
402 let entry_len = entry_header.length as usize;
403
404 let next_offset = match offset.checked_add(entry_len) {
405 Some(n) => n,
406 None => break,
407 };
408 if entry_len < 2 || next_offset > entries_end {
409 break;
410 }
411
412 match entry_header.entry_type {
413 MADT_LOCAL_APIC => {
414 if entry_len >= core::mem::size_of::<MadtLocalApicEntry>()
415 && info.local_apic_count < MAX_CPUS
416 {
417 let entry = unsafe { &*(offset as *const MadtLocalApicEntry) };
420 let lapic = MadtLocalApic {
421 acpi_processor_id: entry.acpi_processor_id,
422 apic_id: entry.apic_id,
423 flags: { entry.flags },
424 };
425 println!(
426 "[ACPI] CPU: proc_id={}, apic_id={}, flags={:#x}{}",
427 lapic.acpi_processor_id,
428 lapic.apic_id,
429 lapic.flags,
430 if lapic.is_usable() {
431 " [usable]"
432 } else {
433 " [disabled]"
434 }
435 );
436 info.local_apics[info.local_apic_count] = Some(lapic);
437 info.local_apic_count += 1;
438 }
439 }
440 MADT_IO_APIC => {
441 if entry_len >= core::mem::size_of::<MadtIoApicEntry>()
442 && info.io_apic_count < MAX_IO_APICS
443 {
444 let entry = unsafe { &*(offset as *const MadtIoApicEntry) };
446 let ioapic = MadtIoApic {
447 id: entry.id,
448 address: { entry.address },
449 gsi_base: { entry.gsi_base },
450 };
451 println!(
452 "[ACPI] I/O APIC: id={}, addr={:#x}, gsi_base={}",
453 ioapic.id, ioapic.address, ioapic.gsi_base
454 );
455 info.io_apics[info.io_apic_count] = Some(ioapic);
456 info.io_apic_count += 1;
457 }
458 }
459 MADT_INTERRUPT_SOURCE_OVERRIDE => {
460 if entry_len >= core::mem::size_of::<MadtIsoEntry>() && info.iso_count < MAX_ISO {
461 let entry = unsafe { &*(offset as *const MadtIsoEntry) };
463 let iso = MadtIso {
464 bus: entry.bus,
465 irq_source: entry.source,
466 gsi: { entry.gsi },
467 flags: { entry.flags },
468 };
469 println!(
470 "[ACPI] ISO: bus={}, irq={} -> gsi={}, flags={:#x}",
471 iso.bus, iso.irq_source, iso.gsi, iso.flags
472 );
473 info.isos[info.iso_count] = Some(iso);
474 info.iso_count += 1;
475 }
476 }
477 MADT_LOCAL_APIC_NMI => {
478 if entry_len >= core::mem::size_of::<MadtLocalApicNmiEntry>() {
479 let entry = unsafe { &*(offset as *const MadtLocalApicNmiEntry) };
481 println!(
482 "[ACPI] LAPIC NMI: proc_id={}, flags={:#x}, lint={}",
483 entry.acpi_processor_id,
484 { entry.flags },
485 entry.lint
486 );
487 }
488 }
489 other => {
490 println!(
491 "[ACPI] Unknown MADT entry type {} (len={})",
492 other, entry_len
493 );
494 }
495 }
496
497 offset += entry_len;
498 }
499
500 println!(
501 "[ACPI] MADT summary: {} CPUs ({} usable), {} I/O APICs, {} ISOs",
502 info.local_apic_count,
503 info.cpu_count(),
504 info.io_apic_count,
505 info.iso_count
506 );
507}
508
509fn parse_mcfg(header_vaddr: usize, info: &mut AcpiInfo) {
511 let sdt = unsafe { &*(header_vaddr as *const AcpiSdtHeader) };
513 let table_len = { sdt.length } as usize;
514 let header_size = core::mem::size_of::<AcpiSdtHeader>() + 8; if table_len <= header_size {
517 println!("[ACPI] MCFG: no allocation entries");
518 return;
519 }
520
521 info.has_mcfg = true;
522 let entries_start = header_vaddr + header_size;
523 let entry_size = core::mem::size_of::<McfgAllocation>();
524 let num_entries = (table_len - header_size) / entry_size;
525
526 println!("[ACPI] MCFG: {} allocation entries", num_entries);
527
528 for i in 0..num_entries {
529 if info.mcfg_count >= MAX_MCFG_ENTRIES {
530 break;
531 }
532 let entry_addr = entries_start + i * entry_size;
533 let entry = unsafe { &*(entry_addr as *const McfgAllocation) };
535 let mcfg = McfgEntry {
536 base_address: { entry.base_address },
537 segment_group: { entry.segment_group },
538 start_bus: entry.start_bus,
539 end_bus: entry.end_bus,
540 };
541 println!(
542 "[ACPI] ECAM: base={:#x}, seg={}, bus={}..{}",
543 mcfg.base_address, mcfg.segment_group, mcfg.start_bus, mcfg.end_bus
544 );
545 info.mcfg_entries[info.mcfg_count] = Some(mcfg);
546 info.mcfg_count += 1;
547 }
548}
549
550fn parse_table(header_vaddr: usize, info: &mut AcpiInfo) {
552 let sdt = unsafe { &*(header_vaddr as *const AcpiSdtHeader) };
554 let sig = { sdt.signature };
555 let len = { sdt.length } as usize;
556
557 if !validate_checksum(header_vaddr, len) {
559 println!(
560 "[ACPI] WARNING: bad checksum for table {:?}",
561 core::str::from_utf8(&sig).unwrap_or("????")
562 );
563 }
565
566 if &sig == MADT_SIGNATURE {
567 parse_madt(header_vaddr, info);
568 } else if &sig == MCFG_SIGNATURE {
569 parse_mcfg(header_vaddr, info);
570 } else if &sig == DMAR_SIGNATURE {
571 info.has_dmar = true;
575 info.dmar_address = header_vaddr as u64;
577 info.dmar_length = len as u32;
578 println!("[ACPI] DMAR table found (len={})", len);
579 } else if &sig == SRAT_SIGNATURE {
580 info.has_srat = true;
581 info.srat_address = header_vaddr as u64;
582 info.srat_length = len as u32;
583 println!("[ACPI] SRAT table found (len={})", len);
584 } else if &sig == SLIT_SIGNATURE {
585 info.has_slit = true;
586 info.slit_address = header_vaddr as u64;
587 info.slit_length = len as u32;
588 println!("[ACPI] SLIT table found (len={})", len);
589 } else {
590 let sig_str = core::str::from_utf8(&sig).unwrap_or("????");
592 println!("[ACPI] Table '{}' (len={}) -- skipped", sig_str, len);
593 }
594}
595
596fn parse_rsdt(rsdt_vaddr: usize, info: &mut AcpiInfo) -> KernelResult<()> {
598 let sdt = unsafe { &*(rsdt_vaddr as *const AcpiSdtHeader) };
600 let len = { sdt.length } as usize;
601
602 if &{ sdt.signature } != RSDT_SIGNATURE {
603 return Err(KernelError::InvalidArgument {
604 name: "RSDT signature",
605 value: "not RSDT",
606 });
607 }
608
609 if !validate_checksum(rsdt_vaddr, len) {
610 println!("[ACPI] WARNING: RSDT checksum invalid");
611 }
612
613 info.oem_id = sdt.oem_id;
615
616 let header_size = core::mem::size_of::<AcpiSdtHeader>();
617 let num_entries = (len - header_size) / 4; println!(
620 "[ACPI] RSDT at {:#x}: {} child tables, OEM='{}'",
621 rsdt_vaddr,
622 num_entries,
623 core::str::from_utf8(&info.oem_id).unwrap_or("??????")
624 );
625
626 for i in 0..num_entries {
627 let ptr_addr = rsdt_vaddr + header_size + i * 4;
628 let phys_addr = unsafe { *(ptr_addr as *const u32) } as usize;
631 if let Some(vaddr) = super::msr::phys_to_virt(phys_addr) {
632 parse_table(vaddr, info);
633 }
634 }
635
636 Ok(())
637}
638
639fn parse_xsdt(xsdt_vaddr: usize, info: &mut AcpiInfo) -> KernelResult<()> {
641 let sdt = unsafe { &*(xsdt_vaddr as *const AcpiSdtHeader) };
643 let len = { sdt.length } as usize;
644
645 if &{ sdt.signature } != XSDT_SIGNATURE {
646 return Err(KernelError::InvalidArgument {
647 name: "XSDT signature",
648 value: "not XSDT",
649 });
650 }
651
652 if !validate_checksum(xsdt_vaddr, len) {
653 println!("[ACPI] WARNING: XSDT checksum invalid");
654 }
655
656 info.oem_id = sdt.oem_id;
658
659 let header_size = core::mem::size_of::<AcpiSdtHeader>();
660 let num_entries = (len - header_size) / 8; println!(
663 "[ACPI] XSDT at {:#x}: {} child tables, OEM='{}'",
664 xsdt_vaddr,
665 num_entries,
666 core::str::from_utf8(&info.oem_id).unwrap_or("??????")
667 );
668
669 for i in 0..num_entries {
670 let ptr_addr = xsdt_vaddr + header_size + i * 8;
671 let phys_addr = unsafe { *(ptr_addr as *const u64) } as usize;
674 if let Some(vaddr) = super::msr::phys_to_virt(phys_addr) {
675 parse_table(vaddr, info);
676 }
677 }
678
679 Ok(())
680}
681
682pub fn init() -> KernelResult<()> {
692 if ACPI_INITIALIZED.load(Ordering::Acquire) {
693 return Err(KernelError::AlreadyExists {
694 resource: "ACPI",
695 id: 0,
696 });
697 }
698
699 println!("[ACPI] Initializing ACPI table parser...");
700
701 #[allow(static_mut_refs)]
705 let rsdp_phys = unsafe {
706 super::boot::BOOT_INFO
707 .as_ref()
708 .and_then(|bi| bi.rsdp_addr.into_option())
709 };
710
711 let rsdp_phys = match rsdp_phys {
712 Some(addr) => addr as usize,
713 None => {
714 println!("[ACPI] No RSDP address from bootloader, ACPI unavailable");
715 return Err(KernelError::NotInitialized {
716 subsystem: "ACPI (no RSDP)",
717 });
718 }
719 };
720
721 println!("[ACPI] RSDP physical address: {:#x}", rsdp_phys);
722
723 let rsdp_vaddr = super::msr::phys_to_virt(rsdp_phys).ok_or(KernelError::NotInitialized {
725 subsystem: "ACPI (phys_to_virt)",
726 })?;
727
728 let rsdp = unsafe { &*(rsdp_vaddr as *const Rsdp) };
732 if &rsdp.signature != RSDP_SIGNATURE {
733 println!("[ACPI] Invalid RSDP signature");
734 return Err(KernelError::InvalidArgument {
735 name: "RSDP signature",
736 value: "not 'RSD PTR '",
737 });
738 }
739
740 if !validate_checksum(rsdp_vaddr, 20) {
742 println!("[ACPI] WARNING: RSDP checksum invalid");
743 }
744
745 let mut info = AcpiInfo::new();
746 info.revision = rsdp.revision;
747
748 println!(
749 "[ACPI] RSDP: revision={}, OEM='{}'",
750 rsdp.revision,
751 core::str::from_utf8(&rsdp.oem_id).unwrap_or("??????")
752 );
753
754 if rsdp.revision >= 2 {
756 let rsdp2 = unsafe { &*(rsdp_vaddr as *const Rsdp2) };
759 let xsdt_phys = { rsdp2.xsdt_address } as usize;
760
761 if xsdt_phys != 0 {
762 if let Some(xsdt_vaddr) = super::msr::phys_to_virt(xsdt_phys) {
763 println!("[ACPI] Using XSDT at phys={:#x}", xsdt_phys);
764 parse_xsdt(xsdt_vaddr, &mut info)?;
765 } else {
766 println!("[ACPI] Cannot map XSDT, falling back to RSDT");
767 let rsdt_phys = { rsdp.rsdt_address } as usize;
768 if let Some(rsdt_vaddr) = super::msr::phys_to_virt(rsdt_phys) {
769 parse_rsdt(rsdt_vaddr, &mut info)?;
770 }
771 }
772 } else {
773 let rsdt_phys = { rsdp.rsdt_address } as usize;
775 if let Some(rsdt_vaddr) = super::msr::phys_to_virt(rsdt_phys) {
776 println!(
777 "[ACPI] XSDT addr is zero, using RSDT at phys={:#x}",
778 rsdt_phys
779 );
780 parse_rsdt(rsdt_vaddr, &mut info)?;
781 }
782 }
783 } else {
784 let rsdt_phys = { rsdp.rsdt_address } as usize;
786 if let Some(rsdt_vaddr) = super::msr::phys_to_virt(rsdt_phys) {
787 println!("[ACPI] Using RSDT at phys={:#x}", rsdt_phys);
788 parse_rsdt(rsdt_vaddr, &mut info)?;
789 }
790 }
791
792 if !info.has_madt {
794 println!("[ACPI] No MADT found, using defaults (1 CPU, LAPIC at 0xFEE00000)");
795 info.local_apic_address = 0xFEE0_0000;
796 info.local_apics[0] = Some(MadtLocalApic {
797 acpi_processor_id: 0,
798 apic_id: 0,
799 flags: 1,
800 });
801 info.local_apic_count = 1;
802 }
803
804 println!(
805 "[ACPI] Initialization complete: {} CPUs, {} I/O APICs, MADT={}, MCFG={}",
806 info.cpu_count(),
807 info.io_apic_count,
808 info.has_madt,
809 info.has_mcfg
810 );
811
812 *ACPI_INFO.lock() = Some(info);
813 ACPI_INITIALIZED.store(true, Ordering::Release);
814 Ok(())
815}
816
817pub fn dump() {
819 let guard = ACPI_INFO.lock();
820 let info = match guard.as_ref() {
821 Some(info) => info,
822 None => {
823 println!("ACPI not initialized");
824 return;
825 }
826 };
827
828 println!("=== ACPI Information ===");
829 println!(
830 " Revision: {} (ACPI {}.0)",
831 info.revision,
832 if info.revision >= 2 { "2" } else { "1" }
833 );
834 println!(
835 " OEM: '{}'",
836 core::str::from_utf8(&info.oem_id).unwrap_or("??????")
837 );
838 println!(" Local APIC Address: {:#x}", info.local_apic_address);
839 println!(
840 " MADT Flags: {:#x} (dual 8259: {})",
841 info.madt_flags,
842 if info.madt_flags & 1 != 0 {
843 "yes"
844 } else {
845 "no"
846 }
847 );
848
849 println!("\n--- CPUs ({}) ---", info.local_apic_count);
850 for i in 0..info.local_apic_count {
851 if let Some(ref lapic) = info.local_apics[i] {
852 println!(
853 " CPU {}: APIC ID={}, proc_id={}, flags={:#x} {}",
854 i,
855 lapic.apic_id,
856 lapic.acpi_processor_id,
857 lapic.flags,
858 if lapic.is_usable() {
859 "[usable]"
860 } else {
861 "[disabled]"
862 }
863 );
864 }
865 }
866
867 println!("\n--- I/O APICs ({}) ---", info.io_apic_count);
868 for i in 0..info.io_apic_count {
869 if let Some(ref ioapic) = info.io_apics[i] {
870 println!(
871 " I/O APIC {}: ID={}, addr={:#x}, GSI base={}",
872 i, ioapic.id, ioapic.address, ioapic.gsi_base
873 );
874 }
875 }
876
877 if info.iso_count > 0 {
878 println!("\n--- Interrupt Source Overrides ({}) ---", info.iso_count);
879 for i in 0..info.iso_count {
880 if let Some(ref iso) = info.isos[i] {
881 println!(
882 " IRQ {} -> GSI {}, flags={:#x} (active_low={}, level={})",
883 iso.irq_source,
884 iso.gsi,
885 iso.flags,
886 iso.is_active_low(),
887 iso.is_level_triggered()
888 );
889 }
890 }
891 }
892
893 if info.has_mcfg {
894 println!("\n--- PCIe ECAM ({}) ---", info.mcfg_count);
895 for i in 0..info.mcfg_count {
896 if let Some(ref mcfg) = info.mcfg_entries[i] {
897 println!(
898 " Segment {}: base={:#x}, bus={}..{}",
899 mcfg.segment_group, mcfg.base_address, mcfg.start_bus, mcfg.end_bus
900 );
901 }
902 }
903 } else {
904 println!("\n--- PCIe ECAM: not available ---");
905 }
906
907 println!(
908 "\nSummary: {} usable CPUs, {} I/O APICs, {} ISOs, {} MCFG entries",
909 info.cpu_count(),
910 info.io_apic_count,
911 info.iso_count,
912 info.mcfg_count
913 );
914}
915
916pub fn find_srat() -> Option<&'static [u8]> {
918 with_acpi_info(|info| {
919 if !info.has_srat || info.srat_address == 0 {
920 return None;
921 }
922 let addr = info.srat_address as usize;
923 let len = info.srat_length as usize;
924 Some(unsafe { core::slice::from_raw_parts(addr as *const u8, len) })
928 })
929 .flatten()
930}
931
932pub fn find_slit() -> Option<&'static [u8]> {
934 with_acpi_info(|info| {
935 if !info.has_slit || info.slit_address == 0 {
936 return None;
937 }
938 let addr = info.slit_address as usize;
939 let len = info.slit_length as usize;
940 Some(unsafe { core::slice::from_raw_parts(addr as *const u8, len) })
944 })
945 .flatten()
946}
947
948pub fn find_madt_cpus() -> Option<alloc::vec::Vec<(u32, u32, bool)>> {
952 with_acpi_info(|info| {
953 if !info.has_madt {
954 return None;
955 }
956 let mut cpus = alloc::vec::Vec::new();
957 for i in 0..info.local_apic_count {
958 if let Some(ref lapic) = info.local_apics[i] {
959 cpus.push((
960 lapic.apic_id as u32,
961 lapic.acpi_processor_id as u32,
962 lapic.is_usable(),
963 ));
964 }
965 }
966 Some(cpus)
967 })
968 .flatten()
969}