vmm/cpu_config/x86_64/cpuid/
mod.rs

1// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3//
4// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
5// Use of this source code is governed by a BSD-style license that can be
6// found in the THIRD-PARTY file.
7
8#![warn(clippy::pedantic)]
9#![allow(
10    clippy::blanket_clippy_restriction_lints,
11    clippy::implicit_return,
12    clippy::pattern_type_mismatch,
13    clippy::std_instead_of_alloc,
14    clippy::std_instead_of_core,
15    clippy::pub_use,
16    clippy::non_ascii_literal,
17    clippy::single_char_lifetime_names,
18    clippy::exhaustive_enums,
19    clippy::exhaustive_structs,
20    clippy::unseparated_literal_suffix,
21    clippy::mod_module_files,
22    clippy::missing_trait_methods
23)]
24// Apply CPUID specific lint adjustments.
25#![allow(
26    clippy::unreadable_literal,
27    clippy::similar_names,
28    clippy::same_name_method,
29    clippy::doc_markdown,
30    clippy::module_name_repetitions
31)]
32
33//! Utility for configuring the CPUID (CPU identification) for the guest microVM.
34
35use std::convert::TryFrom;
36use std::mem::{size_of, transmute};
37
38/// cpuid utility functions.
39pub mod common;
40
41/// AMD CPUID specification handling.
42pub mod amd;
43pub use amd::AmdCpuid;
44
45/// Intel CPUID specification handling.
46pub mod intel;
47pub use intel::IntelCpuid;
48
49/// CPUID normalize implementation.
50mod normalize;
51
52pub use normalize::{FeatureInformationError, GetMaxCpusPerPackageError, NormalizeCpuidError};
53
54/// Intel brand string.
55pub const VENDOR_ID_INTEL: &[u8; 12] = b"GenuineIntel";
56
57/// AMD brand string.
58pub const VENDOR_ID_AMD: &[u8; 12] = b"AuthenticAMD";
59
60/// Intel brand string.
61#[allow(clippy::undocumented_unsafe_blocks)]
62pub const VENDOR_ID_INTEL_STR: &str = unsafe { std::str::from_utf8_unchecked(VENDOR_ID_INTEL) };
63
64/// AMD brand string.
65#[allow(clippy::undocumented_unsafe_blocks)]
66pub const VENDOR_ID_AMD_STR: &str = unsafe { std::str::from_utf8_unchecked(VENDOR_ID_AMD) };
67
68/// To store the brand string we have 3 leaves, each with 4 registers, each with 4 bytes.
69pub const BRAND_STRING_LENGTH: usize = 3 * 4 * 4;
70
71/// Mimic of [`std::arch::x86_64::__cpuid`] that wraps [`cpuid_count`].
72fn cpuid(leaf: u32) -> std::arch::x86_64::CpuidResult {
73    cpuid_count(leaf, 0)
74}
75
76/// Safe wrapper around [`std::arch::x86_64::__cpuid_count`].
77fn cpuid_count(leaf: u32, subleaf: u32) -> std::arch::x86_64::CpuidResult {
78    // JUSTIFICATION: There is no safe alternative.
79    // SAFETY: The `cfg(cpuid)` wrapping the `cpuid` module guarantees `CPUID` is supported.
80    unsafe { std::arch::x86_64::__cpuid_count(leaf, subleaf) }
81}
82
83/// Gets the Intel default brand.
84// As we pass through host frequency, we require CPUID and thus `cfg(cpuid)`.
85/// Gets host brand string.
86///
87/// Its stored in-order with bytes flipped in each register e.g.:
88/// ```text
89/// "etnI" | ")4(l" | "oeX " | ")R(n" |
90/// "orP " | "ssec" | "@ ro" | "0.3 " |
91/// "zHG0" | null | null | null
92/// ------------------------------------
93/// Intel(R) Xeon(R) Processor @ 3.00Ghz
94/// ```
95#[inline]
96#[must_use]
97pub fn host_brand_string() -> [u8; BRAND_STRING_LENGTH] {
98    let leaf_a = cpuid(0x80000002);
99    let leaf_b = cpuid(0x80000003);
100    let leaf_c = cpuid(0x80000004);
101
102    let arr = [
103        leaf_a.eax, leaf_a.ebx, leaf_a.ecx, leaf_a.edx, leaf_b.eax, leaf_b.ebx, leaf_b.ecx,
104        leaf_b.edx, leaf_c.eax, leaf_c.ebx, leaf_c.ecx, leaf_c.edx,
105    ];
106
107    // JUSTIFICATION: There is no safe alternative.
108    // SAFETY: Transmuting `[u32;12]` to `[u8;BRAND_STRING_LENGTH]` (`[u8;48]`) is always safe.
109    unsafe { std::mem::transmute(arr) }
110}
111
112/// Trait defining shared behaviour between CPUID structures.
113pub trait CpuidTrait {
114    /// Returns the CPUID manufacturers ID (e.g. `GenuineIntel` or `AuthenticAMD`) or `None` if it
115    /// cannot be found in CPUID (e.g. leaf 0x0 is missing).
116    #[inline]
117    #[must_use]
118    fn vendor_id(&self) -> Option<[u8; 12]> {
119        let leaf_0 = self.get(&CpuidKey::leaf(0x0))?;
120
121        // The ordering of the vendor string is ebx,edx,ecx this is not a mistake.
122        let (ebx, edx, ecx) = (
123            leaf_0.result.ebx.to_ne_bytes(),
124            leaf_0.result.edx.to_ne_bytes(),
125            leaf_0.result.ecx.to_ne_bytes(),
126        );
127        let arr: [u8; 12] = [
128            ebx[0], ebx[1], ebx[2], ebx[3], edx[0], edx[1], edx[2], edx[3], ecx[0], ecx[1], ecx[2],
129            ecx[3],
130        ];
131        Some(arr)
132    }
133
134    /// Gets a given sub-leaf.
135    fn get(&self, key: &CpuidKey) -> Option<&CpuidEntry>;
136
137    /// Gets a given sub-leaf.
138    fn get_mut(&mut self, key: &CpuidKey) -> Option<&mut CpuidEntry>;
139
140    /// Applies a given brand string to CPUID.
141    ///
142    /// # Errors
143    ///
144    /// When any of the leaves 0x80000002, 0x80000003 or 0x80000004 are not present.
145    #[inline]
146    fn apply_brand_string(
147        &mut self,
148        brand_string: &[u8; BRAND_STRING_LENGTH],
149    ) -> Result<(), MissingBrandStringLeaves> {
150        // 0x80000002
151        {
152            let leaf: &mut CpuidEntry = self
153                .get_mut(&CpuidKey::leaf(0x80000002))
154                .ok_or(MissingBrandStringLeaves)?;
155            leaf.result.eax = u32::from_ne_bytes([
156                brand_string[0],
157                brand_string[1],
158                brand_string[2],
159                brand_string[3],
160            ]);
161            leaf.result.ebx = u32::from_ne_bytes([
162                brand_string[4],
163                brand_string[5],
164                brand_string[6],
165                brand_string[7],
166            ]);
167            leaf.result.ecx = u32::from_ne_bytes([
168                brand_string[8],
169                brand_string[9],
170                brand_string[10],
171                brand_string[11],
172            ]);
173            leaf.result.edx = u32::from_ne_bytes([
174                brand_string[12],
175                brand_string[13],
176                brand_string[14],
177                brand_string[15],
178            ]);
179        }
180
181        // 0x80000003
182        {
183            let leaf: &mut CpuidEntry = self
184                .get_mut(&CpuidKey::leaf(0x80000003))
185                .ok_or(MissingBrandStringLeaves)?;
186            leaf.result.eax = u32::from_ne_bytes([
187                brand_string[16],
188                brand_string[17],
189                brand_string[18],
190                brand_string[19],
191            ]);
192            leaf.result.ebx = u32::from_ne_bytes([
193                brand_string[20],
194                brand_string[21],
195                brand_string[22],
196                brand_string[23],
197            ]);
198            leaf.result.ecx = u32::from_ne_bytes([
199                brand_string[24],
200                brand_string[25],
201                brand_string[26],
202                brand_string[27],
203            ]);
204            leaf.result.edx = u32::from_ne_bytes([
205                brand_string[28],
206                brand_string[29],
207                brand_string[30],
208                brand_string[31],
209            ]);
210        }
211
212        // 0x80000004
213        {
214            let leaf: &mut CpuidEntry = self
215                .get_mut(&CpuidKey::leaf(0x80000004))
216                .ok_or(MissingBrandStringLeaves)?;
217            leaf.result.eax = u32::from_ne_bytes([
218                brand_string[32],
219                brand_string[33],
220                brand_string[34],
221                brand_string[35],
222            ]);
223            leaf.result.ebx = u32::from_ne_bytes([
224                brand_string[36],
225                brand_string[37],
226                brand_string[38],
227                brand_string[39],
228            ]);
229            leaf.result.ecx = u32::from_ne_bytes([
230                brand_string[40],
231                brand_string[41],
232                brand_string[42],
233                brand_string[43],
234            ]);
235            leaf.result.edx = u32::from_ne_bytes([
236                brand_string[44],
237                brand_string[45],
238                brand_string[46],
239                brand_string[47],
240            ]);
241        }
242
243        Ok(())
244    }
245}
246
247impl CpuidTrait for kvm_bindings::CpuId {
248    /// Gets a given sub-leaf.
249    #[allow(clippy::transmute_ptr_to_ptr, clippy::unwrap_used)]
250    #[inline]
251    fn get(&self, CpuidKey { leaf, subleaf }: &CpuidKey) -> Option<&CpuidEntry> {
252        let entry_opt = self
253            .as_slice()
254            .iter()
255            .find(|entry| entry.function == *leaf && entry.index == *subleaf);
256
257        entry_opt.map(|entry| {
258            // JUSTIFICATION: There is no safe alternative.
259            // SAFETY: The `kvm_cpuid_entry2` and `CpuidEntry` are `repr(C)` with known sizes.
260            unsafe {
261                let arr: &[u8; size_of::<kvm_bindings::kvm_cpuid_entry2>()] = transmute(entry);
262                let arr2: &[u8; size_of::<CpuidEntry>()] = arr[8..28].try_into().unwrap();
263                transmute::<_, &CpuidEntry>(arr2)
264            }
265        })
266    }
267
268    /// Gets a given sub-leaf.
269    #[allow(clippy::transmute_ptr_to_ptr, clippy::unwrap_used)]
270    #[inline]
271    fn get_mut(&mut self, CpuidKey { leaf, subleaf }: &CpuidKey) -> Option<&mut CpuidEntry> {
272        let entry_opt = self
273            .as_mut_slice()
274            .iter_mut()
275            .find(|entry| entry.function == *leaf && entry.index == *subleaf);
276        entry_opt.map(|entry| {
277            // JUSTIFICATION: There is no safe alternative.
278            // SAFETY: The `kvm_cpuid_entry2` and `CpuidEntry` are `repr(C)` with known sizes.
279            unsafe {
280                let arr: &mut [u8; size_of::<kvm_bindings::kvm_cpuid_entry2>()] = transmute(entry);
281                let arr2: &mut [u8; size_of::<CpuidEntry>()] =
282                    (&mut arr[8..28]).try_into().unwrap();
283                transmute::<_, &mut CpuidEntry>(arr2)
284            }
285        })
286    }
287}
288
289/// Error type for [`CpuidTrait::apply_brand_string`].
290#[derive(Debug, thiserror::Error, Eq, PartialEq)]
291#[error("Missing brand string leaves 0x80000002, 0x80000003 and 0x80000004.")]
292pub struct MissingBrandStringLeaves;
293
294/// Error type for conversion from `kvm_bindings::CpuId` to `Cpuid`.
295#[rustfmt::skip]
296#[derive(Debug, thiserror::Error, displaydoc::Display, PartialEq, Eq)]
297pub enum CpuidTryFromKvmCpuid {
298    /// Leaf 0 not found in the given `kvm_bindings::CpuId`.
299    MissingLeaf0,
300    /// Unsupported CPUID manufacturer id: \"{0:?}\" (only 'GenuineIntel' and 'AuthenticAMD' are supported).
301    UnsupportedVendor([u8; 12]),
302}
303
304/// CPUID information
305#[derive(Debug, Clone, PartialEq, Eq)]
306pub enum Cpuid {
307    /// Intel CPUID specific information.
308    Intel(IntelCpuid),
309    /// AMD CPUID specific information.
310    Amd(AmdCpuid),
311}
312
313impl Cpuid {
314    /// Returns `Some(&mut IntelCpuid)` if `Self == Self::Intel(_)` else returns `None`.
315    #[inline]
316    #[must_use]
317    pub fn intel_mut(&mut self) -> Option<&mut IntelCpuid> {
318        match self {
319            Self::Intel(intel) => Some(intel),
320            Self::Amd(_) => None,
321        }
322    }
323
324    /// Returns `Some(&IntelCpuid)` if `Self == Self::Intel(_)` else returns `None`.
325    #[inline]
326    #[must_use]
327    pub fn intel(&self) -> Option<&IntelCpuid> {
328        match self {
329            Self::Intel(intel) => Some(intel),
330            Self::Amd(_) => None,
331        }
332    }
333
334    /// Returns `Some(&AmdCpuid)` if `Self == Self::Amd(_)` else returns `None`.
335    #[inline]
336    #[must_use]
337    pub fn amd(&self) -> Option<&AmdCpuid> {
338        match self {
339            Self::Intel(_) => None,
340            Self::Amd(amd) => Some(amd),
341        }
342    }
343
344    /// Returns `Some(&mut AmdCpuid)` if `Self == Self::Amd(_)` else returns `None`.
345    #[inline]
346    #[must_use]
347    pub fn amd_mut(&mut self) -> Option<&mut AmdCpuid> {
348        match self {
349            Self::Intel(_) => None,
350            Self::Amd(amd) => Some(amd),
351        }
352    }
353
354    /// Returns imumutable reference to inner BTreeMap<CpuidKey, CpuidEntry>.
355    #[inline]
356    #[must_use]
357    pub fn inner(&self) -> &std::collections::BTreeMap<CpuidKey, CpuidEntry> {
358        match self {
359            Self::Intel(intel_cpuid) => &intel_cpuid.0,
360            Self::Amd(amd_cpuid) => &amd_cpuid.0,
361        }
362    }
363
364    /// Returns mutable reference to inner BTreeMap<CpuidKey, CpuidEntry>.
365    #[inline]
366    #[must_use]
367    pub fn inner_mut(&mut self) -> &mut std::collections::BTreeMap<CpuidKey, CpuidEntry> {
368        match self {
369            Self::Intel(intel_cpuid) => &mut intel_cpuid.0,
370            Self::Amd(amd_cpuid) => &mut amd_cpuid.0,
371        }
372    }
373}
374
375impl CpuidTrait for Cpuid {
376    /// Gets a given sub-leaf.
377    #[inline]
378    fn get(&self, key: &CpuidKey) -> Option<&CpuidEntry> {
379        match self {
380            Self::Intel(intel_cpuid) => intel_cpuid.get(key),
381            Self::Amd(amd_cpuid) => amd_cpuid.get(key),
382        }
383    }
384
385    /// Gets a given sub-leaf.
386    #[inline]
387    fn get_mut(&mut self, key: &CpuidKey) -> Option<&mut CpuidEntry> {
388        match self {
389            Self::Intel(intel_cpuid) => intel_cpuid.get_mut(key),
390            Self::Amd(amd_cpuid) => amd_cpuid.get_mut(key),
391        }
392    }
393}
394
395impl TryFrom<kvm_bindings::CpuId> for Cpuid {
396    type Error = CpuidTryFromKvmCpuid;
397
398    #[inline]
399    fn try_from(kvm_cpuid: kvm_bindings::CpuId) -> Result<Self, Self::Error> {
400        let vendor_id = kvm_cpuid
401            .vendor_id()
402            .ok_or(CpuidTryFromKvmCpuid::MissingLeaf0)?;
403
404        match std::str::from_utf8(&vendor_id) {
405            Ok(VENDOR_ID_INTEL_STR) => Ok(Cpuid::Intel(IntelCpuid::from(kvm_cpuid))),
406            Ok(VENDOR_ID_AMD_STR) => Ok(Cpuid::Amd(AmdCpuid::from(kvm_cpuid))),
407            _ => Err(CpuidTryFromKvmCpuid::UnsupportedVendor(vendor_id)),
408        }
409    }
410}
411
412impl TryFrom<Cpuid> for kvm_bindings::CpuId {
413    type Error = vmm_sys_util::fam::Error;
414
415    fn try_from(cpuid: Cpuid) -> Result<Self, Self::Error> {
416        let entries = cpuid
417            .inner()
418            .iter()
419            .map(|(key, entry)| kvm_bindings::kvm_cpuid_entry2 {
420                function: key.leaf,
421                index: key.subleaf,
422                flags: entry.flags.0,
423                eax: entry.result.eax,
424                ebx: entry.result.ebx,
425                ecx: entry.result.ecx,
426                edx: entry.result.edx,
427                ..Default::default()
428            })
429            .collect::<Vec<_>>();
430
431        kvm_bindings::CpuId::from_entries(&entries)
432    }
433}
434
435/// CPUID index values `leaf` and `subleaf`.
436#[derive(Debug, Clone, Default, PartialEq, Eq)]
437pub struct CpuidKey {
438    /// CPUID leaf.
439    pub leaf: u32,
440    /// CPUID subleaf.
441    pub subleaf: u32,
442}
443
444impl CpuidKey {
445    /// `CpuidKey { leaf, subleaf: 0 }`
446    #[inline]
447    #[must_use]
448    pub fn leaf(leaf: u32) -> Self {
449        Self { leaf, subleaf: 0 }
450    }
451
452    /// `CpuidKey { leaf, subleaf }`
453    #[inline]
454    #[must_use]
455    pub fn subleaf(leaf: u32, subleaf: u32) -> Self {
456        Self { leaf, subleaf }
457    }
458}
459
460impl std::cmp::PartialOrd for CpuidKey {
461    #[inline]
462    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
463        Some(std::cmp::Ord::cmp(self, other))
464    }
465}
466
467impl std::cmp::Ord for CpuidKey {
468    #[inline]
469    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
470        self.leaf
471            .cmp(&other.leaf)
472            .then(self.subleaf.cmp(&other.subleaf))
473    }
474}
475
476/// Definitions from `kvm/arch/x86/include/uapi/asm/kvm.h
477#[derive(
478    Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash,
479)]
480pub struct KvmCpuidFlags(pub u32);
481impl KvmCpuidFlags {
482    /// Zero.
483    pub const EMPTY: Self = Self(0);
484    /// Indicates if the `index` field is used for indexing sub-leaves (if false, this CPUID leaf
485    /// has no subleaves).
486    pub const SIGNIFICANT_INDEX: Self = Self(1 << 0);
487    /// Deprecated.
488    pub const STATEFUL_FUNC: Self = Self(1 << 1);
489    /// Deprecated.
490    pub const STATE_READ_NEXT: Self = Self(1 << 2);
491}
492
493#[allow(clippy::derivable_impls)]
494impl Default for KvmCpuidFlags {
495    #[inline]
496    fn default() -> Self {
497        Self(0)
498    }
499}
500
501/// CPUID entry information stored for each leaf of [`IntelCpuid`].
502#[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
503#[repr(C)]
504pub struct CpuidEntry {
505    /// The KVM requires a `flags` parameter which indicates if a given CPUID leaf has sub-leaves.
506    /// This does not change at runtime so we can save memory by not storing this under every
507    /// sub-leaf and instead fetching from a map when converting back to the KVM CPUID
508    /// structure. But for robustness we currently do store we do not use this approach.
509    ///
510    /// A map on flags would look like:
511    /// ```ignore
512    /// #[allow(clippy::non_ascii_literal)]
513    /// pub static KVM_CPUID_LEAF_FLAGS: phf::Map<u32, KvmCpuidFlags> = phf::phf_map! {
514    ///     0x00u32 => KvmCpuidFlags::EMPTY,
515    ///     0x01u32 => KvmCpuidFlags::EMPTY,
516    ///     0x02u32 => KvmCpuidFlags::EMPTY,
517    ///     0x03u32 => KvmCpuidFlags::EMPTY,
518    ///     0x04u32 => KvmCpuidFlags::SIGNIFICANT_INDEX,
519    ///     0x05u32 => KvmCpuidFlags::EMPTY,
520    ///     0x06u32 => KvmCpuidFlags::EMPTY,
521    ///     0x07u32 => KvmCpuidFlags::SIGNIFICANT_INDEX,
522    ///     0x09u32 => KvmCpuidFlags::EMPTY,
523    ///     0x0Au32 => KvmCpuidFlags::EMPTY,
524    ///     0x0Bu32 => KvmCpuidFlags::SIGNIFICANT_INDEX,
525    ///     0x0Fu32 => KvmCpuidFlags::SIGNIFICANT_INDEX,
526    ///     0x10u32 => KvmCpuidFlags::SIGNIFICANT_INDEX,
527    ///     0x12u32 => KvmCpuidFlags::SIGNIFICANT_INDEX,
528    ///     0x14u32 => KvmCpuidFlags::SIGNIFICANT_INDEX,
529    ///     0x15u32 => KvmCpuidFlags::EMPTY,
530    ///     0x16u32 => KvmCpuidFlags::EMPTY,
531    ///     0x17u32 => KvmCpuidFlags::SIGNIFICANT_INDEX,
532    ///     0x18u32 => KvmCpuidFlags::SIGNIFICANT_INDEX,
533    ///     0x19u32 => KvmCpuidFlags::EMPTY,
534    ///     0x1Au32 => KvmCpuidFlags::EMPTY,
535    ///     0x1Bu32 => KvmCpuidFlags::EMPTY,
536    ///     0x1Cu32 => KvmCpuidFlags::EMPTY,
537    ///     0x1Fu32 => KvmCpuidFlags::SIGNIFICANT_INDEX,
538    ///     0x20u32 => KvmCpuidFlags::EMPTY,
539    ///     0x80000000u32 => KvmCpuidFlags::EMPTY,
540    ///     0x80000001u32 => KvmCpuidFlags::EMPTY,
541    ///     0x80000002u32 => KvmCpuidFlags::EMPTY,
542    ///     0x80000003u32 => KvmCpuidFlags::EMPTY,
543    ///     0x80000004u32 => KvmCpuidFlags::EMPTY,
544    ///     0x80000005u32 => KvmCpuidFlags::EMPTY,
545    ///     0x80000006u32 => KvmCpuidFlags::EMPTY,
546    ///     0x80000007u32 => KvmCpuidFlags::EMPTY,
547    ///     0x80000008u32 => KvmCpuidFlags::EMPTY,
548    /// };
549    /// ```
550    pub flags: KvmCpuidFlags,
551    /// Register values.
552    pub result: CpuidRegisters,
553}
554
555/// To transmute this into leaves such that we can return mutable reference to it with leaf specific
556/// accessors, requires this to have a consistent member ordering.
557/// [`core::arch::x86_64::CpuidResult`] is not `repr(C)`.
558#[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
559#[repr(C)]
560pub struct CpuidRegisters {
561    /// EAX
562    pub eax: u32,
563    /// EBX
564    pub ebx: u32,
565    /// ECX
566    pub ecx: u32,
567    /// EDX
568    pub edx: u32,
569}
570
571impl From<core::arch::x86_64::CpuidResult> for CpuidRegisters {
572    #[inline]
573    fn from(
574        core::arch::x86_64::CpuidResult { eax, ebx, ecx, edx }: core::arch::x86_64::CpuidResult,
575    ) -> Self {
576        Self { eax, ebx, ecx, edx }
577    }
578}
579
580#[cfg(test)]
581mod tests {
582    use std::collections::BTreeMap;
583
584    use super::*;
585
586    fn build_intel_leaf0_for_cpuid() -> (CpuidKey, CpuidEntry) {
587        (
588            CpuidKey {
589                leaf: 0x0,
590                subleaf: 0x0,
591            },
592            CpuidEntry {
593                flags: KvmCpuidFlags::EMPTY,
594                result: CpuidRegisters {
595                    eax: 0x1,
596                    // GenuineIntel
597                    ebx: 0x756E6547,
598                    ecx: 0x6C65746E,
599                    edx: 0x49656E69,
600                },
601            },
602        )
603    }
604
605    fn build_intel_leaf0_for_kvmcpuid() -> kvm_bindings::kvm_cpuid_entry2 {
606        kvm_bindings::kvm_cpuid_entry2 {
607            function: 0x0,
608            index: 0x0,
609            flags: 0x0,
610            eax: 0x1,
611            // GenuineIntel
612            ebx: 0x756E6547,
613            ecx: 0x6C65746E,
614            edx: 0x49656E69,
615            ..Default::default()
616        }
617    }
618
619    fn build_amd_leaf0_for_cpuid() -> (CpuidKey, CpuidEntry) {
620        (
621            CpuidKey {
622                leaf: 0x0,
623                subleaf: 0x0,
624            },
625            CpuidEntry {
626                flags: KvmCpuidFlags::EMPTY,
627                result: CpuidRegisters {
628                    eax: 0x1,
629                    // AuthenticAMD
630                    ebx: 0x68747541,
631                    ecx: 0x444D4163,
632                    edx: 0x69746E65,
633                },
634            },
635        )
636    }
637
638    fn build_amd_leaf0_for_kvmcpuid() -> kvm_bindings::kvm_cpuid_entry2 {
639        kvm_bindings::kvm_cpuid_entry2 {
640            function: 0x0,
641            index: 0x0,
642            flags: 0x0,
643            eax: 0x1,
644            // AuthenticAMD
645            ebx: 0x68747541,
646            ecx: 0x444D4163,
647            edx: 0x69746E65,
648            ..Default::default()
649        }
650    }
651
652    fn build_sample_leaf_for_cpuid() -> (CpuidKey, CpuidEntry) {
653        (
654            CpuidKey {
655                leaf: 0x1,
656                subleaf: 0x2,
657            },
658            CpuidEntry {
659                flags: KvmCpuidFlags::SIGNIFICANT_INDEX,
660                result: CpuidRegisters {
661                    eax: 0x3,
662                    ebx: 0x4,
663                    ecx: 0x5,
664                    edx: 0x6,
665                },
666            },
667        )
668    }
669
670    fn build_sample_leaf_for_kvmcpuid() -> kvm_bindings::kvm_cpuid_entry2 {
671        kvm_bindings::kvm_cpuid_entry2 {
672            function: 0x1,
673            index: 0x2,
674            flags: 0x1,
675            eax: 0x3,
676            ebx: 0x4,
677            ecx: 0x5,
678            edx: 0x6,
679            ..Default::default()
680        }
681    }
682
683    fn build_sample_intel_cpuid() -> Cpuid {
684        Cpuid::Intel(IntelCpuid(BTreeMap::from([
685            build_intel_leaf0_for_cpuid(),
686            build_sample_leaf_for_cpuid(),
687        ])))
688    }
689
690    fn build_sample_intel_kvmcpuid() -> kvm_bindings::CpuId {
691        kvm_bindings::CpuId::from_entries(&[
692            build_intel_leaf0_for_kvmcpuid(),
693            build_sample_leaf_for_kvmcpuid(),
694        ])
695        .unwrap()
696    }
697
698    fn build_sample_amd_cpuid() -> Cpuid {
699        Cpuid::Amd(AmdCpuid(BTreeMap::from([
700            build_amd_leaf0_for_cpuid(),
701            build_sample_leaf_for_cpuid(),
702        ])))
703    }
704
705    fn build_sample_amd_kvmcpuid() -> kvm_bindings::CpuId {
706        kvm_bindings::CpuId::from_entries(&[
707            build_amd_leaf0_for_kvmcpuid(),
708            build_sample_leaf_for_kvmcpuid(),
709        ])
710        .unwrap()
711    }
712
713    #[test]
714    fn get() {
715        let cpuid = build_sample_intel_cpuid();
716        assert_eq!(
717            cpuid.get(&CpuidKey {
718                leaf: 0x8888,
719                subleaf: 0x0
720            }),
721            None
722        );
723        assert!(
724            cpuid
725                .get(&CpuidKey {
726                    leaf: 0x0,
727                    subleaf: 0x0,
728                })
729                .is_some()
730        );
731    }
732
733    #[test]
734    fn get_mut() {
735        let mut cpuid = build_sample_intel_cpuid();
736        assert_eq!(
737            cpuid.get_mut(&CpuidKey {
738                leaf: 0x888,
739                subleaf: 0x0,
740            }),
741            None
742        );
743        assert!(
744            cpuid
745                .get_mut(&CpuidKey {
746                    leaf: 0x0,
747                    subleaf: 0x0,
748                })
749                .is_some()
750        );
751    }
752
753    #[test]
754    fn test_kvmcpuid_to_cpuid() {
755        let kvm_cpuid = build_sample_intel_kvmcpuid();
756        let cpuid = Cpuid::try_from(kvm_cpuid).unwrap();
757        assert_eq!(cpuid, build_sample_intel_cpuid());
758
759        let kvm_cpuid = build_sample_amd_kvmcpuid();
760        let cpuid = Cpuid::try_from(kvm_cpuid).unwrap();
761        assert_eq!(cpuid, build_sample_amd_cpuid());
762    }
763
764    #[test]
765    fn test_cpuid_to_kvmcpuid() {
766        let cpuid = build_sample_intel_cpuid();
767        let kvm_cpuid = kvm_bindings::CpuId::try_from(cpuid).unwrap();
768        assert_eq!(kvm_cpuid, build_sample_intel_kvmcpuid());
769
770        let cpuid = build_sample_amd_cpuid();
771        let kvm_cpuid = kvm_bindings::CpuId::try_from(cpuid).unwrap();
772        assert_eq!(kvm_cpuid, build_sample_amd_kvmcpuid());
773    }
774
775    #[test]
776    fn test_invalid_kvmcpuid_to_cpuid() {
777        // If leaf 0 contains invalid vendor ID, the type conversion should fail.
778        let kvm_cpuid =
779            kvm_bindings::CpuId::from_entries(&[kvm_bindings::kvm_cpuid_entry2::default()])
780                .unwrap();
781        let cpuid = Cpuid::try_from(kvm_cpuid);
782        assert_eq!(cpuid, Err(CpuidTryFromKvmCpuid::UnsupportedVendor([0; 12])));
783    }
784}