Skip to content

Commit 754479f

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 754479f

File tree

4 files changed

+290
-0
lines changed

4 files changed

+290
-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: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
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+
//! A device may support multiple types of interrupts, and each type of interrupt may support one
39+
//! or multiple interrupt sources. For example, a PCI device may support:
40+
//! * Legacy Irq: exactly one interrupt source.
41+
//! * PCI MSI Irq: 1,2,4,8,16,32 interrupt sources.
42+
//! * PCI MSIx Irq: 2^n(n=0-11) interrupt sources.
43+
44+
pub mod legacy;
45+
pub mod msi;
46+
47+
use std::fmt::{self, Display};
48+
use std::ops::Deref;
49+
50+
/// Errors associated with handling interrupts
51+
#[derive(Debug)]
52+
pub enum Error {
53+
/// Operation not supported for this interrupt.
54+
OperationNotSupported,
55+
56+
/// The specified configuration is not valid.
57+
InvalidConfiguration,
58+
59+
/// The interrupt was not enabled.
60+
InterruptNotChanged,
61+
62+
/// The interrupt could not be triggered.
63+
InterruptNotTriggered,
64+
}
65+
66+
impl std::error::Error for Error {}
67+
68+
/// Reuse std::io::Result to simplify interoperability among crates.
69+
pub type Result<T> = std::result::Result<T, Error>;
70+
71+
impl Display for Error {
72+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
73+
write!(f, "Interrupt error: ")?;
74+
match self {
75+
Error::OperationNotSupported => write!(f, "operation not supported"),
76+
Error::InvalidConfiguration => write!(f, "invalid configuration"),
77+
Error::InterruptNotChanged => write!(f, "the interrupt state could not be changed"),
78+
Error::InterruptNotTriggered => write!(f, "the interrupt could not be triggered"),
79+
}
80+
}
81+
}
82+
83+
/// Trait used by interrupt producers to signal interrupts.
84+
///
85+
/// An object having the Interrupt Trait is shared between the VMM and the device
86+
/// that is using the interrupt.
87+
/// The `Interrupt` performs two main goals:
88+
/// * offers a control interface for the interrupt through the `enable()`
89+
/// and `disable()` methods;
90+
/// * offers a channel through which notifications can be passed between
91+
/// the VMM and the device;
92+
///
93+
/// When an `Interrupt` is triggered, a notification mechanism that is known by the
94+
/// Hypervisor will be used to signal the guest.
95+
///
96+
/// Objects implementing this trait are required to have internal mutability.
97+
pub trait Interrupt {
98+
/// Inject an interrupt from this interrupt source into the guest.
99+
fn trigger(&self) -> Result<()>;
100+
101+
/// EOI callback that will be called when the guest acknowledges the interrupt.
102+
/// This can be used to resample and re-inject a level-triggered interrupt if the
103+
/// device still requires service.
104+
fn acknowledge(&self) -> Result<()> {
105+
Err(Error::OperationNotSupported)
106+
}
107+
108+
/// Enable generation of interrupts from this interrupt source.
109+
fn enable(&self) -> Result<()> {
110+
Err(Error::OperationNotSupported)
111+
}
112+
113+
/// Disable generation of interrupts from this interrupt source.
114+
fn disable(&self) -> Result<()> {
115+
Err(Error::OperationNotSupported)
116+
}
117+
}
118+
119+
/// Trait that provides access to the underlying notification objects used by the
120+
/// hypervisor.
121+
///
122+
/// The type of the underlying notification mechanism used by the `Interrupt` is
123+
/// defined by the `NotifierType` associated type.
124+
/// `InterruptWithNotifiers` allows access to the undelying mechanism used by the
125+
/// Hypervisor through the `trigger_notifier()` and `ack_notifier()` methods.
126+
/// This enables some use cases where the device may want to bypass the VMM completely
127+
/// or when the device crate acts only as a control plane and the actual emulation is
128+
/// implemented in some other component that understands the underlying mechanism.
129+
///
130+
/// A notable example is VFIO that allows a device to register the irqfd so that
131+
/// interrupts follow a fast path that doesn't require going through the VMM.
132+
/// VFIO supports the registration of a `resamplefd` which would be returned by
133+
/// `ack_notifier`.
134+
///
135+
/// Implementations of this trait must provide the trigger notifier object.
136+
pub trait InterruptWithNotifiers: Interrupt {
137+
/// The type of the underlying mechanism used for notifications by this interrupt.
138+
type NotifierType;
139+
140+
/// Returns an interrupt notifier from this interrupt.
141+
///
142+
/// An interrupt notifier allows for external components and processes
143+
/// to inject interrupts into a guest through a different interface other
144+
/// than `trigger()`.
145+
fn trigger_notifier(&self) -> &Self::NotifierType;
146+
147+
/// Returns an end-of-interrupt notifier from this interrupt.
148+
///
149+
/// An end-of-interrupt notifier allows for external components and processes
150+
/// to be notified when a guest acknowledges an interrupt. This can be used
151+
/// to resample and inject a level-triggered interrupt, or to mitigate the
152+
/// effect of lost timer interrupts.
153+
fn ack_notifier(&self) -> Option<&Self::NotifierType> {
154+
None
155+
}
156+
}
157+
158+
/// Trait for interrupts that allow users to configure interrupt parameters.
159+
///
160+
/// This enhances the control plane interface of the `Interrupt` by allowing
161+
/// a device to configure the behavior of the interrupt.
162+
pub trait ConfigurableInterrupt: Interrupt {
163+
/// Type describing the configuration spec of the interrupt.
164+
type Cfg;
165+
166+
/// Update configuration of the interrupt.
167+
fn update(&self, config: &Self::Cfg) -> Result<()>;
168+
169+
/// Returns the current configuration of the interrupt.
170+
fn get_config(&self) -> Result<Self::Cfg>;
171+
}
172+
173+
/// Trait for interrupts that can be masked or unmasked.
174+
pub trait MaskableInterrupt: Interrupt {
175+
/// Mask the interrupt. Masked interrupts are remembered but
176+
/// not delivered.
177+
fn mask(&self) -> Result<()>;
178+
179+
/// Unmask the interrupt, delivering it if it was pending.
180+
fn unmask(&self) -> Result<()>;
181+
}
182+
183+
/// Trait to manage a group of interrupt sources for a device.
184+
///
185+
/// A device may use an InterruptSourceGroup to manage multiple interrupts of the same type.
186+
/// The group allows a device to request and release interrupts and perform actions on the
187+
/// whole collection of interrupts like enable and disable for cases where enabling or disabling
188+
/// a single interrupt in the group does not make sense. For example, PCI MSI interrupts must be
189+
/// enabled as a group.
190+
pub trait InterruptSourceGroup: Send {
191+
/// Type of the interrupts contained in this group.
192+
type InterruptType: Interrupt;
193+
194+
/// Interrupt Type returned by get
195+
type InterruptWrapper: Deref<Target = Self::InterruptType>;
196+
197+
/// Return whether the group manages no interrupts.
198+
fn is_empty(&self) -> bool;
199+
200+
/// Get number of interrupt sources managed by the group.
201+
fn len(&self) -> usize;
202+
203+
/// Enable the interrupt sources in the group to generate interrupts.
204+
fn enable(&self) -> Result<()>;
205+
206+
/// Disable the interrupt sources in the group to generate interrupts.
207+
fn disable(&self) -> Result<()>;
208+
209+
/// Return the index-th interrupt in the group, or `None` if the index is out
210+
/// of bounds.
211+
fn get(&self, index: usize) -> Option<Self::InterruptWrapper>;
212+
213+
/// Request new interrupts within this group.
214+
fn allocate_interrupts(&mut self, size: usize) -> Result<()>;
215+
216+
/// Release all interrupts within this group.
217+
fn free_interrupts(&mut self) -> Result<()>;
218+
}

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)