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