virt/x86/
apic_software_device.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Software implementation of a VPCI-compatible device. Avoids using the
5//! hypervisor device interface.
6
7use hvdef::HvError;
8use hvdef::HvResult;
9use inspect::Inspect;
10use parking_lot::Mutex;
11use pci_core::msi::MsiControl;
12use pci_core::msi::MsiInterruptTarget;
13use slab::Slab;
14use std::collections::HashMap;
15use std::collections::hash_map;
16use std::sync::Arc;
17use thiserror::Error;
18use vmcore::vpci_msi::MsiAddressData;
19use vmcore::vpci_msi::RegisterInterruptError;
20use vmcore::vpci_msi::VpciInterruptMapper;
21use vmcore::vpci_msi::VpciInterruptParameters;
22use x86defs::msi::MSI_ADDRESS;
23use x86defs::msi::MsiAddress;
24use x86defs::msi::MsiData;
25
26/// A set of software devices that can be used to implement VPCI devices on an
27/// APIC (x86) platform.
28///
29/// This is used to provide an indirection between the guest-specified device
30/// interrupt map and the actual MSIs that should be injected into the guest's
31/// APICs.
32#[derive(Inspect)]
33pub struct ApicSoftwareDevices {
34    #[inspect(flatten)]
35    inner: Arc<DevicesInner>,
36}
37
38#[derive(Inspect)]
39struct DevicesInner {
40    #[inspect(flatten, with = "inspect_tables")]
41    tables: Mutex<HashMap<u64, Arc<Mutex<InterruptTable>>>>,
42    #[inspect(skip)]
43    apic_id_map: Vec<u32>,
44}
45
46fn inspect_tables(tables: &Mutex<HashMap<u64, Arc<Mutex<InterruptTable>>>>) -> impl '_ + Inspect {
47    inspect::adhoc(|req| {
48        let mut resp = req.respond();
49        for (device_id, table) in &*tables.lock() {
50            resp.field(&device_id.to_string(), &*table.lock());
51        }
52    })
53}
54
55#[derive(Debug, Error)]
56#[error("device id {0} is already in use")]
57pub struct DeviceIdInUse(u64);
58
59impl ApicSoftwareDevices {
60    pub fn new(apic_id_map: Vec<u32>) -> Self {
61        Self {
62            inner: Arc::new(DevicesInner {
63                tables: Default::default(),
64                apic_id_map,
65            }),
66        }
67    }
68
69    /// Creates a new device with the given ID.
70    pub fn new_device(
71        &self,
72        target: Arc<dyn MsiInterruptTarget>,
73        device_id: u64,
74    ) -> Result<ApicSoftwareDevice, DeviceIdInUse> {
75        let table = Arc::new(Mutex::new(InterruptTable::new()));
76        {
77            let mut tables = self.inner.tables.lock();
78            let entry = match tables.entry(device_id) {
79                hash_map::Entry::Occupied(_) => return Err(DeviceIdInUse(device_id)),
80                hash_map::Entry::Vacant(e) => e,
81            };
82            entry.insert(table.clone());
83        }
84        Ok(ApicSoftwareDevice {
85            devices: self.inner.clone(),
86            target,
87            table,
88            id: device_id,
89        })
90    }
91
92    /// Retargets the interrupt for the given device.
93    pub fn retarget_interrupt(
94        &self,
95        device_id: u64,
96        address: u64,
97        data: u32,
98        params: &VpciInterruptParameters<'_>,
99    ) -> HvResult<()> {
100        let table = self
101            .inner
102            .tables
103            .lock()
104            .get(&device_id)
105            .cloned()
106            .ok_or(HvError::InvalidDeviceId)?;
107
108        if let Err(err) =
109            table
110                .lock()
111                .retarget_interrupt(&self.inner.apic_id_map, address, data, params)
112        {
113            tracing::warn!(
114                error = &err as &dyn std::error::Error,
115                "retarget interrupt failure"
116            );
117            return Err(HvError::InvalidParameter);
118        }
119
120        Ok(())
121    }
122}
123
124/// The software implementation of a VPCI-compatible device.
125pub struct ApicSoftwareDevice {
126    devices: Arc<DevicesInner>,
127    table: Arc<Mutex<InterruptTable>>,
128    target: Arc<dyn MsiInterruptTarget>,
129    id: u64,
130}
131
132impl Drop for ApicSoftwareDevice {
133    fn drop(&mut self) {
134        let _table = self.devices.tables.lock().remove(&self.id);
135    }
136}
137
138/// The table of interrupts for a device.
139#[derive(Inspect)]
140struct InterruptTable {
141    #[inspect(iter_by_key)]
142    entries: Slab<InterruptEntry>,
143    #[inspect(iter_by_key)]
144    msis: Slab<Msi>,
145}
146
147/// State for an individual VPCI interrupt for a device.
148#[derive(Debug, Inspect)]
149struct InterruptEntry {
150    base_vector: u32,
151    vector_count: u32,
152    multicast: bool,
153    target_apic_id: u32,
154}
155
156impl InterruptEntry {
157    fn msi_params(&self) -> MsiAddressData {
158        let address = MsiAddress::new()
159            .with_address(MSI_ADDRESS)
160            .with_virt_destination(self.target_apic_id as u16);
161        let data = MsiData::new().with_vector(self.base_vector as u8);
162        MsiAddressData {
163            address: u32::from(address).into(),
164            data: data.into(),
165        }
166    }
167}
168
169#[derive(Inspect)]
170struct Msi {
171    address: u64,
172    data: u32,
173    #[inspect(skip)]
174    control: Box<dyn MsiControl>,
175}
176
177#[derive(Debug, Error)]
178enum InvalidInterruptParams {
179    #[error("invalid interrupt parameters")]
180    InvalidHypercallInput,
181    #[error("invalid virtual processor index {0}")]
182    InvalidVirtualProcessor(u32),
183}
184
185#[derive(Debug, Error)]
186enum InvalidRetargetParams {
187    #[error("invalid interrupt address {0:#x}")]
188    InvalidAddress(u64),
189    #[error("invalid virtual processor index {0}")]
190    InvalidVirtualProcessor(u32),
191}
192
193impl InterruptTable {
194    fn new() -> Self {
195        Self {
196            entries: Slab::new(),
197            msis: Slab::new(),
198        }
199    }
200
201    fn interrupt_address_from_index(index: usize) -> u64 {
202        // Per Intel spec, set the upper bits to FEE.
203        // Set lower bits to the specified index, shifted to avoid the bits
204        // that actually mean something (redirection hint / destination mode).
205        0xFEE00000 | ((index as u64) << 2)
206    }
207
208    fn interrupt_index_from_address(address: u64) -> usize {
209        ((address >> 2) & 0xffff) as usize
210    }
211
212    fn retarget_interrupt(
213        &mut self,
214        apic_id_map: &[u32],
215        address: u64,
216        _data: u32,
217        params: &VpciInterruptParameters<'_>,
218    ) -> Result<(), InvalidRetargetParams> {
219        let index = Self::interrupt_index_from_address(address);
220
221        let interrupt = self
222            .entries
223            .get_mut(index)
224            .ok_or(InvalidRetargetParams::InvalidAddress(address))?;
225
226        interrupt.base_vector = params.vector;
227        interrupt.multicast = params.multicast;
228        let mut iter = params.target_processors.iter().map(|&vp_index| {
229            apic_id_map
230                .get(vp_index as usize)
231                .copied()
232                .ok_or(InvalidRetargetParams::InvalidVirtualProcessor(vp_index))
233        });
234        if let Some(target_apic_id) = iter.next() {
235            interrupt.target_apic_id = target_apic_id?;
236        }
237
238        // Check the rest of the VPs.
239        iter.map(|x| x.map(drop)).collect::<Result<Vec<()>, _>>()?;
240
241        let target = interrupt.msi_params();
242        for (_, msi) in &mut self.msis {
243            if msi.address == address {
244                msi.control.enable(target.address, target.data);
245            }
246        }
247        Ok(())
248    }
249
250    fn register_interrupt(
251        &mut self,
252        apic_id_map: &[u32],
253        vector_count: u32,
254        params: &VpciInterruptParameters<'_>,
255    ) -> Result<MsiAddressData, InvalidInterruptParams> {
256        if vector_count == 0 || params.target_processors.is_empty() {
257            return Err(InvalidInterruptParams::InvalidHypercallInput);
258        }
259
260        // TODO: the caller should specify the interrupt ID (needed for save/restore)
261        let vp = params.target_processors[0];
262        let i = self.entries.insert(InterruptEntry {
263            base_vector: params.vector,
264            vector_count,
265            multicast: params.multicast,
266            target_apic_id: *apic_id_map
267                .get(vp as usize)
268                .ok_or(InvalidInterruptParams::InvalidVirtualProcessor(vp))?,
269        });
270        let address = Self::interrupt_address_from_index(i);
271        Ok(MsiAddressData { address, data: 0 })
272    }
273
274    fn unregister_interrupt(&mut self, address: u64, _data: u32) {
275        let index = Self::interrupt_index_from_address(address);
276        self.entries.remove(index);
277        for (_, msi) in &mut self.msis {
278            if msi.address == address {
279                msi.control.disable();
280            }
281        }
282    }
283}
284
285struct DeviceInterrupt {
286    table: Arc<Mutex<InterruptTable>>,
287    idx: usize,
288}
289
290impl DeviceInterrupt {
291    fn new(table: Arc<Mutex<InterruptTable>>, control: Box<dyn MsiControl>) -> Self {
292        let idx = table.lock().msis.insert(Msi {
293            address: !0,
294            data: 0,
295            control,
296        });
297        Self { table, idx }
298    }
299}
300
301impl MsiControl for DeviceInterrupt {
302    fn enable(&mut self, address: u64, data: u32) {
303        let mut table = self.table.lock();
304        let table = &mut *table;
305        let msi = &mut table.msis[self.idx];
306        msi.address = address;
307        msi.data = data;
308        let index = InterruptTable::interrupt_index_from_address(address);
309        if let Some(interrupt) = table.entries.get(index) {
310            let target = interrupt.msi_params();
311            msi.control.enable(target.address, target.data);
312        } else {
313            msi.control.disable();
314        }
315    }
316
317    fn disable(&mut self) {
318        let mut table = self.table.lock();
319        table.msis[self.idx].control.disable();
320    }
321
322    fn signal(&mut self, address: u64, _data: u32) {
323        // TODO: don't lock the whole table
324        let mut table = self.table.lock();
325        let table = &mut *table;
326        let index = InterruptTable::interrupt_index_from_address(address);
327        let msi = &mut table.msis[self.idx];
328        if let Some(interrupt) = table.entries.get(index) {
329            let target = interrupt.msi_params();
330            msi.control.signal(target.address, target.data)
331        }
332    }
333}
334
335impl Drop for DeviceInterrupt {
336    fn drop(&mut self) {
337        self.table.lock().msis.remove(self.idx);
338    }
339}
340
341impl MsiInterruptTarget for ApicSoftwareDevice {
342    fn new_interrupt(&self) -> Box<dyn MsiControl> {
343        Box::new(DeviceInterrupt::new(
344            self.table.clone(),
345            self.target.new_interrupt(),
346        ))
347    }
348}
349
350impl VpciInterruptMapper for ApicSoftwareDevice {
351    fn register_interrupt(
352        &self,
353        vector_count: u32,
354        params: &VpciInterruptParameters<'_>,
355    ) -> Result<MsiAddressData, RegisterInterruptError> {
356        self.table
357            .lock()
358            .register_interrupt(&self.devices.apic_id_map, vector_count, params)
359            .map_err(RegisterInterruptError::new)
360    }
361
362    fn unregister_interrupt(&self, address: u64, data: u32) {
363        self.table.lock().unregister_interrupt(address, data)
364    }
365}