vmotherboard/chipset/
mod.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Notable Exports: [`Chipset`], [`ChipsetBuilder`]
5
6pub mod backing;
7mod builder;
8mod io_ranges;
9mod line_sets;
10
11pub use self::builder::ChipsetBuilder;
12pub use self::builder::ChipsetDevices;
13
14use self::io_ranges::IoRanges;
15use self::io_ranges::LookupResult;
16use crate::DebugEventHandler;
17use chipset_device::ChipsetDevice;
18use chipset_device::io::IoError;
19use chipset_device::io::IoResult;
20use closeable_mutex::CloseableMutex;
21use cvm_tracing::CVM_CONFIDENTIAL;
22use inspect::Inspect;
23use std::future::poll_fn;
24use std::sync::Arc;
25
26/// The "glue" that interconnects virtual devices, and exposes an API for
27/// external entities (such as VCPUs) to access devices.
28#[derive(Inspect)]
29pub struct Chipset {
30    #[inspect(rename = "mmio")]
31    mmio_ranges: IoRanges<u64>,
32    #[inspect(rename = "pio")]
33    pio_ranges: IoRanges<u16>,
34
35    #[inspect(rename = "has_pic", with = "Option::is_some")]
36    pic: Option<Arc<CloseableMutex<dyn ChipsetDevice>>>,
37    #[inspect(rename = "has_eoi_handler", with = "Option::is_some")]
38    eoi_handler: Option<Arc<CloseableMutex<dyn ChipsetDevice>>>,
39
40    #[inspect(skip)]
41    debug_event_handler: Arc<dyn DebugEventHandler>,
42}
43
44enum IoType<'a> {
45    Read(&'a mut [u8]),
46    Write(&'a [u8]),
47}
48
49#[derive(Debug)]
50enum IoKind {
51    Pio,
52    Mmio,
53}
54
55impl IoType<'_> {
56    fn bytes(&self) -> &[u8] {
57        match self {
58            IoType::Read(b) => b,
59            IoType::Write(b) => b,
60        }
61    }
62}
63
64impl Chipset {
65    async fn handle_io_result(
66        &self,
67        lookup: LookupResult,
68        vp: u32,
69        kind: IoKind,
70        address: u64,
71        len: usize,
72        mut io_type: IoType<'_>,
73        result: IoResult,
74    ) {
75        if lookup.debug_break {
76            tracing::warn!(
77                device = &*lookup.dev_name,
78                address,
79                len,
80                ?kind,
81                "debug break due to io"
82            );
83            self.debug_event_handler.on_debug_break(Some(vp));
84        }
85        let r = match result {
86            IoResult::Ok => Ok(()),
87            IoResult::Defer(mut token) => match &mut io_type {
88                IoType::Read(bytes) => {
89                    let r = poll_fn(|cx| token.poll_read(cx, bytes)).await;
90                    if r.is_err() {
91                        bytes.fill(!0);
92                    }
93                    r
94                }
95                IoType::Write(_) => poll_fn(|cx| token.poll_write(cx)).await,
96            },
97            IoResult::Err(err) => {
98                let error = match err {
99                    IoError::InvalidRegister => "register not present",
100                    IoError::InvalidAccessSize => "invalid access size",
101                    IoError::UnalignedAccess => "unaligned access",
102                };
103                match &mut io_type {
104                    IoType::Read(bytes) => {
105                        // Fill data with !0 to indicate an error to the guest.
106                        bytes.fill(!0);
107                        tracelimit::warn_ratelimited!(
108                            CVM_CONFIDENTIAL,
109                            device = &*lookup.dev_name,
110                            address,
111                            len,
112                            ?kind,
113                            error,
114                            "device io read error"
115                        );
116                    }
117                    IoType::Write(bytes) => tracelimit::warn_ratelimited!(
118                        CVM_CONFIDENTIAL,
119                        device = &*lookup.dev_name,
120                        address,
121                        len,
122                        ?kind,
123                        error,
124                        ?bytes,
125                        "device io write error"
126                    ),
127                }
128                Ok(())
129            }
130        };
131
132        match r {
133            Ok(()) => {
134                if let Some(range_name) = &lookup.trace {
135                    // Don't lower the tracing level or the whole thing is
136                    // useless.
137                    tracing::info!(
138                        device = &*lookup.dev_name,
139                        range_name = range_name.as_ref(),
140                        ?kind,
141                        address,
142                        direction = if matches!(io_type, IoType::Read(_)) {
143                            "read"
144                        } else {
145                            "write"
146                        },
147                        data = format_args!("{:02x?}", io_type.bytes()),
148                        "device io"
149                    );
150                }
151            }
152            Err(err) => {
153                tracelimit::error_ratelimited!(
154                    CVM_CONFIDENTIAL,
155                    device = &*lookup.dev_name,
156                    ?kind,
157                    address,
158                    direction = if matches!(io_type, IoType::Read(_)) {
159                        "read"
160                    } else {
161                        "write"
162                    },
163                    error = &err as &dyn std::error::Error,
164                    "deferred io failed"
165                );
166            }
167        }
168    }
169
170    /// Dispatch a MMIO read to the given address.
171    pub async fn mmio_read(&self, vp: u32, address: u64, data: &mut [u8]) {
172        let lookup = self.mmio_ranges.lookup(address, true);
173        let r = lookup
174            .dev
175            .lock()
176            .supports_mmio()
177            .expect("objects on the mmio bus support mmio")
178            .mmio_read(address, data);
179
180        self.handle_io_result(
181            lookup,
182            vp,
183            IoKind::Mmio,
184            address,
185            data.len(),
186            IoType::Read(data),
187            r,
188        )
189        .await
190    }
191
192    /// Dispatch a MMIO write to the given address.
193    pub async fn mmio_write(&self, vp: u32, address: u64, data: &[u8]) {
194        let lookup = self.mmio_ranges.lookup(address, false);
195        let r = lookup
196            .dev
197            .lock()
198            .supports_mmio()
199            .expect("objects on the mmio bus support mmio")
200            .mmio_write(address, data);
201
202        self.handle_io_result(
203            lookup,
204            vp,
205            IoKind::Mmio,
206            address,
207            data.len(),
208            IoType::Write(data),
209            r,
210        )
211        .await
212    }
213
214    /// Check if a MMIO device exists at the given address
215    pub fn is_mmio(&self, addr: u64) -> bool {
216        self.mmio_ranges.is_occupied(addr)
217    }
218
219    /// Dispatch a Port IO read to the given address.
220    pub async fn io_read(&self, vp: u32, port: u16, data: &mut [u8]) {
221        let lookup = self.pio_ranges.lookup(port, true);
222        let r = lookup
223            .dev
224            .lock()
225            .supports_pio()
226            .expect("objects on the pio bus support pio")
227            .io_read(port, data);
228
229        self.handle_io_result(
230            lookup,
231            vp,
232            IoKind::Pio,
233            port.into(),
234            data.len(),
235            IoType::Read(data),
236            r,
237        )
238        .await
239    }
240
241    /// Dispatch a Port IO write to the given address.
242    pub async fn io_write(&self, vp: u32, port: u16, data: &[u8]) {
243        let lookup = self.pio_ranges.lookup(port, false);
244        let r = lookup
245            .dev
246            .lock()
247            .supports_pio()
248            .expect("objects on the pio bus support pio")
249            .io_write(port, data);
250
251        self.handle_io_result(
252            lookup,
253            vp,
254            IoKind::Pio,
255            port.into(),
256            data.len(),
257            IoType::Write(data),
258            r,
259        )
260        .await
261    }
262
263    /// Gets the vector of the next interrupt to inject from the legacy
264    /// interrupt controller (PIC) and sets the IRQ in service.
265    pub fn acknowledge_pic_interrupt(&self) -> Option<u8> {
266        self.pic
267            .as_ref()?
268            .lock()
269            .supports_acknowledge_pic_interrupt()
270            .unwrap()
271            .acknowledge_interrupt()
272    }
273
274    /// Handle End Of Interrupt (EOI)
275    ///
276    /// A `u32` is used for the IRQ value for (future) ARM compat.
277    pub fn handle_eoi(&self, irq: u32) {
278        if let Some(eoi_handler) = &self.eoi_handler {
279            eoi_handler
280                .lock()
281                .supports_handle_eoi()
282                .unwrap()
283                .handle_eoi(irq);
284        } else {
285            unreachable!("eoi exit received without a registered interrupt controller");
286        }
287    }
288}
289
290#[derive(Debug)]
291pub enum PciConflictReason {
292    ExistingDev(Arc<str>),
293    MissingBus,
294}
295
296#[derive(Debug)]
297pub struct PciConflict {
298    pub bdf: (u8, u8, u8),
299    pub conflict_dev: Arc<str>,
300    pub reason: PciConflictReason,
301}
302
303impl std::fmt::Display for PciConflict {
304    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
305        match &self.reason {
306            PciConflictReason::ExistingDev(existing_dev) => {
307                let (b, d, f) = self.bdf;
308                write!(
309                    fmt,
310                    "cannot attach {} to {:02x}:{:02x}:{}, already occupied by {}",
311                    self.conflict_dev, b, d, f, existing_dev
312                )
313            }
314            PciConflictReason::MissingBus => {
315                let (b, d, f) = self.bdf;
316                write!(
317                    fmt,
318                    "cannot attach {} to {:02x}:{:02x}:{}, no valid PCI bus",
319                    self.conflict_dev, b, d, f
320                )
321            }
322        }
323    }
324}