ostd/arch/x86/irq/chip/
mod.rs1use alloc::{boxed::Box, vec::Vec};
4use core::{
5 fmt,
6 ops::{Deref, DerefMut},
7 pin::Pin,
8};
9
10use acpi::madt::{Madt, MadtEntry};
11use ioapic::IoApic;
12use spin::Once;
13
14use crate::{
15 Error, Result, arch::kernel::acpi::get_acpi_tables, info, io::IoMemAllocatorBuilder,
16 irq::IrqLine, sync::SpinLock,
17};
18
19mod ioapic;
20mod pic;
21
22pub struct IrqChip {
32 io_apics: SpinLock<Box<[IoApic]>>,
33 overrides: Box<[IsaOverride]>,
34}
35
36struct IsaOverride {
37 source: u8,
39 target: u32,
41}
42
43impl IrqChip {
44 pub fn map_gsi_pin_to(
52 &'static self,
53 irq_line: IrqLine,
54 gsi_index: u32,
55 ) -> Result<MappedIrqLine> {
56 let mut io_apics = self.io_apics.lock();
57
58 let io_apic = io_apics
59 .iter_mut()
60 .rev()
61 .find(|io_apic| io_apic.interrupt_base() <= gsi_index)
62 .unwrap();
63 let index_in_io_apic = (gsi_index - io_apic.interrupt_base())
64 .try_into()
65 .map_err(|_| Error::InvalidArgs)?;
66 io_apic.enable(index_in_io_apic, &irq_line)?;
67
68 Ok(MappedIrqLine {
69 irq_line,
70 gsi_index,
71 irq_chip: self,
72 })
73 }
74
75 fn disable_gsi(&self, gsi_index: u32) {
76 let mut io_apics = self.io_apics.lock();
77
78 let io_apic = io_apics
79 .iter_mut()
80 .rev()
81 .find(|io_apic| io_apic.interrupt_base() <= gsi_index)
82 .unwrap();
83 let index_in_io_apic = (gsi_index - io_apic.interrupt_base()) as u8;
84 io_apic.disable(index_in_io_apic).unwrap();
85 }
86
87 pub fn map_isa_pin_to(
95 &'static self,
96 irq_line: IrqLine,
97 isa_index: u8,
98 ) -> Result<MappedIrqLine> {
99 let gsi_index = self
100 .overrides
101 .iter()
102 .find(|isa_override| isa_override.source == isa_index)
103 .map(|isa_override| isa_override.target)
104 .unwrap_or(isa_index as u32);
105
106 self.map_gsi_pin_to(irq_line, gsi_index)
107 }
108
109 pub fn count_io_apics(&self) -> usize {
120 self.io_apics.lock().len()
121 }
122}
123
124pub struct MappedIrqLine {
128 irq_line: IrqLine,
129 gsi_index: u32,
130 irq_chip: &'static IrqChip,
131}
132
133impl fmt::Debug for MappedIrqLine {
134 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135 f.debug_struct("MappedIrqLine")
136 .field("irq_line", &self.irq_line)
137 .field("gsi_index", &self.gsi_index)
138 .finish_non_exhaustive()
139 }
140}
141
142impl Deref for MappedIrqLine {
143 type Target = IrqLine;
144
145 fn deref(&self) -> &Self::Target {
146 &self.irq_line
147 }
148}
149
150impl DerefMut for MappedIrqLine {
151 fn deref_mut(&mut self) -> &mut Self::Target {
152 &mut self.irq_line
153 }
154}
155
156impl Drop for MappedIrqLine {
157 fn drop(&mut self) {
158 self.irq_chip.disable_gsi(self.gsi_index)
159 }
160}
161
162pub static IRQ_CHIP: Once<IrqChip> = Once::new();
164
165pub(in crate::arch) fn init(io_mem_builder: &IoMemAllocatorBuilder) {
166 let acpi_tables = get_acpi_tables().unwrap();
171 let madt_table = acpi_tables.find_table::<Madt>().unwrap();
172
173 init_and_disable_pic_if_present(madt_table.get());
174
175 let mut io_apics: Vec<IoApic> = Vec::with_capacity(2);
176 let mut isa_overrides = Vec::new();
177
178 const BUS_ISA: u8 = 0; for madt_entry in madt_table.get().entries() {
181 match madt_entry {
182 MadtEntry::IoApic(madt_io_apic) => {
183 let address = madt_io_apic.io_apic_address as usize;
184 let interrupt_base = madt_io_apic.global_system_interrupt_base;
185
186 if address == 0 {
191 crate::warn!("IOAPIC address is invalid (zero), skipping");
192 continue;
193 }
194 if io_apics
195 .iter()
196 .any(|existing| existing.address() == address)
197 {
198 crate::warn!(
199 "IOAPIC address {:#x} duplicates an existing one, skipping",
200 address
201 );
202 continue;
203 }
204
205 let Some(io_apic) = (unsafe {
209 IoApic::new(
210 madt_io_apic.io_apic_address as usize,
211 madt_io_apic.global_system_interrupt_base,
212 io_mem_builder,
213 )
214 }) else {
215 continue;
216 };
217
218 let interrupt_end = io_apic.interrupt_end();
219 if io_apics.iter().any(|existing| {
220 interrupt_base <= existing.interrupt_end()
221 && interrupt_end >= existing.interrupt_base()
222 }) {
223 crate::warn!(
224 "IOAPIC {:#x} GSI range [{}-{}] conflicts with an existing IOAPIC, skipping",
225 address,
226 interrupt_base,
227 interrupt_end
228 );
229 continue;
230 }
231
232 io_apics.push(io_apic);
233 }
234 MadtEntry::InterruptSourceOverride(madt_isa_override)
235 if madt_isa_override.bus == BUS_ISA =>
236 {
237 let isa_override = IsaOverride {
238 source: madt_isa_override.irq,
239 target: madt_isa_override.global_system_interrupt,
240 };
241 isa_overrides.push(isa_override);
242 }
243 _ => {}
244 }
245 }
246
247 if isa_overrides.is_empty() {
248 isa_overrides.push(IsaOverride {
252 source: 0, target: 2, });
255 }
256
257 for isa_override in isa_overrides.iter() {
258 info!(
259 "IOAPIC override: ISA interrupt {} for GSI {}",
260 isa_override.source, isa_override.target
261 );
262 }
263
264 io_apics.sort_by_key(|io_apic| io_apic.interrupt_base());
265 assert!(!io_apics.is_empty(), "No I/O APICs found");
266 assert_eq!(
267 io_apics[0].interrupt_base(),
268 0,
269 "No I/O APIC with zero interrupt base found"
270 );
271
272 let irq_chip = IrqChip {
273 io_apics: SpinLock::new(io_apics.into_boxed_slice()),
274 overrides: isa_overrides.into_boxed_slice(),
275 };
276 IRQ_CHIP.call_once(|| irq_chip);
277}
278
279fn init_and_disable_pic_if_present(madt_table: Pin<&Madt>) {
280 const PCAT_COMPAT: u32 = 1;
283 if madt_table.flags & PCAT_COMPAT != 0 || pic::probe_legacy_pic() {
284 pic::init_and_disable();
285 }
286}