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