devices/irqchip/
mod.rs

1// Copyright 2020 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use std::marker::Send;
6use std::marker::Sized;
7
8use base::Event;
9use base::Result;
10use hypervisor::IrqRoute;
11use hypervisor::MPState;
12use hypervisor::Vcpu;
13use resources::SystemAllocator;
14use serde::Deserialize;
15use serde::Serialize;
16
17use crate::pci::CrosvmDeviceId;
18use crate::pci::PciId;
19use crate::Bus;
20use crate::BusDevice;
21use crate::IrqEdgeEvent;
22use crate::IrqLevelEvent;
23
24cfg_if::cfg_if! {
25    if #[cfg(any(target_os = "android", target_os = "linux"))] {
26        mod kvm;
27        pub use self::kvm::KvmKernelIrqChip;
28        #[cfg(target_arch = "x86_64")]
29        pub use self::kvm::KvmSplitIrqChip;
30        #[cfg(target_arch = "aarch64")]
31        pub use self::kvm::{AARCH64_GIC_NR_IRQS, AARCH64_GIC_NR_SPIS};
32
33        #[cfg(all(target_arch = "aarch64", feature = "gunyah"))]
34        mod gunyah;
35        #[cfg(all(target_arch = "aarch64", feature = "gunyah"))]
36        pub use self::gunyah::GunyahIrqChip;
37
38        #[cfg(all(target_arch = "aarch64", feature = "geniezone"))]
39        mod geniezone;
40        #[cfg(all(target_arch = "aarch64", feature = "geniezone"))]
41        pub use self::geniezone::GeniezoneKernelIrqChip;
42    } else if #[cfg(all(windows, feature = "whpx"))] {
43        mod whpx;
44        pub use self::whpx::WhpxSplitIrqChip;
45    }
46}
47
48cfg_if::cfg_if! {
49    if #[cfg(target_arch = "x86_64")] {
50        mod x86_64;
51        pub use x86_64::*;
52        mod pic;
53        pub use pic::*;
54        mod ioapic;
55        pub use ioapic::*;
56        mod apic;
57        pub use apic::*;
58        mod userspace;
59        pub use userspace::*;
60    } else if #[cfg(target_arch = "aarch64")] {
61        mod aarch64;
62        pub use aarch64::*;
63    } else if #[cfg(target_arch = "riscv64")] {
64        mod riscv64;
65        pub use riscv64::*;
66        pub use self::kvm::aia_addr_imsic;
67        pub use self::kvm::aia_aplic_addr;
68        pub use self::kvm::aia_imsic_addr;
69        pub use self::kvm::aia_imsic_size;
70        pub use self::kvm::AIA_APLIC_SIZE;
71        pub use self::kvm::AIA_IMSIC_BASE;
72        pub use self::kvm::IMSIC_MAX_INT_IDS;
73    }
74
75}
76
77#[cfg(all(target_arch = "aarch64", feature = "halla"))]
78mod halla;
79#[cfg(all(target_arch = "aarch64", feature = "halla"))]
80pub use self::halla::HallaKernelIrqChip;
81
82pub type IrqEventIndex = usize;
83
84#[cfg(target_arch = "x86_64")]
85struct IrqEvent {
86    event: Event,
87    gsi: u32,
88    resample_event: Option<Event>,
89    source: IrqEventSource,
90}
91
92#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
93pub enum DeviceId {
94    /// PCI Device, use its PciId directly.
95    PciDeviceId(PciId),
96    /// Platform device, use a unique Id.
97    PlatformDeviceId(CrosvmDeviceId),
98}
99
100impl From<PciId> for DeviceId {
101    fn from(v: PciId) -> Self {
102        Self::PciDeviceId(v)
103    }
104}
105
106impl From<CrosvmDeviceId> for DeviceId {
107    fn from(v: CrosvmDeviceId) -> Self {
108        Self::PlatformDeviceId(v)
109    }
110}
111
112impl TryFrom<u32> for DeviceId {
113    type Error = base::Error;
114
115    fn try_from(value: u32) -> std::result::Result<Self, Self::Error> {
116        let device_id = (value & 0xFFFF) as u16;
117        let vendor_id = ((value & 0xFFFF_0000) >> 16) as u16;
118        if vendor_id == 0xFFFF {
119            Ok(DeviceId::PlatformDeviceId(CrosvmDeviceId::try_from(
120                device_id,
121            )?))
122        } else {
123            Ok(DeviceId::PciDeviceId(PciId::new(vendor_id, device_id)))
124        }
125    }
126}
127
128impl From<DeviceId> for u32 {
129    fn from(id: DeviceId) -> Self {
130        match id {
131            DeviceId::PciDeviceId(pci_id) => pci_id.into(),
132            DeviceId::PlatformDeviceId(id) => 0xFFFF0000 | id as u32,
133        }
134    }
135}
136
137/// Identification information about the source of an IrqEvent
138#[derive(Clone, Serialize, Deserialize)]
139pub struct IrqEventSource {
140    pub device_id: DeviceId,
141    pub queue_id: usize,
142    pub device_name: String,
143}
144
145impl IrqEventSource {
146    pub fn from_device(device: &dyn BusDevice) -> Self {
147        Self {
148            device_id: device.device_id(),
149            queue_id: 0,
150            device_name: device.debug_label(),
151        }
152    }
153}
154
155/// Trait that abstracts interactions with interrupt controllers.
156///
157/// Each VM will have one IrqChip instance which is responsible for routing IRQ lines and
158/// registering IRQ events. Depending on the implementation, the IrqChip may interact with an
159/// underlying hypervisor API or emulate devices in userspace.
160///
161/// This trait is generic over a Vcpu type because some IrqChip implementations can support
162/// multiple hypervisors with a single implementation.
163pub trait IrqChip: Send {
164    /// Add a vcpu to the irq chip.
165    fn add_vcpu(&mut self, vcpu_id: usize, vcpu: &dyn Vcpu) -> Result<()>;
166
167    /// Register an event with edge-trigger semantic that can trigger an interrupt for a particular
168    /// GSI.
169    fn register_edge_irq_event(
170        &mut self,
171        irq: u32,
172        irq_event: &IrqEdgeEvent,
173        source: IrqEventSource,
174    ) -> Result<Option<IrqEventIndex>>;
175
176    /// Unregister an event with edge-trigger semantic for a particular GSI.
177    fn unregister_edge_irq_event(&mut self, irq: u32, irq_event: &IrqEdgeEvent) -> Result<()>;
178
179    /// Register an event with level-trigger semantic that can trigger an interrupt for a particular
180    /// GSI.
181    fn register_level_irq_event(
182        &mut self,
183        irq: u32,
184        irq_event: &IrqLevelEvent,
185        source: IrqEventSource,
186    ) -> Result<Option<IrqEventIndex>>;
187
188    /// Unregister an event with level-trigger semantic for a particular GSI.
189    fn unregister_level_irq_event(&mut self, irq: u32, irq_event: &IrqLevelEvent) -> Result<()>;
190
191    /// Route an IRQ line to an interrupt controller, or to a particular MSI vector.
192    fn route_irq(&mut self, route: IrqRoute) -> Result<()>;
193
194    /// Replace all irq routes with the supplied routes
195    fn set_irq_routes(&mut self, routes: &[IrqRoute]) -> Result<()>;
196
197    /// Return a vector of all registered irq numbers and their associated events and event
198    /// sources. These should be used by the main thread to wait for irq events.
199    fn irq_event_tokens(&self) -> Result<Vec<(IrqEventIndex, IrqEventSource, Event)>>;
200
201    /// Either assert or deassert an IRQ line.  Sends to either an interrupt controller, or does
202    /// a send_msi if the irq is associated with an MSI.
203    fn service_irq(&mut self, irq: u32, level: bool) -> Result<()>;
204
205    /// Service an IRQ event by asserting then deasserting an IRQ line. The associated Event
206    /// that triggered the irq event will be read from. If the irq is associated with a resample
207    /// Event, then the deassert will only happen after an EOI is broadcast for a vector
208    /// associated with the irq line.
209    fn service_irq_event(&mut self, event_index: IrqEventIndex) -> Result<()>;
210
211    /// Broadcast an end of interrupt.
212    fn broadcast_eoi(&self, vector: u8) -> Result<()>;
213
214    /// Injects any pending interrupts for `vcpu`.
215    fn inject_interrupts(&self, vcpu: &dyn Vcpu) -> Result<()>;
216
217    /// Notifies the irq chip that the specified VCPU has executed a halt instruction.
218    fn halted(&self, vcpu_id: usize);
219
220    /// Blocks until `vcpu` is in a runnable state or until interrupted by
221    /// `IrqChip::kick_halted_vcpus`.  Returns `VcpuRunState::Runnable if vcpu is runnable, or
222    /// `VcpuRunState::Interrupted` if the wait was interrupted.
223    fn wait_until_runnable(&self, vcpu: &dyn Vcpu) -> Result<VcpuRunState>;
224
225    /// Makes unrunnable VCPUs return immediately from `wait_until_runnable`.
226    /// For UserspaceIrqChip, every vcpu gets kicked so its current or next call to
227    /// `wait_until_runnable` will immediately return false.  After that one kick, subsequent
228    /// `wait_until_runnable` calls go back to waiting for runnability normally.
229    fn kick_halted_vcpus(&self);
230
231    /// Get the current MP state of the specified VCPU.
232    fn get_mp_state(&self, vcpu_id: usize) -> Result<MPState>;
233
234    /// Set the current MP state of the specified VCPU.
235    fn set_mp_state(&mut self, vcpu_id: usize, state: &MPState) -> Result<()>;
236
237    /// Attempt to create a shallow clone of this IrqChip instance.
238    fn try_clone(&self) -> Result<Self>
239    where
240        Self: Sized;
241
242    /// Finalize irqchip setup. Should be called once all devices have registered irq events and
243    /// been added to the io_bus and mmio_bus.
244    fn finalize_devices(
245        &mut self,
246        resources: &mut SystemAllocator,
247        io_bus: &Bus,
248        mmio_bus: &Bus,
249    ) -> Result<()>;
250
251    /// Process any irqs events that were delayed because of any locking issues.
252    fn process_delayed_irq_events(&mut self) -> Result<()>;
253
254    /// Return an event which is meant to trigger process of any irqs events that were delayed
255    /// by calling process_delayed_irq_events(). This should be used by the main thread to wait
256    /// for delayed irq event kick. It is process_delayed_irq_events() responsibility to read
257    /// the event as long as there is no more irqs to be serviced.
258    fn irq_delayed_event_token(&self) -> Result<Option<Event>>;
259
260    /// Checks if a particular `IrqChipCap` is available.
261    fn check_capability(&self, c: IrqChipCap) -> bool;
262}
263
264/// A capability the `IrqChip` can possibly expose.
265#[derive(Clone, Copy, Debug, PartialEq, Eq)]
266pub enum IrqChipCap {
267    /// APIC TSC-deadline timer mode.
268    #[cfg(target_arch = "x86_64")]
269    TscDeadlineTimer,
270    /// Extended xAPIC (x2APIC) standard.
271    #[cfg(target_arch = "x86_64")]
272    X2Apic,
273    /// Irqchip exposes mp_state_get/set methods. Calling these methods on chips
274    /// without this capability will result in undefined behavior.
275    MpStateGetSet,
276}
277
278/// A capability the `IrqChip` can possibly expose.
279#[derive(Clone, Copy, Debug, PartialEq, Eq)]
280pub enum VcpuRunState {
281    Runnable,
282    Interrupted,
283}