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