dbs_interrupt/
lib.rs

1// Copyright (C) 2019-2020 Alibaba Cloud. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Traits and Structs to manage interrupt sources for devices.
5//!
6//! Software indicating an event that needs immediate attention. An interrupt alerts the processor
7//! to a high-priority condition requiring the interruption of the current code the processor is
8//! executing. The processor responds by suspending its current activities, saving its state, and
9//! executing a function called an interrupt handler (or an interrupt service routine, ISR) to deal
10//! with the event. This interruption is temporary, and, after the interrupt handler finishes,
11//! unless handling the interrupt has emitted a fatal error, the processor resumes normal
12//! activities.
13//!
14//! Hardware interrupts are used by devices to communicate that they require attention from the
15//! operating system, or a bare-metal program running on the CPU if there are no OSes. The act of
16//! initiating a hardware interrupt is referred to as an interrupt request (IRQ). Different devices
17//! are usually associated with different interrupts using a unique value associated with each
18//! interrupt. This makes it possible to know which hardware device caused which interrupts. These
19//! interrupt values are often called IRQ lines, or just interrupt lines.
20//!
21//! Nowadays, IRQ lines is not the only mechanism to deliver device interrupts to processors. MSI
22//! [(Message Signaled Interrupt)](https://en.wikipedia.org/wiki/Message_Signaled_Interrupts) is
23//! another commonly used alternative in-band method of signaling an interrupt, using special
24//! in-band messages to replace traditional out-of-band assertion of dedicated interrupt lines.
25//! While more complex to implement in a device, message signaled interrupts have some significant
26//! advantages over pin-based out-of-band interrupt signaling. Message signaled interrupts are
27//! supported in PCI bus since its version 2.2, and in later available PCI Express bus. Some non-PCI
28//! architectures also use message signaled interrupts.
29//!
30//! While IRQ is a term commonly used by Operating Systems when dealing with hardware interrupts,
31//! the IRQ numbers managed by OSes are independent of the ones managed by VMM. For simplicity sake,
32//! the term `Interrupt Source` is used instead of IRQ to represent both pin-based interrupts and
33//! MSI interrupts.
34//!
35//! A device may support multiple types of interrupts, and each type of interrupt may support one or
36//! multiple interrupt sources. For example, a PCI device may support:
37//! * Legacy Irq: exactly one interrupt source.
38//! * PCI MSI Irq: 1,2,4,8,16,32 interrupt sources.
39//! * PCI MSIx Irq: 2^n(n=0-11) interrupt sources.
40//!
41//! A distinct Interrupt Source Identifier (ISID) will be assigned to each interrupt source. An ID
42//! allocator will be used to allocate and free Interrupt Source Identifiers for devices. To
43//! decouple this crate from the ID allocator, here we doesn't take the responsibility to
44//! allocate/free Interrupt Source IDs but only makes use of assigned IDs.
45//!
46//! The overall flow to deal with interrupts is:
47//! * the VMM creates an interrupt manager
48//! * the VMM creates a device manager, passing on an reference to the interrupt manager
49//! * the device manager passes on an reference to the interrupt manager to all registered devices
50//! * guest kernel loads drivers for virtual devices
51//! * guest device driver determines the type and number of interrupts needed, and update the device
52//!   configuration
53//! * the virtual device backend requests the interrupt manager to create an interrupt group
54//!   according to guest configuration information
55
56use std::io::Error;
57use std::ops::Deref;
58use std::sync::Arc;
59
60use vmm_sys_util::eventfd::EventFd;
61
62mod manager;
63pub use manager::MSI_DEVICE_ID_SHIFT;
64pub use manager::{DeviceInterruptManager, DeviceInterruptMode, InterruptStatusRegister32};
65
66mod notifier;
67pub use self::notifier::*;
68
69#[cfg(feature = "kvm-irq")]
70pub mod kvm;
71#[cfg(feature = "kvm-irq")]
72pub use self::kvm::KvmIrqManager;
73
74/// Reuse std::io::Result to simplify interoperability among crates.
75pub type Result<T> = std::io::Result<T>;
76
77/// Data type to store an interrupt source identifier.
78pub type InterruptIndex = u32;
79
80/// Type of interrupt source.
81#[derive(Clone, Eq, PartialEq, Debug)]
82pub enum InterruptSourceType {
83    #[cfg(feature = "legacy-irq")]
84    /// Legacy Pin-based Interrupt.
85    /// On x86 platforms, legacy interrupts are routed through 8259 PICs and/or IOAPICs.
86    LegacyIrq,
87    #[cfg(feature = "msi-irq")]
88    /// Message Signaled Interrupt (PCI MSI/PCI MSIx etc).
89    /// Some non-PCI devices (like HPET on x86) make use of generic MSI in platform specific ways.
90    MsiIrq,
91}
92
93/// Configuration data for an interrupt source.
94#[derive(Clone, Debug, Eq, PartialEq)]
95pub enum InterruptSourceConfig {
96    #[cfg(feature = "legacy-irq")]
97    /// Configuration data for Legacy interrupts.
98    LegacyIrq(LegacyIrqSourceConfig),
99    #[cfg(feature = "msi-irq")]
100    /// Configuration data for PciMsi, PciMsix and generic MSI interrupts.
101    MsiIrq(MsiIrqSourceConfig),
102}
103
104/// Configuration data for legacy interrupts.
105///
106/// On x86 platforms, legacy interrupts means those interrupts routed through PICs or IOAPICs.
107#[cfg(feature = "legacy-irq")]
108#[derive(Clone, Debug, Eq, PartialEq)]
109pub struct LegacyIrqSourceConfig {}
110
111/// Configuration data for GenericMsi, PciMsi, PciMsix interrupts.
112#[cfg(feature = "msi-irq")]
113#[derive(Default, Clone, Debug, Eq, PartialEq)]
114pub struct MsiIrqSourceConfig {
115    /// High address to deliver message signaled interrupt.
116    pub high_addr: u32,
117    /// Low address to deliver message signaled interrupt.
118    pub low_addr: u32,
119    /// Data to write to deliver message signaled interrupt.
120    pub data: u32,
121    /// Interrupt control state.
122    pub msg_ctl: u32,
123    /// Device id indicate the device who triggers this msi irq.
124    pub device_id: Option<u32>,
125}
126
127/// Trait to manage interrupt sources for virtual device backends.
128///
129/// The InterruptManager implementations should protect itself from concurrent accesses internally,
130/// so it could be invoked from multi-threaded context.
131pub trait InterruptManager {
132    /// Create an [InterruptSourceGroup](trait.InterruptSourceGroup.html) object to manage interrupt
133    /// sources for a virtual device.
134    ///
135    /// An [InterruptSourceGroup](trait.InterruptSourceGroup.html) object manages all interrupt
136    /// sources of the same type for a virtual device.
137    ///
138    /// # Arguments
139    /// * type_: type of interrupt source.
140    /// * base: base Interrupt Source ID to be managed by the group object.
141    /// * count: number of Interrupt Sources to be managed by the group object.
142    fn create_group(
143        &self,
144        type_: InterruptSourceType,
145        base: InterruptIndex,
146        count: InterruptIndex,
147    ) -> Result<Arc<Box<dyn InterruptSourceGroup>>>;
148
149    /// Destroy an [InterruptSourceGroup](trait.InterruptSourceGroup.html) object created by
150    /// [create_group()](trait.InterruptManager.html#tymethod.create_group).
151    ///
152    /// Assume the caller takes the responsibility to disable all interrupt sources of the group
153    /// before calling destroy_group(). This assumption helps to simplify InterruptSourceGroup
154    /// implementations.
155    fn destroy_group(&self, group: Arc<Box<dyn InterruptSourceGroup>>) -> Result<()>;
156}
157
158impl<T: InterruptManager> InterruptManager for Arc<T> {
159    fn create_group(
160        &self,
161        type_: InterruptSourceType,
162        base: u32,
163        count: u32,
164    ) -> std::result::Result<Arc<Box<dyn InterruptSourceGroup>>, Error> {
165        self.deref().create_group(type_, base, count)
166    }
167
168    fn destroy_group(
169        &self,
170        group: Arc<Box<dyn InterruptSourceGroup>>,
171    ) -> std::result::Result<(), Error> {
172        self.deref().destroy_group(group)
173    }
174}
175
176/// Trait to manage a group of interrupt sources for a device.
177///
178/// A device may support several types of interrupts, and each type of interrupt may contain one or
179/// multiple continuous interrupt sources. For example, a PCI device may concurrently support:
180/// * Legacy Irq: exactly one interrupt source.
181/// * PCI MSI Irq: 1,2,4,8,16,32 interrupt sources.
182/// * PCI MSIx Irq: 2^n(n=0-11) interrupt sources.
183///
184/// PCI MSI interrupts of a device may not be configured individually, and must configured as a
185/// whole block. So all interrupts of the same type of a device are abstracted as an
186/// [InterruptSourceGroup](trait.InterruptSourceGroup.html) object, instead of abstracting each
187/// interrupt source as a distinct InterruptSource.
188#[allow(clippy::len_without_is_empty)]
189pub trait InterruptSourceGroup: Send + Sync {
190    /// Get type of interrupt sources managed by the group.
191    fn interrupt_type(&self) -> InterruptSourceType;
192
193    /// Get number of interrupt sources managed by the group.
194    fn len(&self) -> InterruptIndex;
195
196    /// Get base of the assigned Interrupt Source Identifiers.
197    fn base(&self) -> InterruptIndex;
198
199    /// Enable the interrupt sources in the group to generate interrupts.
200    fn enable(&self, configs: &[InterruptSourceConfig]) -> Result<()>;
201
202    /// Disable the interrupt sources in the group to generate interrupts.
203    fn disable(&self) -> Result<()>;
204
205    /// Update the interrupt source group configuration.
206    ///
207    /// # Arguments
208    /// * index: sub-index into the group.
209    /// * config: configuration data for the interrupt source.
210    fn update(&self, index: InterruptIndex, config: &InterruptSourceConfig) -> Result<()>;
211
212    /// Returns an interrupt notifier from this interrupt.
213    ///
214    /// An interrupt notifier allows for external components and processes to inject interrupts into
215    /// guest, by writing to the file returned by this method.
216    fn notifier(&self, _index: InterruptIndex) -> Option<&EventFd> {
217        None
218    }
219
220    /// Inject an interrupt from this interrupt source into the guest.
221    ///
222    /// If the interrupt has an associated `interrupt_status` register, all bits set in `flag` will
223    /// be atomically ORed into the `interrupt_status` register.
224    fn trigger(&self, index: InterruptIndex) -> Result<()>;
225
226    /// Mask an interrupt from this interrupt source.
227    fn mask(&self, _index: InterruptIndex) -> Result<()> {
228        // Not all interrupt sources can be disabled.
229        // To accommodate this, we can have a no-op here.
230        Ok(())
231    }
232
233    /// Unmask an interrupt from this interrupt source.
234    fn unmask(&self, _index: InterruptIndex) -> Result<()> {
235        // Not all interrupt sources can be disabled.
236        // To accommodate this, we can have a no-op here.
237        Ok(())
238    }
239
240    /// Check whether there's pending interrupt.
241    fn get_pending_state(&self, _index: InterruptIndex) -> bool {
242        false
243    }
244}