virt\aarch64/
gic_software_device.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! VPCI device implementation for GIC-based VMs.
5
6use crate::irqcon::ControlGic;
7use pci_core::msi::MsiControl;
8use pci_core::msi::MsiInterruptTarget;
9use std::ops::Range;
10use std::sync::Arc;
11use thiserror::Error;
12use vmcore::vpci_msi::MsiAddressData;
13use vmcore::vpci_msi::RegisterInterruptError;
14use vmcore::vpci_msi::VpciInterruptMapper;
15
16pub struct GicSoftwareDevice {
17    irqcon: Arc<dyn ControlGic>,
18}
19
20impl GicSoftwareDevice {
21    pub fn new(irqcon: Arc<dyn ControlGic>) -> Self {
22        Self { irqcon }
23    }
24}
25
26#[derive(Debug, Error)]
27enum GicInterruptError {
28    #[error("invalid vector count")]
29    InvalidVectorCount,
30    #[error("invalid vector")]
31    InvalidVector,
32}
33
34const SPI_RANGE: Range<u32> = 32..1020;
35
36impl VpciInterruptMapper for GicSoftwareDevice {
37    fn register_interrupt(
38        &self,
39        vector_count: u32,
40        params: &vmcore::vpci_msi::VpciInterruptParameters<'_>,
41    ) -> Result<MsiAddressData, RegisterInterruptError> {
42        if !vector_count.is_power_of_two() {
43            return Err(RegisterInterruptError::new(
44                GicInterruptError::InvalidVectorCount,
45            ));
46        }
47        if params.vector < SPI_RANGE.start
48            || params.vector.saturating_add(vector_count) > SPI_RANGE.end
49        {
50            return Err(RegisterInterruptError::new(
51                GicInterruptError::InvalidVector,
52            ));
53        }
54        Ok(MsiAddressData {
55            address: 0,
56            data: params.vector,
57        })
58    }
59
60    fn unregister_interrupt(&self, address: u64, data: u32) {
61        let _ = (address, data);
62    }
63}
64
65impl MsiInterruptTarget for GicSoftwareDevice {
66    fn new_interrupt(&self) -> Box<dyn MsiControl> {
67        let irqcon = self.irqcon.clone();
68        Box::new(move |_address, data| {
69            if SPI_RANGE.contains(&data) {
70                irqcon.set_spi_irq(data, true);
71            }
72        })
73    }
74}