Skip to content

Commit 3b39c21

Browse files
author
Alexandru-Cezar Sardan
committed
interrupt: Add traits for interrupt abstractions.
Add a series of Traits and structs for defining the interface for interrupts. The goal is to allow individual development of device crates that can work with different types of interrupt mechanisms. The interrupt mechanism can be implemented in the VMM based on the platform or the available hardware (e.g. CPU, Interrupt controller). These set of traits allow devices to use a simple interface through which they can request, release, configure interrupts and also signal events to the VMM. Signed-off-by: Alexandru-Cezar Sardan <alsardan@amazon.com>
1 parent a3b363d commit 3b39c21

File tree

4 files changed

+353
-0
lines changed

4 files changed

+353
-0
lines changed

src/interrupt/legacy.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright (C) 2021 Amazon.com, Inc. or its affiliates.
2+
// All Rights Reserved.
3+
4+
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
5+
6+
//! Traits and Structs to manage legacy interrupt sources for devices.
7+
//!
8+
//! Legacy interrupt sources typically include pin based interrupt lines.
9+
10+
use crate::interrupt::ConfigurableInterrupt;
11+
12+
/// Definition for PCI INTx pins.
13+
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd)]
14+
pub enum IntXPin {
15+
/// INTA
16+
IntA = 0x1,
17+
/// INTB
18+
IntB = 0x2,
19+
/// INTC
20+
IntC = 0x3,
21+
/// INTD
22+
IntD = 0x4,
23+
}
24+
25+
/// Standard configuration for Legacy interrupts.
26+
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
27+
pub struct LegacyIrqConfig {
28+
/// Input of the system interrupt controllers the device's interrupt pin is connected to.
29+
/// Implemented by any device that makes use of an interrupt pin.
30+
pub interrupt_line: Option<u32>,
31+
/// Specifies which interrupt pin the device uses.
32+
pub interrupt_pin: Option<IntXPin>,
33+
}
34+
35+
/// Trait for defining properties of Legacy interrupts.
36+
pub trait LegacyInterrupt: ConfigurableInterrupt<Cfg = LegacyIrqConfig> {}
37+
38+
/// Blanket implementation for Interrupts that use a LegacyIrqConfig.
39+
impl<T> LegacyInterrupt for T where T: ConfigurableInterrupt<Cfg = LegacyIrqConfig> {}

src/interrupt/mod.rs

Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
// Copyright (C) 2019-2020 Alibaba Cloud, Red Hat, Inc and Amazon.com, Inc. or its affiliates.
2+
// All Rights Reserved.
3+
4+
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
5+
6+
//! Traits and Structs to manage interrupt sources for devices.
7+
//!
8+
//! In system programming, an interrupt is a signal to the processor emitted by hardware or
9+
//! software indicating an event that needs immediate attention. An interrupt alerts the processor
10+
//! to a high-priority condition requiring the interruption of the current code the processor is
11+
//! executing. The processor responds by suspending its current activities, saving its state, and
12+
//! executing a function called an interrupt handler (or an interrupt service routine, ISR) to deal
13+
//! with the event. This interruption is temporary, and, after the interrupt handler finishes,
14+
//! unless handling the interrupt has emitted a fatal error, the processor resumes normal
15+
//! activities.
16+
//!
17+
//! Hardware interrupts are used by devices to communicate that they require attention from the
18+
//! operating system, or a bare-metal program running on the CPU if there are no OSes. The act of
19+
//! initiating a hardware interrupt is referred to as an interrupt request (IRQ). Different devices
20+
//! are usually associated with different interrupts using a unique value associated with each
21+
//! interrupt. This makes it possible to know which hardware device caused which interrupts.
22+
//! These interrupt values are often called IRQ lines, or just interrupt lines.
23+
//!
24+
//! Nowadays, IRQ lines is not the only mechanism to deliver device interrupts to processors.
25+
//! MSI [(Message Signaled Interrupt)](https://en.wikipedia.org/wiki/Message_Signaled_Interrupts)
26+
//! is another commonly used alternative in-band method of signaling an interrupt, using special
27+
//! in-band messages to replace traditional out-of-band assertion of dedicated interrupt lines.
28+
//! While more complex to implement in a device, message signaled interrupts have some significant
29+
//! advantages over pin-based out-of-band interrupt signaling. Message signaled interrupts are
30+
//! supported in PCI bus since its version 2.2, and in later available PCI Express bus. Some non-PCI
31+
//! architectures also use message signaled interrupts.
32+
//!
33+
//! While IRQ is a term commonly used by Operating Systems when dealing with hardware
34+
//! interrupts, the IRQ numbers managed by OSes are independent of the ones managed by VMM.
35+
//! For simplicity sake, the term `Interrupt Source` is used instead of IRQ to represent both pin-based
36+
//! interrupts and MSI interrupts.
37+
38+
pub mod legacy;
39+
pub mod msi;
40+
41+
use std::fmt::{self, Display};
42+
use std::ops::Deref;
43+
use std::sync::Arc;
44+
45+
/// Errors associated with handling interrupts
46+
#[derive(Debug)]
47+
pub enum Error {
48+
/// Operation not supported for this interrupt.
49+
OperationNotSupported,
50+
51+
/// The specified configuration is not valid.
52+
InvalidConfiguration,
53+
54+
/// The interrupt was not enabled.
55+
InterruptNotChanged,
56+
57+
/// The interrupt could not be triggered.
58+
InterruptNotTriggered,
59+
}
60+
61+
impl std::error::Error for Error {}
62+
63+
/// Reuse std::io::Result to simplify interoperability among crates.
64+
pub type Result<T> = std::result::Result<T, Error>;
65+
66+
impl Display for Error {
67+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
68+
write!(f, "Interrupt error: ")?;
69+
match self {
70+
Error::OperationNotSupported => write!(f, "operation not supported"),
71+
Error::InvalidConfiguration => write!(f, "invalid configuration"),
72+
Error::InterruptNotChanged => write!(f, "the interrupt state could not be changed"),
73+
Error::InterruptNotTriggered => write!(f, "the interrupt could not be triggered"),
74+
}
75+
}
76+
}
77+
78+
/// Trait used by interrupt producers to emulate an edge triggered interrupt.
79+
///
80+
/// This trait allows for a device to signal an interrupt event to the guest VM.
81+
/// These events are sampled by the interrupt controller synchronously when `trigger()` is called.
82+
///
83+
/// Edge triggered interrupts cannot be shared.
84+
pub trait EdgeInterrupt {
85+
/// Signal an interrupt to the guest VM.
86+
fn trigger(&self) -> Result<()>;
87+
}
88+
89+
/// Trait used by interrupt producers to emulate a level triggered interrupt.
90+
///
91+
/// This trait allows for a device to assert an interrupt line as long as it needs service
92+
/// from the CPU.
93+
/// A level triggered interrupt is held asserted until the device clears the interrupt signal.
94+
///
95+
/// Assertion of the interrupt signal starts when `assert()` is called by the device and ends when
96+
/// `clear()` is called by the device.
97+
/// The object implementing this trait must hold the assertion state internally and should return
98+
/// from `assert()` and `clear()` once the state is changed.
99+
pub trait LevelInterrupt {
100+
/// Assert the interrupt line to signal an interrupt to the guest VM.
101+
/// This method sets the interrupt in an asserted state.
102+
fn assert(&self) -> Result<()>;
103+
104+
/// Deassert the interrupt line to signal that the device no longer requires service.
105+
fn clear(&self) -> Result<()>;
106+
}
107+
108+
/// Trait that allows access to a device interrupt status.
109+
///
110+
/// A device will implement this trait if it wants to allow other components to check its
111+
/// interrupt status.
112+
/// This allows implementation of auto-triggered shared level interrupts that poll the interrupt
113+
/// state from the device in order to re-trigger the interrupt when resampled.
114+
pub trait InterruptStatusChecker {
115+
/// Check if the device requires service.
116+
/// Returns `true` if the device has not deasserted the interrupt line and still
117+
/// requires service.
118+
fn is_active(&self) -> bool;
119+
}
120+
121+
/// Trait used by interrupt controllers to configure interrupts.
122+
///
123+
/// An object having the `Interrupt` trait is shared between the VMM (which typically implements
124+
/// the interrupt mechanisms) and interrupt control components.
125+
/// It offers a control interface through the `enable()` and `disable()` methods that allow an
126+
/// interrupt to be registered with the interrupt controllers or mechanisms in the VMM.
127+
///
128+
/// Objects implementing this trait are required to have internal mutability.
129+
pub trait Interrupt {
130+
/// Enable generation of interrupts on this line.
131+
fn enable(&self) -> Result<()> {
132+
Err(Error::OperationNotSupported)
133+
}
134+
135+
/// Disable generation of interrupts on this line.
136+
fn disable(&self) -> Result<()> {
137+
Err(Error::OperationNotSupported)
138+
}
139+
}
140+
141+
/// Trait that allows interrupt controllers to configure interrupt parameters.
142+
///
143+
/// This enhances the control plane interface of the `Interrupt` by allowing a device to configure
144+
/// the behavior of the interrupt.
145+
///
146+
/// Objects implementing this trait are required to have internal mutability.
147+
pub trait ConfigurableInterrupt: Interrupt {
148+
/// Type describing the configuration spec of the interrupt.
149+
type Cfg;
150+
151+
/// Update configuration of the interrupt.
152+
fn update(&self, config: &Self::Cfg) -> Result<()>;
153+
154+
/// Returns the current configuration of the interrupt.
155+
fn get_config(&self) -> Result<Self::Cfg>;
156+
}
157+
158+
/// Trait for interrupts that can be masked or unmasked.
159+
///
160+
/// Objects implementing this trait are required to have internal mutability.
161+
pub trait MaskableInterrupt: Interrupt {
162+
/// Mask the interrupt. Masked interrupts are remembered but
163+
/// not delivered.
164+
fn mask(&self) -> Result<()>;
165+
166+
/// Unmask the interrupt, delivering it if it was pending.
167+
fn unmask(&self) -> Result<()>;
168+
}
169+
170+
/// Trait for interrupts that can be auto-retriggered when resampled.
171+
///
172+
/// In some implementations of shared level-triggered interrupts the interrupt can be resampled
173+
/// as a result of different events (e.g. an EOI) before a device explicitly deasserts the
174+
/// interrupt. If the device still requires service, the interrupt should be reasserted.
175+
///
176+
/// This trait allows implementation of the interrupt mechanism described above.
177+
/// It requires that the user of this trait configures an `InterruptStatusChecker`.
178+
/// When the interrupt is resampled, the state of the device will be checked by the implementation
179+
/// of this trait. If the device still requires service, the interrupt is reasserted.
180+
///
181+
/// An example of such mechanism is provided by KVM when using KVM_IRQFD with
182+
/// KVM_CAP_IRQFD_RESAMPLE.
183+
///
184+
/// Objects implementing this trait are required to have internal mutability.
185+
pub trait AutoRetriggerInterrupt: Interrupt {
186+
/// Set the `InterruptStatusChecker` object through which the interrupt can poll the device
187+
/// interrupt status.
188+
fn set_status_checker(&self, status_checker: Arc<dyn InterruptStatusChecker>) -> Result<()>;
189+
}
190+
191+
/// Trait that provides access to the underlying trigger notification object used by the hypervisor.
192+
///
193+
/// The type of the underlying notification mechanism used by the interrupt is defined by the
194+
/// `NotifierType` associated type.
195+
/// This enables some use cases where the device may want to bypass the VMM completely or when the
196+
/// device crate acts only as a control plane and the actual emulation is implemented in some other
197+
/// component that understands the underlying mechanism.
198+
///
199+
/// The usage of the resulted notifier object is speciffic to the hypervisor but the semantics of
200+
/// the object returned by the `trigger_notifier()` method should follow the semantics from
201+
/// `EdgeInterrupt::trigger()` or `LevelInterrupt::assert()` (e.g. when the user changes the state
202+
/// of the notifier object, an interrupt is queued for the guest).
203+
///
204+
/// A notable example is VFIO that allows a device to register the irqfd so that interrupts follow
205+
/// a fast path that doesn't require going through the VMM. Another example is XEN evtchn.
206+
///
207+
/// Implementations of this trait must provide the trigger notifier object.
208+
pub trait AsRefTriggerNotifier {
209+
/// The type of the underlying mechanism used for trigger notifications by this interrupt.
210+
type NotifierType;
211+
212+
/// Returns a reference to a trigger notifier from this interrupt.
213+
///
214+
/// An interrupt notifier allows for external components and processes to inject interrupts
215+
/// into a guest through a different interface other than `EdgeInterrupt::trigger()`.
216+
fn trigger_notifier(&self) -> &Self::NotifierType;
217+
}
218+
219+
/// Trait that provides access to the underlying resample notification object used by
220+
/// the hypervisor.
221+
///
222+
/// This enables use cases where the notification that the interrupt was resampled is
223+
/// handled by a component that understands the underlying hypervisor interrupt implementation
224+
/// and wants to bypass the VMM.
225+
///
226+
/// The semantics of the object returned by `resample_notifier()` are similar to those of
227+
/// `AutoRetriggerInterrupt` (when the state of the notifier object changes it means that
228+
/// the interrupt was resampled and the device should reassert the interrupt).
229+
///
230+
/// VFIO supports the registration of a `resamplefd` which would be returned by
231+
/// `resample_notifier`.
232+
///
233+
/// Implementations of this trait must provide the resample notifier object.
234+
pub trait AsRefResampleNotifier {
235+
/// The type of the underlying mechanism used for resample notifications by an interrupt.
236+
type NotifierType;
237+
238+
/// Returns a reference to a resample notifier from an interrupt.
239+
///
240+
/// An end-of-interrupt notifier allows for external components and processes to be notified
241+
/// when a guest acknowledges an interrupt. This can be used to resample and inject a
242+
/// level-triggered interrupt, or to mitigate the effect of lost timer interrupts.
243+
fn resample_notifier(&self) -> &Self::NotifierType;
244+
}
245+
246+
/// Trait to manage a group of interrupt sources for a device.
247+
///
248+
/// A device may use an InterruptSourceGroup to manage multiple interrupts of the same type.
249+
/// The group allows a device to request and release interrupts and perform actions on the
250+
/// whole collection of interrupts like enable and disable for cases where enabling or disabling
251+
/// a single interrupt in the group does not make sense. For example, PCI MSI interrupts must be
252+
/// enabled as a group.
253+
pub trait InterruptSourceGroup: Send {
254+
/// Type of the interrupts contained in this group.
255+
type InterruptType: Interrupt;
256+
257+
/// Interrupt Type returned by get
258+
type InterruptWrapper: Deref<Target = Self::InterruptType>;
259+
260+
/// Return whether the group manages no interrupts.
261+
fn is_empty(&self) -> bool;
262+
263+
/// Get number of interrupt sources managed by the group.
264+
fn len(&self) -> usize;
265+
266+
/// Enable the interrupt sources in the group to generate interrupts.
267+
fn enable(&self) -> Result<()>;
268+
269+
/// Disable the interrupt sources in the group to generate interrupts.
270+
fn disable(&self) -> Result<()>;
271+
272+
/// Return the index-th interrupt in the group, or `None` if the index is out
273+
/// of bounds.
274+
fn get(&self, index: usize) -> Option<Self::InterruptWrapper>;
275+
276+
/// Request new interrupts within this group.
277+
fn allocate_interrupts(&mut self, size: usize) -> Result<()>;
278+
279+
/// Release all interrupts within this group.
280+
fn free_interrupts(&mut self) -> Result<()>;
281+
}

src/interrupt/msi.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright (C) 2021 Amazon.com, Inc. or its affiliates.
2+
// All Rights Reserved.
3+
4+
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
5+
6+
//! Traits and Structs to manage MSI interrupt sources for devices.
7+
//!
8+
//! MSI interrupts are typically used by PCI devices.
9+
//! These structs and traits can be used to configure both MSI and MSIX interrupts.
10+
11+
use crate::interrupt::{ConfigurableInterrupt, MaskableInterrupt};
12+
13+
/// Configuration data for MSI/MSI-X interrupts.
14+
///
15+
/// On x86 platforms, these interrupts are vectors delivered directly to the LAPIC.
16+
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
17+
pub struct MsiIrqConfig {
18+
/// High address to delivery message signaled interrupt.
19+
pub high_addr: u32,
20+
/// Low address to delivery message signaled interrupt.
21+
pub low_addr: u32,
22+
/// Data to write to delivery message signaled interrupt.
23+
pub data: u32,
24+
/// Unique ID of the device to delivery message signaled interrupt.
25+
pub devid: u32,
26+
}
27+
28+
/// Trait for defining properties of MSI interrupts.
29+
pub trait MsiInterrupt: ConfigurableInterrupt<Cfg = MsiIrqConfig> + MaskableInterrupt {}
30+
31+
/// Blanket implementation for Interrupts that use a MsiIrqConfig.
32+
impl<T> MsiInterrupt for T where T: ConfigurableInterrupt<Cfg = MsiIrqConfig> + MaskableInterrupt {}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
5353
pub mod bus;
5454
pub mod device_manager;
55+
pub mod interrupt;
5556
pub mod resources;
5657

5758
use std::ops::Deref;

0 commit comments

Comments
 (0)