Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/irqchip/x86_64.rs
5394 views
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::ops::Index;
6
use std::vec::Vec;
7
8
use anyhow::anyhow;
9
use anyhow::Context;
10
use base::Error;
11
use base::Event;
12
use base::Result;
13
use hypervisor::IoapicState;
14
use hypervisor::IrqRoute;
15
use hypervisor::IrqSource;
16
use hypervisor::IrqSourceChip;
17
use hypervisor::LapicState;
18
use hypervisor::MPState;
19
use hypervisor::PicSelect;
20
use hypervisor::PicState;
21
use hypervisor::PitState;
22
use serde::Deserialize;
23
use serde::Serialize;
24
use snapshot::AnySnapshot;
25
26
use crate::IrqChip;
27
use crate::IrqChipCap;
28
29
pub trait IrqChipX86_64: IrqChip {
30
// Clones this trait as a `Box` version of itself.
31
fn try_box_clone(&self) -> Result<Box<dyn IrqChipX86_64>>;
32
33
// Get this as the super-trait IrqChip.
34
fn as_irq_chip(&self) -> &dyn IrqChip;
35
36
// Get this as the mutable super-trait IrqChip.
37
fn as_irq_chip_mut(&mut self) -> &mut dyn IrqChip;
38
39
/// Get the current state of the PIC
40
fn get_pic_state(&self, select: PicSelect) -> Result<PicState>;
41
42
/// Set the current state of the PIC
43
fn set_pic_state(&mut self, select: PicSelect, state: &PicState) -> Result<()>;
44
45
/// Get the current state of the IOAPIC
46
fn get_ioapic_state(&self) -> Result<IoapicState>;
47
48
/// Set the current state of the IOAPIC
49
fn set_ioapic_state(&mut self, state: &IoapicState) -> Result<()>;
50
51
/// Get the current state of the specified VCPU's local APIC
52
fn get_lapic_state(&self, vcpu_id: usize) -> Result<LapicState>;
53
54
/// Set the current state of the specified VCPU's local APIC
55
fn set_lapic_state(&mut self, vcpu_id: usize, state: &LapicState) -> Result<()>;
56
57
/// Get the lapic frequency in Hz
58
fn lapic_frequency(&self) -> u32;
59
60
/// Retrieves the state of the PIT.
61
fn get_pit(&self) -> Result<PitState>;
62
63
/// Sets the state of the PIT.
64
fn set_pit(&mut self, state: &PitState) -> Result<()>;
65
66
/// Returns true if the PIT uses port 0x61 for the PC speaker, false if 0x61 is unused.
67
fn pit_uses_speaker_port(&self) -> bool;
68
69
/// Snapshot state specific to different IrqChips.
70
fn snapshot_chip_specific(&self) -> anyhow::Result<AnySnapshot>;
71
72
/// Restore state specific to different IrqChips.
73
fn restore_chip_specific(&mut self, data: AnySnapshot) -> anyhow::Result<()>;
74
75
/// Snapshot state common to IrqChips.
76
fn snapshot(&self, cpus_num: usize) -> anyhow::Result<AnySnapshot> {
77
let mut lapics: Vec<LapicState> = Vec::new();
78
let mut mp_states: Vec<MPState> = Vec::new();
79
let has_mp_states = self.check_capability(IrqChipCap::MpStateGetSet);
80
for i in 0..cpus_num {
81
lapics.push(self.get_lapic_state(i)?);
82
if has_mp_states {
83
mp_states.push(self.get_mp_state(i)?);
84
}
85
}
86
AnySnapshot::to_any(IrqChipSnapshot {
87
ioapic_state: self.get_ioapic_state()?,
88
lapic_state: lapics,
89
pic_state_1: self.get_pic_state(PicSelect::Primary)?,
90
pic_state_2: self.get_pic_state(PicSelect::Secondary)?,
91
pit_state: self.get_pit()?,
92
chip_specific_state: self.snapshot_chip_specific()?,
93
mp_state: mp_states,
94
})
95
.context("failed to serialize KvmKernelIrqChip")
96
}
97
98
/// Restore state common to IrqChips.
99
fn restore(&mut self, data: AnySnapshot, vcpus_num: usize) -> anyhow::Result<()> {
100
let deser: IrqChipSnapshot =
101
AnySnapshot::from_any(data).context("failed to deserialize data")?;
102
103
if deser.lapic_state.len() != vcpus_num {
104
return Err(anyhow!(
105
"IrqChip has the wrong number of LAPIC state snapshots: got {}, expected {}",
106
deser.lapic_state.len(),
107
vcpus_num
108
));
109
}
110
let supports_mp_states = self.check_capability(IrqChipCap::MpStateGetSet);
111
112
if supports_mp_states {
113
if deser.mp_state.len() != vcpus_num {
114
return Err(anyhow!(
115
"IrqChip has the wrong number of mp state snapshots: got {}, expected {}",
116
deser.mp_state.len(),
117
vcpus_num
118
));
119
}
120
} else if !deser.mp_state.is_empty() {
121
return Err(anyhow!(
122
"IrqChip does not support mp state, but mp state was in the snapshot"
123
));
124
}
125
126
self.set_pit(&deser.pit_state)?;
127
self.set_pic_state(PicSelect::Primary, &deser.pic_state_1)
128
.context("failed to set primary PIC")?;
129
self.set_pic_state(PicSelect::Secondary, &deser.pic_state_2)
130
.context("failed to set secondary PIC")?;
131
self.set_ioapic_state(&deser.ioapic_state)
132
.context("failed to set IOAPIC state")?;
133
self.restore_chip_specific(deser.chip_specific_state)
134
.context("failed to set chip specific data")?;
135
for (i, lapic) in deser.lapic_state.iter().enumerate() {
136
self.set_lapic_state(i, lapic)
137
.context("failed to set LAPIC state")?;
138
}
139
140
if supports_mp_states {
141
for (i, mp_state) in deser.mp_state.iter().enumerate() {
142
self.set_mp_state(i, mp_state)
143
.context("failed to set mp state")?;
144
}
145
}
146
Ok(())
147
}
148
}
149
150
#[derive(Serialize, Deserialize)]
151
struct IrqChipSnapshot {
152
ioapic_state: IoapicState,
153
lapic_state: Vec<LapicState>,
154
pic_state_1: PicState,
155
pic_state_2: PicState,
156
pit_state: PitState,
157
chip_specific_state: AnySnapshot,
158
mp_state: Vec<MPState>,
159
}
160
161
/// A container for x86 IrqRoutes, grouped by GSI.
162
pub struct Routes {
163
/// A list of routes, indexed by GSI. Each GSI can map to zero or more routes, so this is a
164
/// Vec of Vecs. Specifically, a GSI can map to:
165
/// * no routes; or
166
/// * one IrqSource::Msi route; or
167
/// * one or more IrqSource::Irqchip routes (PicPrimary, PicSecondary, or Ioapic)
168
routes: Vec<Vec<IrqSource>>,
169
}
170
171
impl Routes {
172
/// Constructs a new `Routes` with an empty routing table.
173
pub fn new() -> Self {
174
Routes { routes: vec![] }
175
}
176
177
/// Inserts a route, replacing any existing route that conflicts. Two routes conflict if they
178
/// have the same GSI, and they're both `IrqSource::Irqchip` routes with the same chip or
179
/// they're both `IrqSource::Msi`. Returns Err if an `IrqSource::Irqchip` and `IrqSource::Msi`
180
/// route have the same GSI.
181
pub fn add(&mut self, route: IrqRoute) -> Result<()> {
182
let routes = self.get_mut(route.gsi as usize);
183
if routes.iter().any(|r| !Self::same_source(&route.source, r)) {
184
// We keep an invariant that legacy and MSI routes can't be mixed on the same GSI.
185
// Irqchip routes are only on GSIs [0..24) and Msi routes are only on GSIs >= 24. This
186
// guarantees that in UserspaceIrqChip, the ioapic's incoming Irqchip routes and
187
// outgoing Msi routes can't trigger each other in a cycle.
188
return Err(Error::new(libc::EINVAL));
189
}
190
routes.retain(|r| !Self::conflict(&route.source, r));
191
routes.push(route.source);
192
Ok(())
193
}
194
195
/// Deletes all existing routes and replaces them with `routes`. If two routes in `routes`
196
/// conflict with each other, the one earlier in the slice is dropped.
197
pub fn replace_all(&mut self, routes: &[IrqRoute]) -> Result<()> {
198
self.routes.clear();
199
for r in routes {
200
self.add(*r)?;
201
}
202
Ok(())
203
}
204
205
/// Default x86 routing table. Pins 0-7 go to primary pic and ioapic, pins 8-15 go to secondary
206
/// pic and ioapic, and pins 16-23 go only to the ioapic.
207
pub fn default_pic_ioapic_routes(ioapic_pins: usize) -> Vec<IrqRoute> {
208
let mut routes: Vec<IrqRoute> = Vec::new();
209
210
for i in 0..8 {
211
routes.push(IrqRoute::pic_irq_route(IrqSourceChip::PicPrimary, i));
212
routes.push(IrqRoute::ioapic_irq_route(i));
213
}
214
for i in 8..16 {
215
routes.push(IrqRoute::pic_irq_route(IrqSourceChip::PicSecondary, i));
216
routes.push(IrqRoute::ioapic_irq_route(i));
217
}
218
for i in 16..ioapic_pins as u32 {
219
routes.push(IrqRoute::ioapic_irq_route(i));
220
}
221
222
routes
223
}
224
225
/// Gets the routes as a flat Vec of `IrqRoute`s.
226
pub fn get_routes(&self) -> Vec<IrqRoute> {
227
let mut routes = Vec::with_capacity(self.routes.len());
228
for (gsi, sources) in self.routes.iter().enumerate() {
229
for source in sources.iter() {
230
routes.push(IrqRoute {
231
gsi: gsi.try_into().expect("GSIs must be < u32::MAX"),
232
source: *source,
233
});
234
}
235
}
236
routes
237
}
238
239
/// Determines whether or not two irq routes on the same GSI conflict.
240
/// Returns true if they conflict.
241
fn conflict(source: &IrqSource, other: &IrqSource) -> bool {
242
use IrqSource::*;
243
244
// If they're both MSI then they conflict.
245
if let (Msi { .. }, Msi { .. }) = (source, other) {
246
return true;
247
}
248
249
// If the route chips match then they conflict.
250
if let (
251
Irqchip { chip, .. },
252
Irqchip {
253
chip: other_chip, ..
254
},
255
) = (source, other)
256
{
257
return chip == other_chip;
258
}
259
260
// Otherwise they do not conflict.
261
false
262
}
263
264
/// Determines whether two routes have the same IrqSource variant (IrqSource::Irqchip or
265
/// IrqSource::Msi).
266
fn same_source(source: &IrqSource, other: &IrqSource) -> bool {
267
use IrqSource::*;
268
matches!(
269
(source, other),
270
(Irqchip { .. }, Irqchip { .. }) | (Msi { .. }, Msi { .. })
271
)
272
}
273
274
/// Returns the routes vec for `irq`. If `irq` is past the end of self.routes, then self.routes
275
/// is first resized with empty vecs.
276
fn get_mut(&mut self, irq: usize) -> &mut Vec<IrqSource> {
277
if irq >= self.routes.len() {
278
self.routes.resize_with(irq + 1, Vec::new);
279
}
280
self.routes.get_mut(irq).unwrap()
281
}
282
}
283
284
impl Default for Routes {
285
fn default() -> Self {
286
Self::new()
287
}
288
}
289
290
const EMPTY_ROUTE: [IrqSource; 0] = [];
291
292
impl Index<usize> for Routes {
293
type Output = [IrqSource];
294
295
/// Returns all routes for `irq`, or an empty slice if no routes registered for `irq`.
296
fn index(&self, irq: usize) -> &Self::Output {
297
if irq < self.routes.len() {
298
self.routes[irq].as_slice()
299
} else {
300
&EMPTY_ROUTE
301
}
302
}
303
}
304
305
pub(super) struct DelayedIoApicIrqEvents {
306
/// Vec of ioapic irq events that have been delayed because the ioapic was locked when
307
/// service_irq was called on the irqchip.
308
pub events: Vec<usize>,
309
/// Event which is meant to trigger process of any irqs events that were delayed.
310
pub trigger: Event,
311
}
312
313
impl DelayedIoApicIrqEvents {
314
pub fn new() -> Result<Self> {
315
Ok(DelayedIoApicIrqEvents {
316
events: Vec::new(),
317
trigger: Event::new()?,
318
})
319
}
320
}
321
322