devices/irqchip/
x86_64.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::ops::Index;
6use std::vec::Vec;
7
8use anyhow::anyhow;
9use anyhow::Context;
10use base::Error;
11use base::Event;
12use base::Result;
13use hypervisor::IoapicState;
14use hypervisor::IrqRoute;
15use hypervisor::IrqSource;
16use hypervisor::IrqSourceChip;
17use hypervisor::LapicState;
18use hypervisor::MPState;
19use hypervisor::PicSelect;
20use hypervisor::PicState;
21use hypervisor::PitState;
22use serde::Deserialize;
23use serde::Serialize;
24use snapshot::AnySnapshot;
25
26use crate::IrqChip;
27use crate::IrqChipCap;
28
29pub 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)]
151struct 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.
162pub 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
171impl 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
284impl Default for Routes {
285    fn default() -> Self {
286        Self::new()
287    }
288}
289
290const EMPTY_ROUTE: [IrqSource; 0] = [];
291
292impl 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
305pub(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
313impl DelayedIoApicIrqEvents {
314    pub fn new() -> Result<Self> {
315        Ok(DelayedIoApicIrqEvents {
316            events: Vec::new(),
317            trigger: Event::new()?,
318        })
319    }
320}