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
19const IOREGSEL: u64 = 0x00;
21const IOWIN: u64 = 0x10;
22
23const IOAPICID: u32 = 0x00;
25const IOAPICVER: u32 = 0x01;
26const IOREDTBL_BASE: u32 = 0x10;
29
30const REDIR_MASK: u64 = 1 << 16;
32const REDIR_LEVEL_TRIGGER: u64 = 1 << 15;
33const REDIR_ACTIVE_LOW: u64 = 1 << 13;
34
35unsafe fn ioapic_read(reg: u32) -> u32 {
40 let base = IOAPIC_BASE_VIRT.load(Ordering::Relaxed);
41 unsafe {
43 core::ptr::write_volatile((base + IOREGSEL) as *mut u32, reg);
44 core::ptr::read_volatile((base + IOWIN) as *const u32)
45 }
46}
47
48unsafe fn ioapic_write(reg: u32, value: u32) {
53 let base = IOAPIC_BASE_VIRT.load(Ordering::Relaxed);
54 unsafe {
56 core::ptr::write_volatile((base + IOREGSEL) as *mut u32, reg);
57 core::ptr::write_volatile((base + IOWIN) as *mut u32, value);
58 }
59}
60
61unsafe fn read_redir(index: u32) -> u64 {
66 let reg_low = IOREDTBL_BASE + index * 2;
67 let reg_high = IOREDTBL_BASE + index * 2 + 1;
68 let low = unsafe { ioapic_read(reg_low) } as u64;
70 let high = unsafe { ioapic_read(reg_high) } as u64;
71 low | (high << 32)
72}
73
74unsafe fn write_redir(index: u32, value: u64) {
79 let reg_low = IOREDTBL_BASE + index * 2;
80 let reg_high = IOREDTBL_BASE + index * 2 + 1;
81 unsafe {
83 ioapic_write(reg_low, value as u32);
84 ioapic_write(reg_high, (value >> 32) as u32);
85 }
86}
87
88pub fn init(phys_addr: u32, gsi_base: u32) {
93 let virt_addr = memory::phys_to_virt(phys_addr as u64);
94 IOAPIC_BASE_VIRT.store(virt_addr, Ordering::Relaxed);
95 IOAPIC_GSI_BASE.store(gsi_base, Ordering::Relaxed);
96
97 let id = unsafe { ioapic_read(IOAPICID) >> 24 };
99 let ver_reg = unsafe { ioapic_read(IOAPICVER) };
100 let version = ver_reg & 0xFF;
101 let max_redir = ((ver_reg >> 16) & 0xFF) + 1;
102
103 for i in 0..max_redir {
105 unsafe {
107 let entry = read_redir(i);
108 write_redir(i, entry | REDIR_MASK);
109 }
110 }
111
112 IOAPIC_INITIALIZED.store(true, Ordering::Relaxed);
113
114 log::info!(
115 "I/O APIC: id={}, version={}, {} entries, GSI base={}, virt=0x{:X}",
116 id,
117 version,
118 max_redir,
119 gsi_base,
120 virt_addr
121 );
122}
123
124pub fn route_irq(gsi: u32, lapic_id: u32, vector: u8, trigger: u8, polarity: u8) {
132 let gsi_base = IOAPIC_GSI_BASE.load(Ordering::Relaxed);
133 if gsi < gsi_base {
134 log::warn!("I/O APIC: GSI {} below base {}", gsi, gsi_base);
135 return;
136 }
137 let index = gsi - gsi_base;
138
139 let mut entry: u64 = vector as u64;
148
149 if polarity == 0x03 || polarity == 1 {
150 entry |= REDIR_ACTIVE_LOW;
151 }
152 if trigger == 0x03 || trigger == 1 {
153 entry |= REDIR_LEVEL_TRIGGER;
154 }
155
156 entry |= (lapic_id as u64) << 56;
158
159 unsafe {
161 write_redir(index, entry);
162 }
163
164 log::debug!(
165 "I/O APIC: GSI{} -> vec 0x{:02X}, LAPIC {}, pol={}, trig={}",
166 gsi,
167 vector,
168 lapic_id,
169 polarity,
170 trigger
171 );
172}
173
174pub fn route_legacy_irq(
178 irq: u8,
179 lapic_id: u32,
180 vector: u8,
181 overrides: &[Option<InterruptSourceOverride>],
182) {
183 let (gsi, polarity, trigger) = find_override(irq, overrides);
185
186 route_irq(gsi, lapic_id, vector, trigger, polarity);
187
188 if gsi != irq as u32 {
189 log::info!("I/O APIC: IRQ{} remapped to GSI{} (override)", irq, gsi);
190 }
191}
192
193fn find_override(irq: u8, overrides: &[Option<InterruptSourceOverride>]) -> (u32, u8, u8) {
195 for ovr in overrides {
196 if let Some(ref o) = ovr {
197 if o.irq_source == irq {
198 return (o.gsi, o.polarity(), o.trigger_mode());
199 }
200 }
201 }
202 (irq as u32, 0, 0)
204}
205
206pub fn mask_legacy_irq(irq: u8, overrides: &[Option<InterruptSourceOverride>]) {
208 let (gsi, _, _) = find_override(irq, overrides);
209 mask_irq(gsi);
210 log::debug!("I/O APIC: masked legacy IRQ{} (GSI{})", irq, gsi);
211}
212
213pub fn mask_irq(gsi: u32) {
215 let gsi_base = IOAPIC_GSI_BASE.load(Ordering::Relaxed);
216 if gsi < gsi_base {
217 return;
218 }
219 let index = gsi - gsi_base;
220 unsafe {
222 let entry = read_redir(index);
223 write_redir(index, entry | REDIR_MASK);
224 }
225}
226
227#[allow(dead_code)]
229pub fn unmask_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}