Skip to content

Commit c04210b

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 e5c35e0 commit c04210b

File tree

3 files changed

+252
-3
lines changed

3 files changed

+252
-3
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)]
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)]
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+
/// Supertrait 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: 181 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,52 @@
1-
// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
1+
// Copyright (C) 2019-2020 Alibaba Cloud, Red Hat, Inc and Amazon.com, Inc. or its affiliates.
2+
// All Rights Reserved.
3+
24
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
35

46
//! 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;
546

6-
use std::result::Result;
47+
use std::fmt::{self, Display};
48+
use std::io;
49+
use std::ops::Deref;
750

851
/// Abstraction for a simple, push-button like interrupt mechanism.
952
/// This helps in abstracting away how events/interrupts are generated when
@@ -18,5 +61,140 @@ pub trait Trigger {
1861
type E: std::fmt::Debug;
1962

2063
/// Trigger an event.
21-
fn trigger(&self) -> Result<(), Self::E>;
64+
fn trigger(&self) -> std::result::Result<(), Self::E>;
65+
}
66+
67+
/// Errors associated with handling interrupts
68+
#[derive(Debug)]
69+
pub enum Error {
70+
/// Operation not supported for this interrupt.
71+
OperationNotSupported,
72+
73+
/// The specified configuration is not valid.
74+
InvalidConfiguration,
75+
76+
/// The interrupt was not enabled.
77+
InterruptNotEnabled,
78+
79+
/// Generic IO error,
80+
IOError(io::Error),
81+
}
82+
83+
/// Reuse std::io::Result to simplify interoperability among crates.
84+
pub type Result<T> = std::result::Result<T, Error>;
85+
86+
impl Display for Error {
87+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
88+
write!(f, "Interrupt error: ")?;
89+
match self {
90+
Error::OperationNotSupported => write!(f, "operation not supported"),
91+
Error::InvalidConfiguration => write!(f, "invalid configuration"),
92+
Error::InterruptNotEnabled => write!(f, "the interrupt was not enabled"),
93+
Error::IOError(error) => write!(f, "{}", error),
94+
}
95+
}
96+
}
97+
98+
impl From<io::Error> for Error {
99+
fn from(e: io::Error) -> Error {
100+
Error::IOError(e)
101+
}
102+
}
103+
104+
/// Trait for interrupt producers.
105+
pub trait Interrupt {
106+
/// The type of Trigger object used for notifications by this interrupt.
107+
type TriggerType: Trigger;
108+
109+
/// Inject an interrupt from this interrupt source into the guest.
110+
fn trigger(&self) -> std::result::Result<(), <Self::TriggerType as Trigger>::E>;
111+
112+
/// Returns an interrupt notifier from this interrupt.
113+
///
114+
/// An interrupt notifier allows for external components and processes
115+
/// to inject interrupts into a guest, by writing to the file returned
116+
/// by this method.
117+
fn notifier(&self) -> Option<Self::TriggerType> {
118+
None
119+
}
120+
121+
/// Called back when the CPU acknowledges the interrupt.
122+
fn acknowledge(&self) {}
123+
124+
/// Returns an end-of-interrupt notifier from this interrupt.
125+
///
126+
/// An end-of-interrupt notifier allows for external components and processes
127+
/// to be notified when a guest acknowledges an interrupt. This can be used
128+
/// to resample and inject a level-triggered interrupt, or to mitigate the
129+
/// effect of lost timer interrupts.
130+
fn ack_notifier(&self) -> Option<Self::TriggerType> {
131+
None
132+
}
133+
134+
/// Enable generation of interrupts from this interrupt source.
135+
fn enable(&self) -> Result<()>;
136+
137+
/// Disable generation of interrupts from this interrupt source.
138+
fn disable(&self) -> Result<()> {
139+
Err(Error::OperationNotSupported)
140+
}
141+
}
142+
143+
/// Trait for interrupts that allow users to configure interrupt parameters.
144+
pub trait ConfigurableInterrupt: Interrupt {
145+
/// Type describing the configuration spec of the interrupt.
146+
type Cfg;
147+
148+
/// Update configuration of the interrupt.
149+
fn update(&self, config: &Self::Cfg) -> Result<()>;
150+
151+
/// Returns the current configuration of the interrupt.
152+
fn get_config(&self) -> Result<Self::Cfg>;
153+
}
154+
155+
/// Trait for interrupts that can be masked or unmasked.
156+
pub trait MaskableInterrupt: Interrupt {
157+
/// Mask the interrupt. Masked interrupts are remembered but
158+
/// not delivered.
159+
fn mask(&self) -> Result<()>;
160+
161+
/// Unmask the interrupt, delivering it if it was pending.
162+
fn unmask(&self) -> Result<()>;
163+
}
164+
165+
/// Trait to manage a group of interrupt sources for a device.
166+
///
167+
/// A device may use an InterruptSourceGroup to manage multiple interrupts of the same type.
168+
/// The group allows a device to request and release interrupts and perform actions on the
169+
/// whole collection of interrupts like enable and disable for cases where enabling or disabling
170+
/// a single interrupt in the group does not make sense. For example, PCI MSI interrupts must be
171+
/// enabled as a group.
172+
pub trait InterruptSourceGroup: Send {
173+
/// Type of the interrupts contained in this group.
174+
type InterruptType: Interrupt;
175+
176+
/// Interrupt Type returned by get
177+
type InterruptWrapper: Deref<Target = Self::InterruptType>;
178+
179+
/// Return whether the group manages no interrupts.
180+
fn is_empty(&self) -> bool;
181+
182+
/// Get number of interrupt sources managed by the group.
183+
fn len(&self) -> usize;
184+
185+
/// Enable the interrupt sources in the group to generate interrupts.
186+
fn enable(&self) -> Result<()>;
187+
188+
/// Disable the interrupt sources in the group to generate interrupts.
189+
fn disable(&self) -> Result<()>;
190+
191+
/// Return the index-th interrupt in the group, or `None` if the index is out
192+
/// of bounds.
193+
fn get(&self, index: usize) -> Option<Self::InterruptWrapper>;
194+
195+
/// Request new interrupts within this group.
196+
fn allocate_interrupts(&mut self, size: usize) -> Result<()>;
197+
198+
/// Release all interrupts within this group.
199+
fn free_interrupts(&mut self) -> Result<()>;
22200
}

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)]
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+
/// Supertrait 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 {}

0 commit comments

Comments
 (0)