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}