ostd/arch/x86/irq/chip/
mod.rs1use alloc::{boxed::Box, vec::Vec};
4use core::{
5 fmt,
6 ops::{Deref, DerefMut},
7};
8
9use ioapic::IoApic;
10use spin::Once;
11
12use crate::{
13 Error, Result, arch::kernel::acpi::get_acpi_tables, info, io::IoMemAllocatorBuilder,
14 irq::IrqLine, sync::SpinLock,
15};
16
17mod ioapic;
18mod pic;
19
20pub struct IrqChip {
30 io_apics: SpinLock<Box<[IoApic]>>,
31 overrides: Box<[IsaOverride]>,
32}
33
34struct IsaOverride {
35 source: u8,
37 target: u32,
39}
40
41impl IrqChip {
42 pub fn map_gsi_pin_to(
50 &'static self,
51 irq_line: IrqLine,
52 gsi_index: u32,
53 ) -> Result<MappedIrqLine> {
54 let mut io_apics = self.io_apics.lock();
55
56 let io_apic = io_apics
57 .iter_mut()
58 .rev()
59 .find(|io_apic| io_apic.interrupt_base() <= gsi_index)
60 .unwrap();
61 let index_in_io_apic = (gsi_index - io_apic.interrupt_base())
62 .try_into()
63 .map_err(|_| Error::InvalidArgs)?;
64 io_apic.enable(index_in_io_apic, &irq_line)?;
65
66 Ok(MappedIrqLine {
67 irq_line,
68 gsi_index,
69 irq_chip: self,
70 })
71 }
72
73 fn disable_gsi(&self, gsi_index: u32) {
74 let mut io_apics = self.io_apics.lock();
75
76 let io_apic = io_apics
77 .iter_mut()
78 .rev()
79 .find(|io_apic| io_apic.interrupt_base() <= gsi_index)
80 .unwrap();
81 let index_in_io_apic = (gsi_index - io_apic.interrupt_base()) as u8;
82 io_apic.disable(index_in_io_apic).unwrap();
83 }
84
85 pub fn map_isa_pin_to(
93 &'static self,
94 irq_line: IrqLine,
95 isa_index: u8,
96 ) -> Result<MappedIrqLine> {
97 let gsi_index = self
98 .overrides
99 .iter()
100 .find(|isa_override| isa_override.source == isa_index)
101 .map(|isa_override| isa_override.target)
102 .unwrap_or(isa_index as u32);
103
104 self.map_gsi_pin_to(irq_line, gsi_index)
105 }
106
107 pub fn count_io_apics(&self) -> usize {
118 self.io_apics.lock().len()
119 }
120}
121
122pub struct MappedIrqLine {
126 irq_line: IrqLine,
127 gsi_index: u32,
128 irq_chip: &'static IrqChip,
129}
130
131impl fmt::Debug for MappedIrqLine {
132 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133 f.debug_struct("MappedIrqLine")
134 .field("irq_line", &self.irq_line)
135 .field("gsi_index", &self.gsi_index)
136 .finish_non_exhaustive()
137 }
138}
139
140impl Deref for MappedIrqLine {
141 type Target = IrqLine;
142
143 fn deref(&self) -> &Self::Target {
144 &self.irq_line
145 }
146}
147
148impl DerefMut for MappedIrqLine {
149 fn deref_mut(&mut self) -> &mut Self::Target {
150 &mut self.irq_line
151 }
152}
153
154impl Drop for MappedIrqLine {
155 fn drop(&mut self) {
156 self.irq_chip.disable_gsi(self.gsi_index)
157 }
158}
159
160pub static IRQ_CHIP: Once<IrqChip> = Once::new();
162
163pub(in crate::arch) fn init(io_mem_builder: &IoMemAllocatorBuilder) {
164 use acpi::madt::{Madt, MadtEntry};
165
166 let acpi_tables = get_acpi_tables().unwrap();
171 let madt_table = acpi_tables.find_table::<Madt>().unwrap();
172
173 const PCAT_COMPAT: u32 = 1;
176 if madt_table.get().flags & PCAT_COMPAT != 0 {
177 pic::init_and_disable();
178 }
179
180 let mut io_apics = Vec::with_capacity(2);
181 let mut isa_overrides = Vec::new();
182
183 const BUS_ISA: u8 = 0; for madt_entry in madt_table.get().entries() {
186 match madt_entry {
187 MadtEntry::IoApic(madt_io_apic) => {
188 let io_apic = unsafe {
191 IoApic::new(
192 madt_io_apic.io_apic_address as usize,
193 madt_io_apic.global_system_interrupt_base,
194 io_mem_builder,
195 )
196 };
197 io_apics.push(io_apic);
198 }
199 MadtEntry::InterruptSourceOverride(madt_isa_override)
200 if madt_isa_override.bus == BUS_ISA =>
201 {
202 let isa_override = IsaOverride {
203 source: madt_isa_override.irq,
204 target: madt_isa_override.global_system_interrupt,
205 };
206 isa_overrides.push(isa_override);
207 }
208 _ => {}
209 }
210 }
211
212 if isa_overrides.is_empty() {
213 isa_overrides.push(IsaOverride {
217 source: 0, target: 2, });
220 }
221
222 for isa_override in isa_overrides.iter() {
223 info!(
224 "IOAPIC override: ISA interrupt {} for GSI {}",
225 isa_override.source, isa_override.target
226 );
227 }
228
229 io_apics.sort_by_key(|io_apic| io_apic.interrupt_base());
230 assert!(!io_apics.is_empty(), "No I/O APICs found");
231 assert_eq!(
232 io_apics[0].interrupt_base(),
233 0,
234 "No I/O APIC with zero interrupt base found"
235 );
236
237 let irq_chip = IrqChip {
238 io_apics: SpinLock::new(io_apics.into_boxed_slice()),
239 overrides: isa_overrides.into_boxed_slice(),
240 };
241 IRQ_CHIP.call_once(|| irq_chip);
242}