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