Nothing to see here, move along meow
1use crate::error::KernelError;
2use crate::mem::phys::BitmapFrameAllocator;
3use crate::mem::virt;
4use crate::pci::config::{self, EcamAddress};
5use crate::pci::device::BarInfo;
6use crate::sync::IrqMutex;
7use x86_64::structures::paging::OffsetPageTable;
8
9use crate::arch::idt::IrqVector;
10
11const MAX_MSIX_DEVICES: usize = 8;
12
13#[derive(Clone, Copy)]
14struct MsixTableMapping {
15 table_virt: u64,
16 table_entries: u16,
17}
18
19static MSIX_STATE: IrqMutex<[Option<MsixTableMapping>; MAX_MSIX_DEVICES], 3> =
20 IrqMutex::new([None; MAX_MSIX_DEVICES]);
21
22pub fn ensure_table_mapped(
23 device_table_idx: u8,
24 mapper: &mut OffsetPageTable,
25 allocator: &mut BitmapFrameAllocator,
26 hhdm_offset: u64,
27) -> Result<(), KernelError> {
28 let idx = device_table_idx as usize;
29 {
30 let state = MSIX_STATE.lock();
31 if state.get(idx).and_then(|s| s.as_ref()).is_some() {
32 return Ok(());
33 }
34 }
35
36 let dev_table = crate::pci::DEVICE_TABLE.lock();
37 let dev = dev_table.get(idx).ok_or(KernelError::InvalidObject)?;
38 let msix_cap = dev.msix_cap.ok_or(KernelError::InvalidParameter)?;
39 let bar = dev.bars[msix_cap.table_bir as usize];
40 drop(dev_table);
41
42 let phys_base = match bar {
43 BarInfo::Memory { phys_base, .. } => phys_base,
44 _ => return Err(KernelError::InvalidParameter),
45 };
46
47 let table_phys = phys_base + msix_cap.table_offset as u64;
48 let table_byte_size = (msix_cap.table_size as u64) * 16;
49 let page_count = table_byte_size.div_ceil(4096);
50
51 (0..page_count).try_for_each(|i| {
52 let page_phys = (table_phys & !0xFFF) + i * 4096;
53 virt::map_mmio(mapper, allocator, page_phys, hhdm_offset)
54 })?;
55
56 let table_virt = table_phys + hhdm_offset;
57
58 let mut state = MSIX_STATE.lock();
59 match state.get_mut(idx) {
60 Some(slot) => {
61 *slot = Some(MsixTableMapping {
62 table_virt,
63 table_entries: msix_cap.table_size,
64 });
65 Ok(())
66 }
67 None => Err(KernelError::ResourceExhausted),
68 }
69}
70
71pub fn configure_entry(
72 device_table_idx: u8,
73 entry_idx: u16,
74 vector: IrqVector,
75 dest_apic_id: u8,
76) -> Result<(), KernelError> {
77 let state = MSIX_STATE.lock();
78 let mapping = state
79 .get(device_table_idx as usize)
80 .and_then(|s| s.as_ref())
81 .ok_or(KernelError::BadState)?;
82
83 if entry_idx >= mapping.table_entries {
84 return Err(KernelError::InvalidParameter);
85 }
86
87 let entry_base = mapping.table_virt + (entry_idx as u64) * 16;
88
89 let msg_addr: u32 = 0xFEE0_0000 | ((dest_apic_id as u32) << 12);
90 let msg_data: u32 = vector.raw() as u32;
91 let vector_control: u32 = 1;
92
93 unsafe {
94 core::ptr::write_volatile(entry_base as *mut u32, msg_addr);
95 core::ptr::write_volatile((entry_base + 4) as *mut u32, 0);
96 core::ptr::write_volatile((entry_base + 8) as *mut u32, msg_data);
97 core::ptr::write_volatile((entry_base + 12) as *mut u32, vector_control);
98 }
99
100 Ok(())
101}
102
103pub fn unmask_entry(device_table_idx: u8, entry_idx: u16) -> Result<(), KernelError> {
104 let state = MSIX_STATE.lock();
105 let mapping = state
106 .get(device_table_idx as usize)
107 .and_then(|s| s.as_ref())
108 .ok_or(KernelError::BadState)?;
109
110 if entry_idx >= mapping.table_entries {
111 return Err(KernelError::InvalidParameter);
112 }
113
114 let vc_addr = mapping.table_virt + (entry_idx as u64) * 16 + 12;
115 unsafe {
116 let current = core::ptr::read_volatile(vc_addr as *const u32);
117 core::ptr::write_volatile(vc_addr as *mut u32, current & !1);
118 }
119
120 Ok(())
121}
122
123#[allow(dead_code)]
124pub fn mask_entry(device_table_idx: u8, entry_idx: u16) -> Result<(), KernelError> {
125 let state = MSIX_STATE.lock();
126 let mapping = state
127 .get(device_table_idx as usize)
128 .and_then(|s| s.as_ref())
129 .ok_or(KernelError::BadState)?;
130
131 if entry_idx >= mapping.table_entries {
132 return Err(KernelError::InvalidParameter);
133 }
134
135 let vc_addr = mapping.table_virt + (entry_idx as u64) * 16 + 12;
136 unsafe {
137 let current = core::ptr::read_volatile(vc_addr as *const u32);
138 core::ptr::write_volatile(vc_addr as *mut u32, current | 1);
139 }
140
141 Ok(())
142}
143
144pub fn enable_msix(device_table_idx: u8) -> Result<(), KernelError> {
145 let dev_table = crate::pci::DEVICE_TABLE.lock();
146 let dev = dev_table
147 .get(device_table_idx as usize)
148 .ok_or(KernelError::InvalidObject)?;
149 let msix_cap = dev.msix_cap.ok_or(KernelError::InvalidParameter)?;
150
151 let mcfg_entries = crate::acpi::mcfg::cached_mcfg_entries();
152 let ecam_base = mcfg_entries
153 .iter()
154 .find(|e| dev.bus >= e.start_bus && dev.bus <= e.end_bus)
155 .map(|e| e.base_address)
156 .ok_or(KernelError::InvalidObject)?;
157
158 let addr = EcamAddress::new(ecam_base, dev.bus, dev.device, dev.function)
159 .ok_or(KernelError::InvalidParameter)?;
160 drop(dev_table);
161
162 let cap_dword_offset = msix_cap.cap_offset & !0x3;
163 let current = config::read_config_u32(addr, cap_dword_offset);
164 let enabled = current | (1 << 31);
165 let unmasked = enabled & !(1 << 30);
166 config::write_config_u32(addr, cap_dword_offset, unmasked);
167
168 Ok(())
169}