virt/
cpuid.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! CPUID definitions.
5
6use inspect::Inspect;
7
8/// A CPUID result.
9///
10/// This may define a partial result if some mask bits are zero. This is used to
11/// provide an update on top of another CPUID result provided elsewhere (e.g. by
12/// the hypervisor).
13#[derive(Debug, Copy, Clone)]
14pub struct CpuidLeaf {
15    /// The CPUID function/leaf, provided in eax.
16    pub function: u32,
17    /// The CPUID index/subleaf, provided in ecx. If `None`, any index value is
18    /// accepted.
19    pub index: Option<u32>,
20    /// The result.
21    pub result: [u32; 4],
22    /// The bits of the result that are valid.
23    pub mask: [u32; 4],
24}
25
26impl CpuidLeaf {
27    /// Returns a new result for the given function.
28    pub fn new(function: u32, result: [u32; 4]) -> Self {
29        Self {
30            function,
31            index: None,
32            result,
33            mask: [!0; 4],
34        }
35    }
36
37    /// Updates the result to be for specific `index` and returns it.
38    pub fn indexed(self, index: u32) -> Self {
39        Self {
40            index: Some(index),
41            ..self
42        }
43    }
44
45    /// Updates the result to be partial with the provided mask.
46    pub fn masked(self, mask: [u32; 4]) -> Self {
47        Self { mask, ..self }
48    }
49
50    fn cmp_key(&self, other: &Self) -> std::cmp::Ordering {
51        (self.function, self.index).cmp(&(other.function, other.index))
52    }
53
54    /// Returns true if this result is intended for the given `eax` and `ecx`
55    /// input values.
56    pub fn matches(&self, eax: u32, ecx: u32) -> bool {
57        self.function == eax && (self.index.is_none() || self.index == Some(ecx))
58    }
59
60    /// Applies this result to `result`, replacing bits in `result` with
61    /// `self.result` when the corresponding bits in `self.mask` are set.
62    pub fn apply(&self, result: &mut [u32; 4]) {
63        for ((x, y), m) in result.iter_mut().zip(self.result).zip(self.mask) {
64            *x &= !m;
65            *x |= y & m;
66        }
67    }
68
69    fn inspect_kv(&self) -> (String, impl '_ + Inspect) {
70        let key = if let Some(index) = self.index {
71            format!("{:#x}/{:#x}", self.function, index)
72        } else {
73            format!("{:#x}", self.function)
74        };
75        (
76            key,
77            inspect::adhoc(|req| {
78                let mut resp = req.respond();
79                resp.hex("eax", self.result[0])
80                    .hex("ebx", self.result[1])
81                    .hex("ecx", self.result[2])
82                    .hex("edx", self.result[3]);
83                if self.mask != [!0, !0, !0, !0] {
84                    resp.hex("eax_mask", self.mask[0])
85                        .hex("ebx_mask", self.mask[1])
86                        .hex("ecx_mask", self.mask[2])
87                        .hex("edx_mask", self.mask[3]);
88                }
89            }),
90        )
91    }
92}
93
94/// A collection of CPUID results.
95#[derive(Debug, Inspect, Default)]
96pub struct CpuidLeafSet {
97    #[inspect(
98        flatten,
99        with = "|x| inspect::iter_by_key(x.iter().map(|y| y.inspect_kv()))"
100    )]
101    leaves: Vec<CpuidLeaf>,
102}
103
104impl CpuidLeafSet {
105    /// Returns a new result set.
106    ///
107    /// `leaves` may contain multiple results for the same function and index.
108    /// In this case, they are merged internally (respecting their mask bits),
109    /// with later leaves overriding earlier ones.
110    pub fn new(mut leaves: Vec<CpuidLeaf>) -> Self {
111        // Sort and combine entries. Note that this must be a stable sort to
112        // preserve ordering.
113        leaves.sort_by(|x, y| x.cmp_key(y));
114        leaves.dedup_by(|right, left| {
115            if left.cmp_key(right).is_ne() {
116                return false;
117            }
118            right.apply(&mut left.result);
119            for (x, y) in left.mask.iter_mut().zip(right.mask) {
120                *x |= y;
121            }
122            true
123        });
124        Self { leaves }
125    }
126
127    /// Returns the merged leaves.
128    pub fn into_leaves(self) -> Vec<CpuidLeaf> {
129        self.leaves
130    }
131
132    /// Returns the merged leaves.
133    pub fn leaves(&self) -> &[CpuidLeaf] {
134        &self.leaves
135    }
136
137    /// Returns the result value to return for inputs `eax` and `ecx`.
138    ///
139    /// `default` provides the base value which is used for a missing leaf or
140    /// for any bits of the result whose mask bits are clear.
141    pub fn result(&self, eax: u32, ecx: u32, default: &[u32; 4]) -> [u32; 4] {
142        let mut result = *default;
143        if let Some(x) = self.leaves.iter().find(|x| x.matches(eax, ecx)) {
144            x.apply(&mut result);
145        }
146        result
147    }
148}