strat9_kernel/arch/x86_64/
ioapic.rs1use crate::{acpi::madt::InterruptSourceOverride, memory};
8use core::sync::atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering};
9
10static IOAPIC_INITIALIZED: AtomicBool = AtomicBool::new(false);
12
13static IOAPIC_BASE_VIRT: AtomicU64 = AtomicU64::new(0);
15
16static IOAPIC_GSI_BASE: AtomicU32 = AtomicU32::new(0);
18
19static MADT_OVERRIDES: spin::Mutex<[Option<InterruptSourceOverride>; 16]> =
22 spin::Mutex::new([None; 16]);
23
24const IOREGSEL: u64 = 0x00;
26const IOWIN: u64 = 0x10;
27
28const IOAPICID: u32 = 0x00;
30const IOAPICVER: u32 = 0x01;
31const IOREDTBL_BASE: u32 = 0x10;
34
35const REDIR_MASK: u64 = 1 << 16;
37const REDIR_LEVEL_TRIGGER: u64 = 1 << 15;
38const REDIR_ACTIVE_LOW: u64 = 1 << 13;
39
40unsafe fn ioapic_read(reg: u32) -> u32 {
45 let base = IOAPIC_BASE_VIRT.load(Ordering::Relaxed);
46 unsafe {
48 core::ptr::write_volatile((base + IOREGSEL) as *mut u32, reg);
49 core::ptr::read_volatile((base + IOWIN) as *const u32)
50 }
51}
52
53unsafe fn ioapic_write(reg: u32, value: u32) {
58 let base = IOAPIC_BASE_VIRT.load(Ordering::Relaxed);
59 unsafe {
61 core::ptr::write_volatile((base + IOREGSEL) as *mut u32, reg);
62 core::ptr::write_volatile((base + IOWIN) as *mut u32, value);
63 }
64}
65
66unsafe fn read_redir(index: u32) -> u64 {
71 let reg_low = IOREDTBL_BASE + index * 2;
72 let reg_high = IOREDTBL_BASE + index * 2 + 1;
73 let low = unsafe { ioapic_read(reg_low) } as u64;
75 let high = unsafe { ioapic_read(reg_high) } as u64;
76 low | (high << 32)
77}
78
79unsafe fn write_redir(index: u32, value: u64) {
84 let reg_low = IOREDTBL_BASE + index * 2;
85 let reg_high = IOREDTBL_BASE + index * 2 + 1;
86 unsafe {
88 ioapic_write(reg_low, value as u32);
89 ioapic_write(reg_high, (value >> 32) as u32);
90 }
91}
92
93pub fn store_madt_overrides(overrides: &[Option<InterruptSourceOverride>]) {
97 let mut dest = MADT_OVERRIDES.lock();
98 for (i, ovr) in overrides.iter().enumerate().take(16) {
99 dest[i] = ovr.clone();
100 }
101}
102
103pub fn init(phys_addr: u32, gsi_base: u32) {
108 let virt_addr = memory::phys_to_virt(phys_addr as u64);
109 IOAPIC_BASE_VIRT.store(virt_addr, Ordering::Relaxed);
110 IOAPIC_GSI_BASE.store(gsi_base, Ordering::Relaxed);
111
112 let id = unsafe { ioapic_read(IOAPICID) >> 24 };
114 let ver_reg = unsafe { ioapic_read(IOAPICVER) };
115 let version = ver_reg & 0xFF;
116 let max_redir = ((ver_reg >> 16) & 0xFF) + 1;
117
118 for i in 0..max_redir {
120 unsafe {
122 let entry = read_redir(i);
123 write_redir(i, entry | REDIR_MASK);
124 }
125 }
126
127 IOAPIC_INITIALIZED.store(true, Ordering::Relaxed);
128
129 log::info!(
130 "I/O APIC: id={}, version={}, {} entries, GSI base={}, virt=0x{:X}",
131 id,
132 version,
133 max_redir,
134 gsi_base,
135 virt_addr
136 );
137}
138
139pub fn route_irq(gsi: u32, lapic_id: u32, vector: u8, trigger: u8, polarity: u8) {
147 let gsi_base = IOAPIC_GSI_BASE.load(Ordering::Relaxed);
148 if gsi < gsi_base {
149 log::warn!("I/O APIC: GSI {} below base {}", gsi, gsi_base);
150 return;
151 }
152 let index = gsi - gsi_base;
153
154 let mut entry: u64 = vector as u64;
163
164 if polarity == 0x03 || polarity == 1 {
165 entry |= REDIR_ACTIVE_LOW;
166 }
167 if trigger == 0x03 || trigger == 1 {
168 entry |= REDIR_LEVEL_TRIGGER;
169 }
170
171 entry |= (lapic_id as u64) << 56;
173
174 unsafe {
176 write_redir(index, entry);
177 }
178
179 log::debug!(
180 "I/O APIC: GSI{} -> vec 0x{:02X}, LAPIC {}, pol={}, trig={}",
181 gsi,
182 vector,
183 lapic_id,
184 polarity,
185 trigger
186 );
187}
188
189pub fn route_legacy_irq(
193 irq: u8,
194 lapic_id: u32,
195 vector: u8,
196 overrides: &[Option<InterruptSourceOverride>],
197) {
198 let (gsi, polarity, trigger) = find_override(irq, overrides);
200
201 route_irq(gsi, lapic_id, vector, trigger, polarity);
202
203 if gsi != irq as u32 {
204 log::info!("I/O APIC: IRQ{} remapped to GSI{} (override)", irq, gsi);
205 }
206}
207
208fn find_override(irq: u8, overrides: &[Option<InterruptSourceOverride>]) -> (u32, u8, u8) {
210 for ovr in overrides {
211 if let Some(ref o) = ovr {
212 if o.irq_source == irq {
213 return (o.gsi, o.polarity(), o.trigger_mode());
214 }
215 }
216 }
217 (irq as u32, 0, 0)
219}
220
221pub fn mask_legacy_irq(irq: u8, overrides: &[Option<InterruptSourceOverride>]) {
223 let (gsi, _, _) = find_override(irq, overrides);
224 mask_irq(gsi);
225 log::debug!("I/O APIC: masked legacy IRQ{} (GSI{})", irq, gsi);
226}
227
228pub fn mask_irq(gsi: u32) {
230 let gsi_base = IOAPIC_GSI_BASE.load(Ordering::Relaxed);
231 if gsi < gsi_base {
232 return;
233 }
234 let index = gsi - gsi_base;
235 unsafe {
237 let entry = read_redir(index);
238 write_redir(index, entry | REDIR_MASK);
239 }
240}
241
242#[allow(dead_code)]
244pub fn unmask_irq(gsi: u32) {
245 let gsi_base = IOAPIC_GSI_BASE.load(Ordering::Relaxed);
246 if gsi < gsi_base {
247 return;
248 }
249 let index = gsi - gsi_base;
250 unsafe {
252 let entry = read_redir(index);
253 write_redir(index, entry & !REDIR_MASK);
254 }
255}
256
257pub fn route_nic_irq(irq: u8, vector: u8) {
267 let lapic_id = super::apic::lapic_id();
268 let overrides = MADT_OVERRIDES.lock();
269 route_legacy_irq(irq, lapic_id, vector, &overrides[..]);
270}
271