strat9_kernel/acpi/
mcfg.rs1use super::sdt::Sdt;
4use alloc::vec::Vec;
5use zerocopy::FromBytes;
6
7pub const MCFG_SIGNATURE: &[u8; 4] = b"MCFG";
8
9#[derive(Clone, Copy, Debug, FromBytes)]
10#[repr(C, packed)]
11pub struct Mcfg {
12 pub header: Sdt,
13 pub _reserved: u64,
14}
15
16impl Mcfg {
17 pub fn get() -> Option<&'static Mcfg> {
19 unsafe { super::find_table(MCFG_SIGNATURE).map(|ptr| &*(ptr as *const Mcfg)) }
20 }
21}
22
23#[derive(Clone, Copy, Debug, FromBytes)]
24#[repr(C, packed)]
25struct McfgAllocation {
26 base_address: u64,
27 segment_group: u16,
28 start_bus: u8,
29 end_bus: u8,
30 _reserved: u32,
31}
32
33#[derive(Clone, Copy, Debug)]
34pub struct McfgEntry {
35 pub base_address: u64,
36 pub segment_group: u16,
37 pub start_bus: u8,
38 pub end_bus: u8,
39}
40
41impl McfgEntry {
42 pub fn bus_count(&self) -> u16 {
44 (self.end_bus as u16).saturating_sub(self.start_bus as u16) + 1
45 }
46}
47
48#[derive(Debug)]
49pub struct McfgInfo {
50 pub entries: Vec<McfgEntry>,
51}
52
53impl McfgInfo {
54 pub fn entry_for_segment(&self, segment_group: u16) -> Option<&McfgEntry> {
56 self.entries
57 .iter()
58 .find(|e| e.segment_group == segment_group)
59 }
60}
61
62fn is_aligned_1m(addr: u64) -> bool {
64 (addr & ((1 << 20) - 1)) == 0
65}
66
67fn should_skip_entry(entry: &McfgEntry) -> bool {
69 if entry.base_address == 0 {
70 return true;
71 }
72 if entry.start_bus > entry.end_bus {
73 return true;
74 }
75 if !is_aligned_1m(entry.base_address) {
76 return true;
77 }
78 false
79}
80
81fn overlaps(a: &McfgEntry, b: &McfgEntry) -> bool {
83 if a.segment_group != b.segment_group {
84 return false;
85 }
86 !(a.end_bus < b.start_bus || b.end_bus < a.start_bus)
87}
88
89pub fn parse_mcfg() -> Option<McfgInfo> {
91 let mcfg_ptr = super::find_table(MCFG_SIGNATURE)? as *const Mcfg;
92 let mcfg = unsafe { &*mcfg_ptr };
93
94 let header_len = mcfg.header.length as usize;
95 let min_len = core::mem::size_of::<Mcfg>();
96 if header_len < min_len {
97 log::error!("ACPI: MCFG length too small: {}", header_len);
98 return None;
99 }
100
101 let alloc_len = core::mem::size_of::<McfgAllocation>();
102 let entries_bytes = header_len - min_len;
103 if entries_bytes % alloc_len != 0 {
104 log::warn!(
105 "ACPI: MCFG payload not aligned to allocation size (payload={}, alloc={})",
106 entries_bytes,
107 alloc_len
108 );
109 }
110
111 let mut entries = Vec::new();
112 let mut offset = mcfg_ptr as usize + min_len;
113 let end = mcfg_ptr as usize + header_len;
114
115 while offset + alloc_len <= end {
116 let alloc = unsafe { core::ptr::read_unaligned(offset as *const McfgAllocation) };
117 let candidate = McfgEntry {
118 base_address: alloc.base_address,
119 segment_group: alloc.segment_group,
120 start_bus: alloc.start_bus,
121 end_bus: alloc.end_bus,
122 };
123 if should_skip_entry(&candidate) {
124 log::warn!(
125 "ACPI: MCFG entry skipped (seg={} ecam={:#x} buses={}..{})",
126 candidate.segment_group,
127 candidate.base_address,
128 candidate.start_bus,
129 candidate.end_bus
130 );
131 offset += alloc_len;
132 continue;
133 }
134 if entries.iter().any(|e| overlaps(e, &candidate)) {
135 log::warn!(
136 "ACPI: MCFG overlapping entry skipped (seg={} ecam={:#x} buses={}..{})",
137 candidate.segment_group,
138 candidate.base_address,
139 candidate.start_bus,
140 candidate.end_bus
141 );
142 offset += alloc_len;
143 continue;
144 }
145 entries.push(candidate);
146 offset += alloc_len;
147 }
148
149 if entries.is_empty() {
150 log::warn!("ACPI: MCFG present but no usable allocation entries");
151 return None;
152 }
153
154 Some(McfgInfo { entries })
155}