strat9_kernel/arch/x86_64/
msi.rs1use crate::{arch::x86_64::pci::*, hardware::pci_client::PciDevice};
11
12fn enable_msi(pci_dev: &PciDevice, vector: u8) -> bool {
25 let Some(cap) = pci_dev.msi_cap_offset() else {
26 return false;
27 };
28
29 let ctrl = pci_dev.read_config_u16(cap + msi_cap::CONTROL);
30 let addr64 = (ctrl & msi_ctrl::ADDR64) != 0;
31 let lapic_id = crate::arch::x86_64::apic::lapic_id();
32
33 let msg_addr = MSI_ADDR_BASE | (lapic_id << MSI_ADDR_DEST_SHIFT);
35
36 let msg_data = vector as u16;
38
39 pci_dev.write_config_u32(cap + msi_cap::ADDR_LOW, msg_addr);
41
42 if addr64 {
43 pci_dev.write_config_u32(cap + msi_cap::ADDR_HIGH, 0);
45 pci_dev.write_config_u16(cap + msi_cap::DATA + 4, msg_data);
46 } else {
47 pci_dev.write_config_u16(cap + msi_cap::DATA, msg_data);
49 }
50
51 let new_ctrl = ctrl & !msi_ctrl::MME_MASK | msi_ctrl::ENABLE;
54 pci_dev.write_config_u16(cap + msi_cap::CONTROL, new_ctrl);
55
56 let verify = pci_dev.read_config_u16(cap + msi_cap::CONTROL);
58 if verify & msi_ctrl::ENABLE == 0 {
59 log::warn!(
60 "MSI: enable bit did not stick on {:04x}:{:04x}",
61 pci_dev.vendor_id,
62 pci_dev.device_id
63 );
64 return false;
65 }
66
67 let mut cmd = pci_dev.read_config_u16(config::COMMAND);
70 cmd |= command::INTERRUPT_DISABLE;
71 pci_dev.write_config_u16(config::COMMAND, cmd);
72
73 log::info!(
74 "MSI: enabled on {:04x}:{:04x} → vector {:#x} ({} addr)",
75 pci_dev.vendor_id,
76 pci_dev.device_id,
77 vector,
78 if addr64 { "64-bit" } else { "32-bit" },
79 );
80 true
81}
82
83fn enable_msix(pci_dev: &PciDevice, vector: u8) -> bool {
98 let Some(cap) = pci_dev.msix_cap_offset() else {
99 return false;
100 };
101
102 let ctrl = pci_dev.read_config_u16(cap + msix_cap::CONTROL);
103 let table_size = (ctrl & msix_ctrl::TABLE_SIZE_MASK) as usize;
104 if table_size == 0 {
105 log::warn!(
106 "MSI-X: {:04x}:{:04x} has zero-sized table",
107 pci_dev.vendor_id,
108 pci_dev.device_id
109 );
110 return false;
111 }
112
113 let table_reg = pci_dev.read_config_u32(cap + msix_cap::TABLE);
115 let bar_index = (table_reg & 0x7) as u8;
116 let tbl_offset = (table_reg & 0xFFFF_FFF8) as u64;
117
118 let bar = match pci_dev.read_bar(bar_index) {
120 Some(bar) => bar,
121 None => {
122 log::warn!(
123 "MSI-X: {:04x}:{:04x} table BAR {} invalid",
124 pci_dev.vendor_id,
125 pci_dev.device_id,
126 bar_index
127 );
128 return false;
129 }
130 };
131
132 let (bar_phys, _bar_size) = match bar {
133 crate::hardware::pci_client::Bar::Memory32 { addr, .. } => (addr as u64, 0x1000),
134 crate::hardware::pci_client::Bar::Memory64 { addr, .. } => (addr, 0x1000),
135 _ => {
136 log::warn!(
137 "MSI-X: {:04x}:{:04x} table BAR is I/O space",
138 pci_dev.vendor_id,
139 pci_dev.device_id
140 );
141 return false;
142 }
143 };
144
145 let table_phys = bar_phys + tbl_offset;
147 crate::memory::paging::ensure_identity_map_range(table_phys, 0x1000);
148 let table_virt = crate::memory::phys_to_virt(table_phys) as *mut u32;
149
150 let lapic_id = crate::arch::x86_64::apic::lapic_id();
151 let msg_addr = MSI_ADDR_BASE | (lapic_id << MSI_ADDR_DEST_SHIFT);
152 let msg_data = vector as u32; unsafe {
159 core::ptr::write_volatile(table_virt, msg_addr);
161 core::ptr::write_volatile(table_virt.add(1), 0u32);
163 core::ptr::write_volatile(table_virt.add(2), msg_data);
165 core::ptr::write_volatile(table_virt.add(3), 0u32);
167 }
168
169 let new_ctrl = (ctrl | msix_ctrl::ENABLE) & !msix_ctrl::FUNC_MASK;
171 pci_dev.write_config_u16(cap + msix_cap::CONTROL, new_ctrl);
172
173 let mut cmd = pci_dev.read_config_u16(config::COMMAND);
175 cmd |= command::INTERRUPT_DISABLE;
176 pci_dev.write_config_u16(config::COMMAND, cmd);
177
178 log::info!(
179 "MSI-X: enabled on {:04x}:{:04x} → vector {:#x} ({} entries, table BAR{}+{:#x})",
180 pci_dev.vendor_id,
181 pci_dev.device_id,
182 vector,
183 table_size + 1,
184 bar_index,
185 tbl_offset,
186 );
187 true
188}
189
190pub fn probe_and_enable(pci_dev: &PciDevice, prefer_msix: bool) -> (u8, u8) {
203 let irq_line = pci_dev.interrupt_line;
204 let vector = if irq_line < 16 && irq_line != 0 && irq_line != 0xFF {
205 0x20 + irq_line
206 } else if irq_line != 0 && irq_line != 0xFF {
207 irq_line
208 } else {
209 0x20 };
211
212 if prefer_msix && enable_msix(pci_dev, vector) {
214 return (irq_line, vector);
215 }
216
217 if enable_msi(pci_dev, vector) {
219 return (irq_line, vector);
220 }
221
222 log::info!(
224 "MSI/MSI-X unavailable on {:04x}:{:04x}, falling back to INTx IRQ {}",
225 pci_dev.vendor_id,
226 pci_dev.device_id,
227 irq_line,
228 );
229 (irq_line, vector)
230}