Skip to main content

pci_core/capabilities/
read_only.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! A generic, read-only PCI Capability (backed by an [`IntoBytes`] type).
5
6use super::PciCapability;
7use crate::spec::caps::CapabilityId;
8use chipset_device::pci::ByteEnabledDwordRead;
9use chipset_device::pci::ByteEnabledDwordWrite;
10use inspect::Inspect;
11use std::fmt::Debug;
12use zerocopy::Immutable;
13use zerocopy::IntoBytes;
14use zerocopy::KnownLayout;
15
16/// Helper to define a read-only [`PciCapability`] from an [`IntoBytes`] type.
17#[derive(Debug)]
18pub struct ReadOnlyCapability<T> {
19    label: String,
20    capability_id: CapabilityId,
21    data: T,
22}
23
24impl<T: IntoBytes + Immutable + KnownLayout> ReadOnlyCapability<T> {
25    /// Create a new [`ReadOnlyCapability`] with VENDOR_SPECIFIC capability ID
26    pub fn new(label: impl Into<String>, data: T) -> Self {
27        Self {
28            label: label.into(),
29            capability_id: CapabilityId::VENDOR_SPECIFIC,
30            data,
31        }
32    }
33
34    /// Create a new [`ReadOnlyCapability`] with a specific capability ID
35    pub fn new_with_capability_id(
36        label: impl Into<String>,
37        capability_id: CapabilityId,
38        data: T,
39    ) -> Self {
40        Self {
41            label: label.into(),
42            capability_id,
43            data,
44        }
45    }
46}
47
48impl<T: Debug> Inspect for ReadOnlyCapability<T> {
49    fn inspect(&self, req: inspect::Request<'_>) {
50        req.respond()
51            .field("label", &self.label)
52            .field("capability_id", format!("0x{:02X}", self.capability_id.0))
53            .display_debug("data", &self.data);
54    }
55}
56
57impl<T> PciCapability for ReadOnlyCapability<T>
58where
59    T: IntoBytes + Send + Sync + Debug + Immutable + KnownLayout + 'static,
60{
61    fn label(&self) -> &str {
62        &self.label
63    }
64
65    fn capability_id(&self) -> CapabilityId {
66        self.capability_id
67    }
68
69    fn len(&self) -> usize {
70        size_of::<T>()
71    }
72
73    fn read(&self, offset: u16, mut value: ByteEnabledDwordRead<'_>) {
74        let dword_value = if offset as usize + 4 <= self.len() {
75            let offset = offset.into();
76            u32::from_ne_bytes(self.data.as_bytes()[offset..offset + 4].try_into().unwrap())
77        } else {
78            !0
79        };
80        value.set(dword_value);
81    }
82
83    fn write(&mut self, offset: u16, val: ByteEnabledDwordWrite) {
84        tracelimit::warn_ratelimited!(
85            label = ?self.label,
86            ?offset,
87            ?val,
88            "write to read-only capability"
89        );
90    }
91
92    fn reset(&mut self) {}
93}
94
95mod save_restore {
96    use super::*;
97    use vmcore::save_restore::NoSavedState;
98    use vmcore::save_restore::RestoreError;
99    use vmcore::save_restore::SaveError;
100    use vmcore::save_restore::SaveRestore;
101
102    // This is a noop impl, as the capability is (by definition) read only.
103    impl<T> SaveRestore for ReadOnlyCapability<T> {
104        type SavedState = NoSavedState;
105
106        fn save(&mut self) -> Result<Self::SavedState, SaveError> {
107            Ok(NoSavedState)
108        }
109
110        fn restore(&mut self, NoSavedState: Self::SavedState) -> Result<(), RestoreError> {
111            Ok(())
112        }
113    }
114}