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::MapVpciInterrupt;
13use vmcore::vpci_msi::MsiAddressData;
14use vmcore::vpci_msi::RegisterInterruptError;
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 {0}")]
29    InvalidVectorCount(u32),
30    #[error("invalid {count} vectors at {start}")]
31    InvalidVector { start: u32, count: u32 },
32}
33
34const SPI_RANGE: Range<u32> = 32..1020;
35
36impl MapVpciInterrupt for GicSoftwareDevice {
37    async 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(vector_count),
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                    start: params.vector,
53                    count: vector_count,
54                },
55            ));
56        }
57        Ok(MsiAddressData {
58            address: 0,
59            data: params.vector,
60        })
61    }
62
63    async fn unregister_interrupt(&self, address: u64, data: u32) {
64        let _ = (address, data);
65    }
66}
67
68impl MsiInterruptTarget for GicSoftwareDevice {
69    fn new_interrupt(&self) -> Box<dyn MsiControl> {
70        let irqcon = self.irqcon.clone();
71        Box::new(move |_address, data| {
72            if SPI_RANGE.contains(&data) {
73                irqcon.set_spi_irq(data, true);
74            }
75        })
76    }
77}