• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(unix)] {
26         mod kvm;
27         pub use self::kvm::KvmKernelIrqChip;
28         #[cfg(any(target_arch = "x86", 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(any(target_arch = "x86", 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     }
61 }
62 
63 #[cfg(any(target_arch = "aarch64"))]
64 #[cfg(feature = "geniezone")]
65 mod geniezone;
66 #[cfg(any(target_arch = "aarch64"))]
67 #[cfg(feature = "geniezone")]
68 pub use self::geniezone::GeniezoneKernelIrqChip;
69 
70 pub type IrqEventIndex = usize;
71 
72 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
73 struct IrqEvent {
74     event: Event,
75     gsi: u32,
76     resample_event: Option<Event>,
77     source: IrqEventSource,
78 }
79 
80 #[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
81 pub enum DeviceId {
82     /// PCI Device, use its PciId directly.
83     PciDeviceId(PciId),
84     /// Platform device, use a unique Id.
85     PlatformDeviceId(CrosvmDeviceId),
86 }
87 
88 impl From<PciId> for DeviceId {
from(v: PciId) -> Self89     fn from(v: PciId) -> Self {
90         Self::PciDeviceId(v)
91     }
92 }
93 
94 impl From<CrosvmDeviceId> for DeviceId {
from(v: CrosvmDeviceId) -> Self95     fn from(v: CrosvmDeviceId) -> Self {
96         Self::PlatformDeviceId(v)
97     }
98 }
99 
100 impl TryFrom<u32> for DeviceId {
101     type Error = base::Error;
102 
try_from(value: u32) -> std::result::Result<Self, Self::Error>103     fn try_from(value: u32) -> std::result::Result<Self, Self::Error> {
104         let device_id = (value & 0xFFFF) as u16;
105         let vendor_id = ((value & 0xFFFF_0000) >> 16) as u16;
106         if vendor_id == 0xFFFF {
107             Ok(DeviceId::PlatformDeviceId(CrosvmDeviceId::try_from(
108                 device_id,
109             )?))
110         } else {
111             Ok(DeviceId::PciDeviceId(PciId::new(vendor_id, device_id)))
112         }
113     }
114 }
115 
116 impl From<DeviceId> for u32 {
from(id: DeviceId) -> Self117     fn from(id: DeviceId) -> Self {
118         match id {
119             DeviceId::PciDeviceId(pci_id) => pci_id.into(),
120             DeviceId::PlatformDeviceId(id) => 0xFFFF0000 | id as u32,
121         }
122     }
123 }
124 
125 /// Identification information about the source of an IrqEvent
126 #[derive(Clone, Serialize, Deserialize)]
127 pub struct IrqEventSource {
128     pub device_id: DeviceId,
129     pub queue_id: usize,
130     pub device_name: String,
131 }
132 
133 impl IrqEventSource {
from_device(device: &dyn BusDevice) -> Self134     pub fn from_device(device: &dyn BusDevice) -> Self {
135         Self {
136             device_id: device.device_id(),
137             queue_id: 0,
138             device_name: device.debug_label(),
139         }
140     }
141 }
142 
143 /// Trait that abstracts interactions with interrupt controllers.
144 ///
145 /// Each VM will have one IrqChip instance which is responsible for routing IRQ lines and
146 /// registering IRQ events. Depending on the implementation, the IrqChip may interact with an
147 /// underlying hypervisor API or emulate devices in userspace.
148 ///
149 /// This trait is generic over a Vcpu type because some IrqChip implementations can support
150 /// multiple hypervisors with a single implementation.
151 pub trait IrqChip: Send {
152     /// Add a vcpu to the irq chip.
add_vcpu(&mut self, vcpu_id: usize, vcpu: &dyn Vcpu) -> Result<()>153     fn add_vcpu(&mut self, vcpu_id: usize, vcpu: &dyn Vcpu) -> Result<()>;
154 
155     /// Register an event with edge-trigger semantic that can trigger an interrupt for a particular GSI.
register_edge_irq_event( &mut self, irq: u32, irq_event: &IrqEdgeEvent, source: IrqEventSource, ) -> Result<Option<IrqEventIndex>>156     fn register_edge_irq_event(
157         &mut self,
158         irq: u32,
159         irq_event: &IrqEdgeEvent,
160         source: IrqEventSource,
161     ) -> Result<Option<IrqEventIndex>>;
162 
163     /// Unregister an event with edge-trigger semantic for a particular GSI.
unregister_edge_irq_event(&mut self, irq: u32, irq_event: &IrqEdgeEvent) -> Result<()>164     fn unregister_edge_irq_event(&mut self, irq: u32, irq_event: &IrqEdgeEvent) -> Result<()>;
165 
166     /// Register an event with level-trigger semantic that can trigger an interrupt for a particular GSI.
register_level_irq_event( &mut self, irq: u32, irq_event: &IrqLevelEvent, source: IrqEventSource, ) -> Result<Option<IrqEventIndex>>167     fn register_level_irq_event(
168         &mut self,
169         irq: u32,
170         irq_event: &IrqLevelEvent,
171         source: IrqEventSource,
172     ) -> Result<Option<IrqEventIndex>>;
173 
174     /// Unregister an event with level-trigger semantic for a particular GSI.
unregister_level_irq_event(&mut self, irq: u32, irq_event: &IrqLevelEvent) -> Result<()>175     fn unregister_level_irq_event(&mut self, irq: u32, irq_event: &IrqLevelEvent) -> Result<()>;
176 
177     /// Route an IRQ line to an interrupt controller, or to a particular MSI vector.
route_irq(&mut self, route: IrqRoute) -> Result<()>178     fn route_irq(&mut self, route: IrqRoute) -> Result<()>;
179 
180     /// Replace all irq routes with the supplied routes
set_irq_routes(&mut self, routes: &[IrqRoute]) -> Result<()>181     fn set_irq_routes(&mut self, routes: &[IrqRoute]) -> Result<()>;
182 
183     /// Return a vector of all registered irq numbers and their associated events and event
184     /// sources. These should be used by the main thread to wait for irq events.
irq_event_tokens(&self) -> Result<Vec<(IrqEventIndex, IrqEventSource, Event)>>185     fn irq_event_tokens(&self) -> Result<Vec<(IrqEventIndex, IrqEventSource, Event)>>;
186 
187     /// Either assert or deassert an IRQ line.  Sends to either an interrupt controller, or does
188     /// a send_msi if the irq is associated with an MSI.
service_irq(&mut self, irq: u32, level: bool) -> Result<()>189     fn service_irq(&mut self, irq: u32, level: bool) -> Result<()>;
190 
191     /// Service an IRQ event by asserting then deasserting an IRQ line. The associated Event
192     /// that triggered the irq event will be read from. If the irq is associated with a resample
193     /// Event, then the deassert will only happen after an EOI is broadcast for a vector
194     /// associated with the irq line.
service_irq_event(&mut self, event_index: IrqEventIndex) -> Result<()>195     fn service_irq_event(&mut self, event_index: IrqEventIndex) -> Result<()>;
196 
197     /// Broadcast an end of interrupt.
broadcast_eoi(&self, vector: u8) -> Result<()>198     fn broadcast_eoi(&self, vector: u8) -> Result<()>;
199 
200     /// Injects any pending interrupts for `vcpu`.
inject_interrupts(&self, vcpu: &dyn Vcpu) -> Result<()>201     fn inject_interrupts(&self, vcpu: &dyn Vcpu) -> Result<()>;
202 
203     /// Notifies the irq chip that the specified VCPU has executed a halt instruction.
halted(&self, vcpu_id: usize)204     fn halted(&self, vcpu_id: usize);
205 
206     /// Blocks until `vcpu` is in a runnable state or until interrupted by
207     /// `IrqChip::kick_halted_vcpus`.  Returns `VcpuRunState::Runnable if vcpu is runnable, or
208     /// `VcpuRunState::Interrupted` if the wait was interrupted.
wait_until_runnable(&self, vcpu: &dyn Vcpu) -> Result<VcpuRunState>209     fn wait_until_runnable(&self, vcpu: &dyn Vcpu) -> Result<VcpuRunState>;
210 
211     /// Makes unrunnable VCPUs return immediately from `wait_until_runnable`.
212     /// For UserspaceIrqChip, every vcpu gets kicked so its current or next call to
213     /// `wait_until_runnable` will immediately return false.  After that one kick, subsequent
214     /// `wait_until_runnable` calls go back to waiting for runnability normally.
kick_halted_vcpus(&self)215     fn kick_halted_vcpus(&self);
216 
217     /// Get the current MP state of the specified VCPU.
get_mp_state(&self, vcpu_id: usize) -> Result<MPState>218     fn get_mp_state(&self, vcpu_id: usize) -> Result<MPState>;
219 
220     /// Set the current MP state of the specified VCPU.
set_mp_state(&mut self, vcpu_id: usize, state: &MPState) -> Result<()>221     fn set_mp_state(&mut self, vcpu_id: usize, state: &MPState) -> Result<()>;
222 
223     /// Attempt to create a shallow clone of this IrqChip instance.
try_clone(&self) -> Result<Self> where Self: Sized224     fn try_clone(&self) -> Result<Self>
225     where
226         Self: Sized;
227 
228     /// Finalize irqchip setup. Should be called once all devices have registered irq events and
229     /// been added to the io_bus and mmio_bus.
finalize_devices( &mut self, resources: &mut SystemAllocator, io_bus: &Bus, mmio_bus: &Bus, ) -> Result<()>230     fn finalize_devices(
231         &mut self,
232         resources: &mut SystemAllocator,
233         io_bus: &Bus,
234         mmio_bus: &Bus,
235     ) -> Result<()>;
236 
237     /// Process any irqs events that were delayed because of any locking issues.
process_delayed_irq_events(&mut self) -> Result<()>238     fn process_delayed_irq_events(&mut self) -> Result<()>;
239 
240     /// Return an event which is meant to trigger process of any irqs events that were delayed
241     /// by calling process_delayed_irq_events(). This should be used by the main thread to wait
242     /// for delayed irq event kick. It is process_delayed_irq_events() responsibility to read
243     /// the event as long as there is no more irqs to be serviced.
irq_delayed_event_token(&self) -> Result<Option<Event>>244     fn irq_delayed_event_token(&self) -> Result<Option<Event>>;
245 
246     /// Checks if a particular `IrqChipCap` is available.
check_capability(&self, c: IrqChipCap) -> bool247     fn check_capability(&self, c: IrqChipCap) -> bool;
248 }
249 
250 /// A capability the `IrqChip` can possibly expose.
251 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
252 pub enum IrqChipCap {
253     /// APIC TSC-deadline timer mode.
254     TscDeadlineTimer,
255     /// Extended xAPIC (x2APIC) standard.
256     X2Apic,
257 }
258 
259 /// A capability the `IrqChip` can possibly expose.
260 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
261 pub enum VcpuRunState {
262     Runnable,
263     Interrupted,
264 }
265