virt/x86/
apic_software_device.rs1use hvdef::HvError;
8use hvdef::HvResult;
9use inspect::Inspect;
10use parking_lot::Mutex;
11use pci_core::msi::SignalMsi;
12use slab::Slab;
13use std::collections::HashMap;
14use std::collections::hash_map;
15use std::sync::Arc;
16use thiserror::Error;
17use vmcore::vpci_msi::MapVpciInterrupt;
18use vmcore::vpci_msi::MsiAddressData;
19use vmcore::vpci_msi::RegisterInterruptError;
20use vmcore::vpci_msi::VpciInterruptParameters;
21use x86defs::msi::MSI_ADDRESS;
22use x86defs::msi::MsiAddress;
23use x86defs::msi::MsiData;
24
25#[derive(Inspect)]
32pub struct ApicSoftwareDevices {
33 #[inspect(flatten)]
34 inner: Arc<DevicesInner>,
35}
36
37#[derive(Inspect)]
38struct DevicesInner {
39 #[inspect(flatten, with = "inspect_tables")]
40 tables: Mutex<HashMap<u64, Arc<Mutex<InterruptTable>>>>,
41 #[inspect(skip)]
42 apic_id_map: Vec<u32>,
43}
44
45fn inspect_tables(tables: &Mutex<HashMap<u64, Arc<Mutex<InterruptTable>>>>) -> impl '_ + Inspect {
46 inspect::adhoc(|req| {
47 let mut resp = req.respond();
48 for (device_id, table) in &*tables.lock() {
49 resp.field(&device_id.to_string(), &*table.lock());
50 }
51 })
52}
53
54#[derive(Debug, Error)]
55#[error("device id {0} is already in use")]
56pub struct DeviceIdInUse(u64);
57
58impl ApicSoftwareDevices {
59 pub fn new(apic_id_map: Vec<u32>) -> Self {
60 Self {
61 inner: Arc::new(DevicesInner {
62 tables: Default::default(),
63 apic_id_map,
64 }),
65 }
66 }
67
68 pub fn new_device(
70 &self,
71 target: Arc<dyn SignalMsi>,
72 device_id: u64,
73 ) -> Result<ApicSoftwareDevice, DeviceIdInUse> {
74 let table = Arc::new(Mutex::new(InterruptTable::new()));
75 {
76 let mut tables = self.inner.tables.lock();
77 let entry = match tables.entry(device_id) {
78 hash_map::Entry::Occupied(_) => return Err(DeviceIdInUse(device_id)),
79 hash_map::Entry::Vacant(e) => e,
80 };
81 entry.insert(table.clone());
82 }
83 Ok(ApicSoftwareDevice {
84 devices: self.inner.clone(),
85 target,
86 table,
87 id: device_id,
88 })
89 }
90
91 pub fn retarget_interrupt(
93 &self,
94 device_id: u64,
95 address: u64,
96 data: u32,
97 params: &VpciInterruptParameters<'_>,
98 ) -> HvResult<()> {
99 let table = self
100 .inner
101 .tables
102 .lock()
103 .get(&device_id)
104 .cloned()
105 .ok_or(HvError::InvalidDeviceId)?;
106
107 if let Err(err) =
108 table
109 .lock()
110 .retarget_interrupt(&self.inner.apic_id_map, address, data, params)
111 {
112 tracing::warn!(
113 error = &err as &dyn std::error::Error,
114 "retarget interrupt failure"
115 );
116 return Err(HvError::InvalidParameter);
117 }
118
119 Ok(())
120 }
121}
122
123pub struct ApicSoftwareDevice {
125 devices: Arc<DevicesInner>,
126 table: Arc<Mutex<InterruptTable>>,
127 target: Arc<dyn SignalMsi>,
128 id: u64,
129}
130
131impl Drop for ApicSoftwareDevice {
132 fn drop(&mut self) {
133 let _table = self.devices.tables.lock().remove(&self.id);
134 }
135}
136
137#[derive(Inspect)]
139struct InterruptTable {
140 #[inspect(iter_by_key)]
141 entries: Slab<InterruptEntry>,
142}
143
144#[derive(Debug, Inspect)]
146struct InterruptEntry {
147 base_vector: u32,
148 vector_count: u32,
149 multicast: bool,
150 target_apic_id: u32,
151}
152
153impl InterruptEntry {
154 fn msi_params(&self) -> MsiAddressData {
155 let address = MsiAddress::new()
156 .with_address(MSI_ADDRESS)
157 .with_virt_destination(self.target_apic_id as u16);
158 let data = MsiData::new().with_vector(self.base_vector as u8);
159 MsiAddressData {
160 address: u32::from(address).into(),
161 data: data.into(),
162 }
163 }
164}
165
166#[derive(Debug, Error)]
167enum InvalidInterruptParams {
168 #[error("invalid interrupt parameters")]
169 InvalidHypercallInput,
170 #[error("invalid virtual processor index {0}")]
171 InvalidVirtualProcessor(u32),
172}
173
174#[derive(Debug, Error)]
175enum InvalidRetargetParams {
176 #[error("invalid interrupt address {0:#x}")]
177 InvalidAddress(u64),
178 #[error("invalid virtual processor index {0}")]
179 InvalidVirtualProcessor(u32),
180}
181
182impl InterruptTable {
183 fn new() -> Self {
184 Self {
185 entries: Slab::new(),
186 }
187 }
188
189 fn interrupt_address_from_index(index: usize) -> u64 {
190 0xFEE00000 | ((index as u64) << 2)
194 }
195
196 fn interrupt_index_from_address(address: u64) -> usize {
197 ((address >> 2) & 0xffff) as usize
198 }
199
200 fn retarget_interrupt(
201 &mut self,
202 apic_id_map: &[u32],
203 address: u64,
204 _data: u32,
205 params: &VpciInterruptParameters<'_>,
206 ) -> Result<(), InvalidRetargetParams> {
207 let index = Self::interrupt_index_from_address(address);
208
209 let interrupt = self
210 .entries
211 .get_mut(index)
212 .ok_or(InvalidRetargetParams::InvalidAddress(address))?;
213
214 interrupt.base_vector = params.vector;
215 interrupt.multicast = params.multicast;
216 let mut iter = params.target_processors.iter().map(|&vp_index| {
217 apic_id_map
218 .get(vp_index as usize)
219 .copied()
220 .ok_or(InvalidRetargetParams::InvalidVirtualProcessor(vp_index))
221 });
222 if let Some(target_apic_id) = iter.next() {
223 interrupt.target_apic_id = target_apic_id?;
224 }
225
226 iter.map(|x| x.map(drop)).collect::<Result<Vec<()>, _>>()?;
228
229 Ok(())
230 }
231
232 fn register_interrupt(
233 &mut self,
234 apic_id_map: &[u32],
235 vector_count: u32,
236 params: &VpciInterruptParameters<'_>,
237 ) -> Result<MsiAddressData, InvalidInterruptParams> {
238 if vector_count == 0 || params.target_processors.is_empty() {
239 return Err(InvalidInterruptParams::InvalidHypercallInput);
240 }
241
242 let vp = params.target_processors[0];
244 let i = self.entries.insert(InterruptEntry {
245 base_vector: params.vector,
246 vector_count,
247 multicast: params.multicast,
248 target_apic_id: *apic_id_map
249 .get(vp as usize)
250 .ok_or(InvalidInterruptParams::InvalidVirtualProcessor(vp))?,
251 });
252 let address = Self::interrupt_address_from_index(i);
253 Ok(MsiAddressData { address, data: 0 })
254 }
255
256 fn unregister_interrupt(&mut self, address: u64, _data: u32) {
257 let index = Self::interrupt_index_from_address(address);
258 self.entries.remove(index);
259 }
260}
261
262impl SignalMsi for ApicSoftwareDevice {
263 fn signal_msi(&self, _rid: u32, address: u64, _data: u32) {
264 let mut table = self.table.lock();
265 let table = &mut *table;
266 let index = InterruptTable::interrupt_index_from_address(address);
267 if let Some(interrupt) = table.entries.get(index) {
268 let target = interrupt.msi_params();
269 self.target.signal_msi(0, target.address, target.data)
270 }
271 }
272}
273
274impl MapVpciInterrupt for ApicSoftwareDevice {
275 async fn register_interrupt(
276 &self,
277 vector_count: u32,
278 params: &VpciInterruptParameters<'_>,
279 ) -> Result<MsiAddressData, RegisterInterruptError> {
280 self.table
281 .lock()
282 .register_interrupt(&self.devices.apic_id_map, vector_count, params)
283 .map_err(RegisterInterruptError::new)
284 }
285
286 async fn unregister_interrupt(&self, address: u64, data: u32) {
287 self.table.lock().unregister_interrupt(address, data)
288 }
289}