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::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/// A set of software devices that can be used to implement VPCI devices on an
26/// APIC (x86) platform.
27///
28/// This is used to provide an indirection between the guest-specified device
29/// interrupt map and the actual MSIs that should be injected into the guest's
30/// APICs.
31#[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    /// Creates a new device with the given ID.
69    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    /// Retargets the interrupt for the given device.
92    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
123/// The software implementation of a VPCI-compatible device.
124pub 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/// The table of interrupts for a device.
138#[derive(Inspect)]
139struct InterruptTable {
140    #[inspect(iter_by_key)]
141    entries: Slab<InterruptEntry>,
142}
143
144/// State for an individual VPCI interrupt for a device.
145#[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        // Per Intel spec, set the upper bits to FEE.
191        // Set lower bits to the specified index, shifted to avoid the bits
192        // that actually mean something (redirection hint / destination mode).
193        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        // Check the rest of the VPs.
227        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        // TODO: the caller should specify the interrupt ID (needed for save/restore)
243        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}