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