From d3d13efcbf465bd5f7959d19da28af9cacbca1da Mon Sep 17 00:00:00 2001 From: Serban Iorga Date: Tue, 19 Feb 2019 10:00:28 +0200 Subject: [PATCH] cpuid crate refactoring - part 2 - move leaf handlers into separate functions - test leaf handlers individually - move all the templates into a folder Signed-off-by: Serban Iorga --- cpuid/src/bit_helper.rs | 37 + cpuid/src/brand_string.rs | 3 +- cpuid/src/cpu_leaf.rs | 50 +- cpuid/src/lib.rs | 1247 +---------------- cpuid/src/{c3_template.rs => template/c3.rs} | 0 cpuid/src/template/mod.rs | 7 + cpuid/src/{t2_template.rs => template/t2.rs} | 0 cpuid/src/transformer/common.rs | 112 ++ cpuid/src/transformer/intel.rs | 337 +++++ cpuid/src/transformer/mod.rs | 70 + .../integration_tests/build/test_coverage.py | 3 +- vmm/src/vstate.rs | 24 +- 12 files changed, 597 insertions(+), 1293 deletions(-) rename cpuid/src/{c3_template.rs => template/c3.rs} (100%) create mode 100644 cpuid/src/template/mod.rs rename cpuid/src/{t2_template.rs => template/t2.rs} (100%) create mode 100644 cpuid/src/transformer/common.rs create mode 100644 cpuid/src/transformer/intel.rs create mode 100644 cpuid/src/transformer/mod.rs diff --git a/cpuid/src/bit_helper.rs b/cpuid/src/bit_helper.rs index b325d8c6c10..9b6ea3d44f7 100644 --- a/cpuid/src/bit_helper.rs +++ b/cpuid/src/bit_helper.rs @@ -1,6 +1,8 @@ // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +#![macro_use] + /// Structure representing a range of bits in a number. /// /// # Example @@ -86,9 +88,22 @@ impl BitRangeExt for BitRange { } } +macro_rules! bit_range { + ($msb_index:expr, $lsb_index:expr) => { + BitRange { + msb_index: $msb_index, + lsb_index: $lsb_index, + } + }; +} + /// Trait containing helper methods for bit operations. /// pub trait BitHelper { + /// Reads the value of the bit at position `pos` + /// + fn read_bit(&self, pos: u32) -> bool; + /// Changes the value of the bit at position `pos` to `val` /// fn write_bit(&mut self, pos: u32, val: bool) -> &mut Self; @@ -144,6 +159,12 @@ pub trait BitHelper { } impl BitHelper for u32 { + fn read_bit(&self, pos: u32) -> bool { + assert!(pos <= MAX_U32_BIT_INDEX, "Invalid pos"); + + (*self & (1 << pos)) > 0 + } + fn write_bit(&mut self, pos: u32, val: bool) -> &mut Self { assert!(pos <= MAX_U32_BIT_INDEX, "Invalid pos"); @@ -215,6 +236,22 @@ mod tests { assert!(val == 0); } + #[test] + #[should_panic] + fn test_invalid_read_bit() { + // Set bit to 1 + let val: u32 = 0; + val.read_bit(32); + } + + #[test] + fn test_simple_read_bit() { + // Set bit to 1 + let val: u32 = 0b100000; + assert!(val.read_bit(5)); + assert!(!val.read_bit(4)); + } + #[test] fn test_chained_write_bit() { let mut val: u32 = 1 << 12; diff --git a/cpuid/src/brand_string.rs b/cpuid/src/brand_string.rs index 59fb43ac816..3cecb961a8d 100644 --- a/cpuid/src/brand_string.rs +++ b/cpuid/src/brand_string.rs @@ -4,7 +4,7 @@ use std::arch::x86_64::__cpuid as host_cpuid; use std::slice; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum Error { NotSupported, Overflow(String), @@ -23,6 +23,7 @@ pub enum Reg { /// This is achieved by bypassing the `O(n)` indexing, heap allocation, and the unicode checks /// done by `std::string::String`. /// +#[derive(Clone)] pub struct BrandString { /// Flattened buffer, holding an array of 32-bit register values. /// diff --git a/cpuid/src/cpu_leaf.rs b/cpuid/src/cpu_leaf.rs index f5025d6afed..67fdb98f0b5 100644 --- a/cpuid/src/cpu_leaf.rs +++ b/cpuid/src/cpu_leaf.rs @@ -16,20 +16,11 @@ pub mod leaf_0x1 { use bit_helper::BitRange; // The bit-range containing the (fixed) default APIC ID. - pub const APICID_BITRANGE: BitRange = BitRange { - msb_index: 31, - lsb_index: 24, - }; + pub const APICID_BITRANGE: BitRange = bit_range!(31, 24); // The bit-range containing the number of bytes flushed when executing CLFLUSH. - pub const CLFLUSH_SIZE_BITRANGE: BitRange = BitRange { - msb_index: 15, - lsb_index: 8, - }; + pub const CLFLUSH_SIZE_BITRANGE: BitRange = bit_range!(15, 8); // The bit-range containing the logical processor count. - pub const CPU_COUNT_BITRANGE: BitRange = BitRange { - msb_index: 23, - lsb_index: 16, - }; + pub const CPU_COUNT_BITRANGE: BitRange = bit_range!(23, 16); } pub mod ecx { @@ -77,18 +68,9 @@ pub mod leaf_0x4 { pub mod eax { use bit_helper::BitRange; - pub const CACHE_LEVEL_BITRANGE: BitRange = BitRange { - msb_index: 7, - lsb_index: 5, - }; - pub const MAX_ADDR_IDS_SHARING_CACHE_BITRANGE: BitRange = BitRange { - msb_index: 25, - lsb_index: 14, - }; - pub const MAX_ADDR_IDS_IN_PACKAGE_BITRANGE: BitRange = BitRange { - msb_index: 31, - lsb_index: 26, - }; + pub const CACHE_LEVEL_BITRANGE: BitRange = bit_range!(7, 5); + pub const MAX_CPUS_PER_CORE_BITRANGE: BitRange = bit_range!(25, 14); + pub const MAX_CORES_PER_PACKAGE_BITRANGE: BitRange = bit_range!(31, 26); } } @@ -184,10 +166,7 @@ pub mod leaf_0xb { // The bit-range containing the number of bits to shift right the APIC ID in order to get // the next level APIC ID - pub const APICID_BITRANGE: BitRange = BitRange { - msb_index: 4, - lsb_index: 0, - }; + pub const APICID_BITRANGE: BitRange = bit_range!(4, 0); } pub mod ebx { @@ -195,22 +174,13 @@ pub mod leaf_0xb { // The bit-range containing the number of factory-configured logical processors // at the current cache level - pub const NUM_LOGICAL_PROCESSORS_BITRANGE: BitRange = BitRange { - msb_index: 15, - lsb_index: 0, - }; + pub const NUM_LOGICAL_PROCESSORS_BITRANGE: BitRange = bit_range!(15, 0); } pub mod ecx { use bit_helper::BitRange; - pub const LEVEL_TYPE_BITRANGE: BitRange = BitRange { - msb_index: 15, - lsb_index: 8, - }; - pub const LEVEL_NUMBER_BITRANGE: BitRange = BitRange { - msb_index: 7, - lsb_index: 0, - }; + pub const LEVEL_TYPE_BITRANGE: BitRange = bit_range!(15, 8); + pub const LEVEL_NUMBER_BITRANGE: BitRange = bit_range!(7, 0); } } diff --git a/cpuid/src/lib.rs b/cpuid/src/lib.rs index 1192d135767..7fb7d139e58 100644 --- a/cpuid/src/lib.rs +++ b/cpuid/src/lib.rs @@ -11,38 +11,22 @@ extern crate kvm; extern crate kvm_bindings; -use std::result; - use kvm::CpuId; /// Contains helper methods for bit operations. pub mod bit_helper; -mod brand_string; -/// Follows a C3 template in setting up the CPUID. -pub mod c3_template; -mod cpu_leaf; -/// Follows a T2 template in setting up the CPUID. -pub mod t2_template; -use bit_helper::BitHelper; -use brand_string::BrandString; -use brand_string::Reg as BsReg; -use cpu_leaf::*; +mod template; +pub use template::c3; +pub use template::t2; -/// Errors associated with configuring the CPUID entries. -#[derive(Debug)] -pub enum Error { - /// The maximum number of addressable logical CPUs cannot be stored in an `u8`. - VcpuCountOverflow, - /// Failure with getting brand string. - CreateBrandString(brand_string::Error), -} +mod cpu_leaf; -/// Type for returning functions outcome. -pub type Result = result::Result; +mod transformer; +pub use transformer::Error; +use transformer::*; -/// Type for propagating errors that can be optionally handled. -pub type PartialError = (T, Option); +mod brand_string; /// Sets up the CPUID entries for the given vcpu. /// @@ -74,1216 +58,13 @@ pub fn filter_cpuid( cpu_count: u8, ht_enabled: bool, kvm_cpuid: &mut CpuId, -) -> Result<()> { - let entries = kvm_cpuid.mut_entries_slice(); - let max_addr_cpu = u32::from(get_max_addressable_lprocessors(cpu_count)?); - - let res = get_brand_string(); - let bstr = res.0; - - for entry in entries.iter_mut() { - match entry.function { - 0x1 => { - use leaf_0x1::*; - - // X86 hypervisor feature - entry - .ecx - .write_bit(ecx::TSC_DEADLINE_TIMER_BITINDEX, true) - .write_bit(ecx::HYPERVISOR_BITINDEX, true); - - entry - .ebx - .write_bits_in_range(&ebx::APICID_BITRANGE, u32::from(cpu_id)) - .write_bits_in_range(&ebx::CLFLUSH_SIZE_BITRANGE, EBX_CLFLUSH_CACHELINE) - .write_bits_in_range(&ebx::CPU_COUNT_BITRANGE, max_addr_cpu); - - // A value of 1 for HTT indicates the value in CPUID.1.EBX[23:16] - // (the Maximum number of addressable IDs for logical processors in this package) - // is valid for the package - entry.edx.write_bit(edx::HTT, cpu_count > 1); - } - 0x4 => { - // Deterministic Cache Parameters Leaf - use leaf_0x4::*; - - match entry.eax.read_bits_in_range(&eax::CACHE_LEVEL_BITRANGE) { - // L1 & L2 Cache - 1 | 2 => { - // The L1 & L2 cache is shared by at most 2 hyperthreads - entry.eax.write_bits_in_range( - &eax::MAX_ADDR_IDS_SHARING_CACHE_BITRANGE, - (cpu_count > 1 && ht_enabled) as u32, - ); - } - // L3 Cache - 3 => { - // The L3 cache is shared among all the logical threads - entry.eax.write_bits_in_range( - &eax::MAX_ADDR_IDS_SHARING_CACHE_BITRANGE, - u32::from(cpu_count - 1), - ); - } - _ => (), - } +) -> Result<(), Error> { + let vm_spec = VmSpec::new(cpu_id, cpu_count, ht_enabled); - // Put all the cores in the same socket - entry.eax.write_bits_in_range( - &eax::MAX_ADDR_IDS_IN_PACKAGE_BITRANGE, - u32::from(cpu_count - 1), - ); - } - 0x6 => { - use leaf_0x6::*; - - // Disable Turbo Boost - entry.eax.write_bit(eax::TURBO_BOOST_BITINDEX, false); - // Clear X86 EPB feature. No frequency selection in the hypervisor. - entry.ecx.write_bit(ecx::EPB_BITINDEX, false); - } - 0xA => { - // Architectural Performance Monitor Leaf - // Disable PMU - entry.eax = 0; - entry.ebx = 0; - entry.ecx = 0; - entry.edx = 0; - } - 0xB => { - use leaf_0xb::*; - //reset eax, ebx, ecx - entry.eax = 0 as u32; - entry.ebx = 0 as u32; - entry.ecx = 0 as u32; - // EDX bits 31..0 contain x2APIC ID of current logical processor - // x2APIC increases the size of the APIC ID from 8 bits to 32 bits - entry.edx = u32::from(cpu_id); - match entry.index { - // Thread Level Topology; index = 0 - 0 => { - // To get the next level APIC ID, shift right with at most 1 because we have - // maximum 2 hyperthreads per core that can be represented by 1 bit. - entry.eax.write_bits_in_range( - &eax::APICID_BITRANGE, - (cpu_count > 1 && ht_enabled) as u32, - ); - // When cpu_count == 1 or HT is disabled, there is 1 logical core at this level - // Otherwise there are 2 - entry.ebx.write_bits_in_range( - &ebx::NUM_LOGICAL_PROCESSORS_BITRANGE, - 1 + (cpu_count > 1 && ht_enabled) as u32, - ); - - entry.ecx.write_bits_in_range(&ecx::LEVEL_TYPE_BITRANGE, { - if cpu_count == 1 { - // There are no hyperthreads for 1 VCPU, set the level type = 2 (Core) - LEVEL_TYPE_CORE - } else { - LEVEL_TYPE_THREAD - } - }); - } - // Core Level Processor Topology; index = 1 - 1 => { - entry - .eax - .write_bits_in_range(&eax::APICID_BITRANGE, LEAFBH_INDEX1_APICID); - entry - .ecx - .write_bits_in_range(&ecx::LEVEL_NUMBER_BITRANGE, entry.index as u32); - if cpu_count == 1 { - // For 1 vCPU, this level is invalid - entry - .ecx - .write_bits_in_range(&ecx::LEVEL_TYPE_BITRANGE, LEVEL_TYPE_INVALID); - } else { - entry.ebx.write_bits_in_range( - &ebx::NUM_LOGICAL_PROCESSORS_BITRANGE, - u32::from(cpu_count), - ); - entry - .ecx - .write_bits_in_range(&ecx::LEVEL_TYPE_BITRANGE, LEVEL_TYPE_CORE); - } - } - // Core Level Processor Topology; index >=2 - // No other levels available; This should already be set to correctly, - // and it is added here as a "re-enforcement" in case we run on - // different hardware - level => { - entry.ecx = level; - } - } - } - 0x8000_0002..=0x8000_0004 => { - entry.eax = bstr.get_reg_for_leaf(entry.function, BsReg::EAX); - entry.ebx = bstr.get_reg_for_leaf(entry.function, BsReg::EBX); - entry.ecx = bstr.get_reg_for_leaf(entry.function, BsReg::ECX); - entry.edx = bstr.get_reg_for_leaf(entry.function, BsReg::EDX); - } - _ => (), - } - } - - if let Some(e) = res.1 { - Err(Error::CreateBrandString(e)) - } else { - Ok(()) - } -} - -// constants for setting the fields of kvm_cpuid2 structures -// CPUID bits in ebx, ecx, and edx. -const EBX_CLFLUSH_CACHELINE: u32 = 8; // Flush a cache line size. - -// The APIC ID shift in leaf 0xBh specifies the number of bits to shit the x2APIC ID to get a -// unique topology of the next level. This allows 64 logical processors/package. -const LEAFBH_INDEX1_APICID: u32 = 6; - -const DEFAULT_BRAND_STRING: &[u8] = b"Intel(R) Xeon(R) Processor"; - -/// Sets leaf 01H EBX[23-16]. -/// -/// The maximum number of addressable logical CPUs is computed as the closest power of 2 -/// higher or equal to the CPU count configured by the user. -fn get_max_addressable_lprocessors(cpu_count: u8) -> Result { - let mut max_addressable_lcpu = f64::from(cpu_count).log2().ceil(); - max_addressable_lcpu = f64::from(2).powf(max_addressable_lcpu); - // check that this number is still an u8 - if max_addressable_lcpu > u8::max_value().into() { - return Err(Error::VcpuCountOverflow); + let transform_entry_fn: EntryTransformerFn = intel::transform_entry; + for entry in kvm_cpuid.mut_entries_slice().iter_mut() { + transform_entry_fn(entry, &vm_spec)?; } - Ok(max_addressable_lcpu as u8) -} -/// Generates the emulated brand string. -/// TODO: Add non-Intel CPU support. -/// -/// For non-Intel CPUs, we'll just expose DEFAULT_BRAND_STRING. -/// -/// For Intel CPUs, the brand string we expose will be: -/// "Intel(R) Xeon(R) Processor @ {host freq}" -/// where {host freq} is the CPU frequency, as present in the -/// host brand string (e.g. 4.01GHz). -/// -/// This is safe because we know DEFAULT_BRAND_STRING to hold valid data -/// (allowed length and holding only valid ASCII chars). -fn get_brand_string() -> PartialError { - let mut bstr = BrandString::from_bytes_unchecked(DEFAULT_BRAND_STRING); - if let Ok(host_bstr) = BrandString::from_host_cpuid() { - if host_bstr.starts_with(b"Intel") { - if let Some(freq) = host_bstr.find_freq() { - let mut v3 = vec![]; - v3.extend_from_slice(b" @ "); - v3.extend_from_slice(freq); - if let Err(e) = bstr.push_bytes(&v3) { - return (bstr, Some(e)); - } - } - } - } - (bstr, None) -} - -#[cfg(test)] -mod tests { - use super::*; - use kvm::{Kvm, MAX_KVM_CPUID_ENTRIES}; - use kvm_bindings::kvm_cpuid_entry2; - - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - #[test] - fn test_get_max_addressable_lprocessors() { - assert_eq!(get_max_addressable_lprocessors(1).unwrap(), 1); - assert_eq!(get_max_addressable_lprocessors(2).unwrap(), 2); - assert_eq!(get_max_addressable_lprocessors(4).unwrap(), 4); - assert_eq!(get_max_addressable_lprocessors(6).unwrap(), 8); - assert!(get_max_addressable_lprocessors(u8::max_value()).is_err()); - } - - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - #[test] - fn test_cpuid() { - let kvm = Kvm::new().unwrap(); - let mut kvm_cpuid: CpuId = kvm.get_supported_cpuid(MAX_KVM_CPUID_ENTRIES).unwrap(); - filter_cpuid(0, 1, true, &mut kvm_cpuid).unwrap(); - - let entries = kvm_cpuid.mut_entries_slice(); - // TODO: This should be tested as part of the CI; only check that the function result is ok - // after moving this to the CI. - // Test the extended topology. See: - // https://www.scss.tcd.ie/~jones/CS4021/processor-identification-cpuid-instruction-note.pdf - let leaf11_index0 = kvm_cpuid_entry2 { - function: 11, - index: 0, - flags: 1, - eax: 0, - // no of hyperthreads/core - ebx: 1, - // ECX[15:8] = 2 (Core Level) - ecx: leaf_0xb::LEVEL_TYPE_CORE << leaf_0xb::ecx::LEVEL_TYPE_BITRANGE.lsb_index, - // EDX = APIC ID = 0 - edx: 0, - padding: [0, 0, 0], - }; - assert!(entries.contains(&leaf11_index0)); - let leaf11_index1 = kvm_cpuid_entry2 { - function: 11, - index: 1, - flags: 1, - eax: LEAFBH_INDEX1_APICID, - ebx: 0, - ecx: 1, // ECX[15:8] = 0 (Invalid Level) & ECX[7:0] = 1 (Level Number) - edx: 0, // EDX = APIC ID = 0 - padding: [0, 0, 0], - }; - assert!(entries.contains(&leaf11_index1)); - let leaf11_index2 = kvm_cpuid_entry2 { - function: 11, - index: 2, - flags: 1, - eax: 0, - ebx: 0, // nr of hyperthreads/core - ecx: 2, // ECX[15:8] = 0 (Invalid Level) & ECX[7:0] = 2 (Level Number) - edx: 0, // EDX = APIC ID = 0 - padding: [0, 0, 0], - }; - assert!(entries.contains(&leaf11_index2)); - } - - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - #[test] - fn test_filter_cpuid_1vcpu_ht_off() { - let mut kvm_cpuid = CpuId::new(11); - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[0].function = 0x1; - } - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[1].function = 0x4; - entries[1].eax = 0b10000; - } - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[2].function = 0x4; - entries[2].eax = 0b10_0000; - } - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[3].function = 0x4; - entries[3].eax = 0b100_0000; - } - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[4].function = 0x4; - entries[4].eax = 0b110_0000; - } - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[5].function = 0x6; - entries[5].eax = 1; - entries[5].ecx = 1; - } - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[6].function = 0xA; - } - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[7].function = 0xB; - entries[7].index = 0; - } - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[8].function = 0xB; - entries[8].index = 1; - } - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[9].function = 0xB; - entries[9].index = 2; - } - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[10].function = 0x8000_0003; - } - filter_cpuid(0, 1, false, &mut kvm_cpuid).unwrap(); - let max_addr_cpu = u32::from(get_max_addressable_lprocessors(1).unwrap()); - - let cpuid_f1 = kvm_cpuid_entry2 { - function: 1, - index: 0, - flags: 0, - eax: 0, - ebx: (EBX_CLFLUSH_CACHELINE << leaf_0x1::ebx::CLFLUSH_SIZE_BITRANGE.lsb_index) - | max_addr_cpu << leaf_0x1::ebx::CPU_COUNT_BITRANGE.lsb_index, - ecx: 1 << leaf_0x1::ecx::TSC_DEADLINE_TIMER_BITINDEX - | 1 << leaf_0x1::ecx::HYPERVISOR_BITINDEX, - edx: 0, - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[0], cpuid_f1); - } - let cpuid_f4 = kvm_cpuid_entry2 { - function: 0x4, - index: 0, - flags: 0, - eax: 0b10000 - & !(0b1111_1111_1111 - << leaf_0x4::eax::MAX_ADDR_IDS_SHARING_CACHE_BITRANGE.lsb_index), - ebx: 0, - ecx: 0, - edx: 0, - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[1], cpuid_f4); - } - let cpuid_f4 = kvm_cpuid_entry2 { - function: 0x4, - index: 0, - flags: 0, - eax: 0b10_0000 - & !(0b1111_1111_1111 - << leaf_0x4::eax::MAX_ADDR_IDS_SHARING_CACHE_BITRANGE.lsb_index), - ebx: 0, - ecx: 0, - edx: 0, - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[2], cpuid_f4); - } - let cpuid_f4 = kvm_cpuid_entry2 { - function: 0x4, - index: 0, - flags: 0, - eax: 0b100_0000 - & !(0b1111_1111_1111 - << leaf_0x4::eax::MAX_ADDR_IDS_SHARING_CACHE_BITRANGE.lsb_index), - ebx: 0, - ecx: 0, - edx: 0, - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[3], cpuid_f4); - } - let cpuid_f4 = kvm_cpuid_entry2 { - function: 0x4, - index: 0, - flags: 0, - eax: 0b110_0000 - & !(0b1111_1111_1111 - << leaf_0x4::eax::MAX_ADDR_IDS_SHARING_CACHE_BITRANGE.lsb_index), - ebx: 0, - ecx: 0, - edx: 0, - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[4], cpuid_f4); - } - let cpuid_f6 = kvm_cpuid_entry2 { - function: 0x6, - index: 0, - flags: 0, - eax: 1 & !(1 << leaf_0x6::eax::TURBO_BOOST_BITINDEX), - ebx: 0, - ecx: 1 & !(1 << leaf_0x6::ecx::EPB_BITINDEX), - edx: 0, - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[5], cpuid_f6); - } - let cpuid_fa = kvm_cpuid_entry2 { - function: 0xA, - index: 0, - flags: 0, - eax: 0, - ebx: 0, - ecx: 0, - edx: 0, - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[6], cpuid_fa); - } - let cpuid_fb = kvm_cpuid_entry2 { - function: 0xB, - index: 0, - flags: 0, - eax: 0, - ebx: 1, - ecx: leaf_0xb::LEVEL_TYPE_CORE << leaf_0xb::ecx::LEVEL_TYPE_BITRANGE.lsb_index, - edx: 0, - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[7], cpuid_fb); - } - let cpuid_fb = kvm_cpuid_entry2 { - function: 0xB, - index: 1, - flags: 0, - eax: LEAFBH_INDEX1_APICID, - ebx: 0, - ecx: 1 | (leaf_0xb::LEVEL_TYPE_INVALID << leaf_0xb::ecx::LEVEL_TYPE_BITRANGE.lsb_index), - edx: 0, - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[8], cpuid_fb); - } - let cpuid_fb = kvm_cpuid_entry2 { - function: 0xB, - index: 2, - flags: 0, - eax: 0, - ebx: 0, - ecx: 2, - edx: 0, - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[9], cpuid_fb); - } - let bstr = get_brand_string().0; - let cpuid_fother = kvm_cpuid_entry2 { - function: 0x8000_0003, - index: 0, - flags: 0, - eax: bstr.get_reg_for_leaf(0x8000_0003, BsReg::EAX), - ebx: bstr.get_reg_for_leaf(0x8000_0003, BsReg::EBX), - ecx: bstr.get_reg_for_leaf(0x8000_0003, BsReg::ECX), - edx: bstr.get_reg_for_leaf(0x8000_0003, BsReg::EDX), - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[10], cpuid_fother); - } - } - - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - #[test] - fn test_filter_cpuid_multiple_vcpu_ht_off() { - let mut kvm_cpuid = CpuId::new(11); - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[0].function = 0x1; - } - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[1].function = 0x4; - entries[1].eax = 0b10000; - } - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[2].function = 0x4; - entries[2].eax = 0b10_0000; - } - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[3].function = 0x4; - entries[3].eax = 0b100_0000; - } - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[4].function = 0x4; - entries[4].eax = 0b110_0000; - } - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[5].function = 0x6; - entries[5].eax = 1; - entries[5].ecx = 1; - } - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[6].function = 0xA; - } - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[7].function = 0xB; - entries[7].index = 0; - } - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[8].function = 0xB; - entries[8].index = 1; - } - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[9].function = 0xB; - entries[9].index = 2; - } - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[10].function = 0x8000_0003; - } - let cpu_count = 3; - filter_cpuid(0, cpu_count, false, &mut kvm_cpuid).unwrap(); - let max_addr_cpu = u32::from(get_max_addressable_lprocessors(cpu_count).unwrap()); - - let cpuid_f1 = kvm_cpuid_entry2 { - function: 1, - index: 0, - flags: 0, - eax: 0, - ebx: (EBX_CLFLUSH_CACHELINE << leaf_0x1::ebx::CLFLUSH_SIZE_BITRANGE.lsb_index) - | max_addr_cpu << leaf_0x1::ebx::CPU_COUNT_BITRANGE.lsb_index, - ecx: 1 << leaf_0x1::ecx::TSC_DEADLINE_TIMER_BITINDEX - | 1 << leaf_0x1::ecx::HYPERVISOR_BITINDEX, - edx: 1 << leaf_0x1::edx::HTT, - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[0], cpuid_f1); - } - let cpuid_f4 = kvm_cpuid_entry2 { - function: 0x4, - index: 0, - flags: 0, - eax: 0b10000 - & !(0b11_1111 << leaf_0x4::eax::MAX_ADDR_IDS_IN_PACKAGE_BITRANGE.lsb_index) - | u32::from(cpu_count - 1) - << leaf_0x4::eax::MAX_ADDR_IDS_IN_PACKAGE_BITRANGE.lsb_index, - ebx: 0, - ecx: 0, - edx: 0, - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[1], cpuid_f4); - } - let cpuid_f4 = kvm_cpuid_entry2 { - function: 0x4, - index: 0, - flags: 0, - eax: 0b10_0000 - & !(0b1111_1111_1111 - << leaf_0x4::eax::MAX_ADDR_IDS_SHARING_CACHE_BITRANGE.lsb_index) - & !(0b11_1111 << leaf_0x4::eax::MAX_ADDR_IDS_IN_PACKAGE_BITRANGE.lsb_index) - | u32::from(cpu_count - 1) - << leaf_0x4::eax::MAX_ADDR_IDS_IN_PACKAGE_BITRANGE.lsb_index, - ebx: 0, - ecx: 0, - edx: 0, - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[2], cpuid_f4); - } - let cpuid_f4 = kvm_cpuid_entry2 { - function: 0x4, - index: 0, - flags: 0, - eax: 0b100_0000 - & !(0b1111_1111_1111 - << leaf_0x4::eax::MAX_ADDR_IDS_SHARING_CACHE_BITRANGE.lsb_index) - & !(0b11_1111 << leaf_0x4::eax::MAX_ADDR_IDS_IN_PACKAGE_BITRANGE.lsb_index) - | u32::from(cpu_count - 1) - << leaf_0x4::eax::MAX_ADDR_IDS_IN_PACKAGE_BITRANGE.lsb_index, - ebx: 0, - ecx: 0, - edx: 0, - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[3], cpuid_f4); - } - let cpuid_f4 = kvm_cpuid_entry2 { - function: 0x4, - index: 0, - flags: 0, - eax: 0b110_0000 - & !(0b1111_1111_1111 - << leaf_0x4::eax::MAX_ADDR_IDS_SHARING_CACHE_BITRANGE.lsb_index) - | u32::from(cpu_count - 1) - << leaf_0x4::eax::MAX_ADDR_IDS_SHARING_CACHE_BITRANGE.lsb_index - & !(0b11_1111 << leaf_0x4::eax::MAX_ADDR_IDS_IN_PACKAGE_BITRANGE.lsb_index) - | u32::from(cpu_count - 1) - << leaf_0x4::eax::MAX_ADDR_IDS_IN_PACKAGE_BITRANGE.lsb_index, - ebx: 0, - ecx: 0, - edx: 0, - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[4], cpuid_f4); - } - let cpuid_f6 = kvm_cpuid_entry2 { - function: 0x6, - index: 0, - flags: 0, - eax: 1 & !(1 << leaf_0x6::eax::TURBO_BOOST_BITINDEX), - ebx: 0, - ecx: 1 & !(1 << leaf_0x6::ecx::EPB_BITINDEX), - edx: 0, - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[5], cpuid_f6); - } - let cpuid_fa = kvm_cpuid_entry2 { - function: 0xA, - index: 0, - flags: 0, - eax: 0, - ebx: 0, - ecx: 0, - edx: 0, - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[6], cpuid_fa); - } - let cpuid_fb = kvm_cpuid_entry2 { - function: 0xB, - index: 0, - flags: 0, - eax: 0, - ebx: 1, - ecx: leaf_0xb::LEVEL_TYPE_THREAD << leaf_0xb::ecx::LEVEL_TYPE_BITRANGE.lsb_index, - edx: 0, - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[7], cpuid_fb); - } - let cpuid_fb = kvm_cpuid_entry2 { - function: 0xB, - index: 1, - flags: 0, - eax: LEAFBH_INDEX1_APICID, - ebx: u32::from(cpu_count), - ecx: 1 | leaf_0xb::LEVEL_TYPE_CORE << leaf_0xb::ecx::LEVEL_TYPE_BITRANGE.lsb_index, - edx: 0, - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[8], cpuid_fb); - } - let cpuid_fb = kvm_cpuid_entry2 { - function: 0xB, - index: 2, - flags: 0, - eax: 0, - ebx: 0, - ecx: 2, - edx: 0, - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[9], cpuid_fb); - } - let bstr = get_brand_string().0; - let cpuid_fother = kvm_cpuid_entry2 { - function: 0x8000_0003, - index: 0, - flags: 0, - eax: bstr.get_reg_for_leaf(0x8000_0003, BsReg::EAX), - ebx: bstr.get_reg_for_leaf(0x8000_0003, BsReg::EBX), - ecx: bstr.get_reg_for_leaf(0x8000_0003, BsReg::ECX), - edx: bstr.get_reg_for_leaf(0x8000_0003, BsReg::EDX), - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[10], cpuid_fother); - } - } - - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - #[test] - fn test_filter_cpuid_1vcpu_ht_on() { - let mut kvm_cpuid = CpuId::new(11); - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[0].function = 0x1; - } - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[1].function = 0x4; - entries[1].eax = 0b10_000; - } - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[2].function = 0x4; - entries[2].eax = 0b10_0000; - } - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[3].function = 0x4; - entries[3].eax = 0b100_0000; - } - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[4].function = 0x4; - entries[4].eax = 0b110_0000; - } - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[5].function = 0x6; - entries[5].eax = 1; - entries[5].ecx = 1; - } - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[6].function = 0xA; - } - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[7].function = 0xB; - entries[7].index = 0; - } - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[8].function = 0xB; - entries[8].index = 1; - } - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[9].function = 0xB; - entries[9].index = 2; - } - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[10].function = 0x8000_0003; - } - filter_cpuid(0, 1, true, &mut kvm_cpuid).unwrap(); - let max_addr_cpu = u32::from(get_max_addressable_lprocessors(1).unwrap()); - - let cpuid_f1 = kvm_cpuid_entry2 { - function: 1, - index: 0, - flags: 0, - eax: 0, - ebx: (EBX_CLFLUSH_CACHELINE << leaf_0x1::ebx::CLFLUSH_SIZE_BITRANGE.lsb_index) - | max_addr_cpu << leaf_0x1::ebx::CPU_COUNT_BITRANGE.lsb_index, - ecx: 1 << leaf_0x1::ecx::TSC_DEADLINE_TIMER_BITINDEX - | 1 << leaf_0x1::ecx::HYPERVISOR_BITINDEX, - edx: 0, - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[0], cpuid_f1); - } - let cpuid_f4 = kvm_cpuid_entry2 { - function: 0x4, - index: 0, - flags: 0, - eax: 0b1_0000 - & !(0b1111_1111_1111 - << leaf_0x4::eax::MAX_ADDR_IDS_SHARING_CACHE_BITRANGE.lsb_index), - ebx: 0, - ecx: 0, - edx: 0, - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[1], cpuid_f4); - } - let cpuid_f4 = kvm_cpuid_entry2 { - function: 0x4, - index: 0, - flags: 0, - eax: 0b10_0000 - & !(0b1111_1111_1111 - << leaf_0x4::eax::MAX_ADDR_IDS_SHARING_CACHE_BITRANGE.lsb_index), - ebx: 0, - ecx: 0, - edx: 0, - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[2], cpuid_f4); - } - let cpuid_f4 = kvm_cpuid_entry2 { - function: 0x4, - index: 0, - flags: 0, - eax: 0b100_0000 - & !(0b1111_1111_1111 - << leaf_0x4::eax::MAX_ADDR_IDS_SHARING_CACHE_BITRANGE.lsb_index), - ebx: 0, - ecx: 0, - edx: 0, - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[3], cpuid_f4); - } - let cpuid_f4 = kvm_cpuid_entry2 { - function: 0x4, - index: 0, - flags: 0, - eax: 0b110_0000 - & !(0b1111_1111_1111 - << leaf_0x4::eax::MAX_ADDR_IDS_SHARING_CACHE_BITRANGE.lsb_index), - ebx: 0, - ecx: 0, - edx: 0, - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[4], cpuid_f4); - } - let cpuid_f6 = kvm_cpuid_entry2 { - function: 0x6, - index: 0, - flags: 0, - eax: 1 & !(1 << leaf_0x6::eax::TURBO_BOOST_BITINDEX), - ebx: 0, - ecx: 1 & !(1 << leaf_0x6::ecx::EPB_BITINDEX), - edx: 0, - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[5], cpuid_f6); - } - let cpuid_fa = kvm_cpuid_entry2 { - function: 0xA, - index: 0, - flags: 0, - eax: 0, - ebx: 0, - ecx: 0, - edx: 0, - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[6], cpuid_fa); - } - let cpuid_fb = kvm_cpuid_entry2 { - function: 0xB, - index: 0, - flags: 0, - eax: 0, - ebx: 1, - ecx: leaf_0xb::LEVEL_TYPE_CORE << leaf_0xb::ecx::LEVEL_TYPE_BITRANGE.lsb_index, - edx: 0, - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[7], cpuid_fb); - } - let cpuid_fb = kvm_cpuid_entry2 { - function: 0xB, - index: 1, - flags: 0, - eax: LEAFBH_INDEX1_APICID, - ebx: 0, - ecx: 1 | (leaf_0xb::LEVEL_TYPE_INVALID << leaf_0xb::ecx::LEVEL_TYPE_BITRANGE.lsb_index), - edx: 0, - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[8], cpuid_fb); - } - let cpuid_fb = kvm_cpuid_entry2 { - function: 0xB, - index: 2, - flags: 0, - eax: 0, - ebx: 0, - ecx: 2, - edx: 0, - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[9], cpuid_fb); - } - let bstr = get_brand_string().0; - let cpuid_fother = kvm_cpuid_entry2 { - function: 0x8000_0003, - index: 0, - flags: 0, - eax: bstr.get_reg_for_leaf(0x8000_0003, BsReg::EAX), - ebx: bstr.get_reg_for_leaf(0x8000_0003, BsReg::EBX), - ecx: bstr.get_reg_for_leaf(0x8000_0003, BsReg::ECX), - edx: bstr.get_reg_for_leaf(0x8000_0003, BsReg::EDX), - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[10], cpuid_fother); - } - } - - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - #[test] - fn test_filter_cpuid_multiple_vcpu_ht_on() { - let mut kvm_cpuid = CpuId::new(11); - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[0].function = 0x1; - } - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[1].function = 0x4; - entries[1].eax = 0b1_0000; - } - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[2].function = 0x4; - entries[2].eax = 0b10_0000; - } - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[3].function = 0x4; - entries[3].eax = 0b100_0000; - } - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[4].function = 0x4; - entries[4].eax = 0b110_0000; - } - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[5].function = 0x6; - entries[5].eax = 1; - entries[5].ecx = 1; - } - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[6].function = 0xA; - } - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[7].function = 0xB; - entries[7].index = 0; - } - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[8].function = 0xB; - entries[8].index = 1; - } - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[9].function = 0xB; - entries[9].index = 2; - } - { - let entries = kvm_cpuid.mut_entries_slice(); - entries[10].function = 0x8000_0003; - } - let cpu_count = 3; - filter_cpuid(0, cpu_count, true, &mut kvm_cpuid).unwrap(); - let max_addr_cpu = u32::from(get_max_addressable_lprocessors(cpu_count).unwrap()); - - let cpuid_f1 = kvm_cpuid_entry2 { - function: 1, - index: 0, - flags: 0, - eax: 0, - ebx: (EBX_CLFLUSH_CACHELINE << leaf_0x1::ebx::CLFLUSH_SIZE_BITRANGE.lsb_index) - | max_addr_cpu << leaf_0x1::ebx::CPU_COUNT_BITRANGE.lsb_index, - ecx: 1 << leaf_0x1::ecx::TSC_DEADLINE_TIMER_BITINDEX - | 1 << leaf_0x1::ecx::HYPERVISOR_BITINDEX, - edx: 1 << leaf_0x1::edx::HTT, - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[0], cpuid_f1); - } - let cpuid_f4 = kvm_cpuid_entry2 { - function: 0x4, - index: 0, - flags: 0, - eax: 0b1_0000 - & !(0b11_1111 << leaf_0x4::eax::MAX_ADDR_IDS_IN_PACKAGE_BITRANGE.lsb_index) - | u32::from(cpu_count - 1) - << leaf_0x4::eax::MAX_ADDR_IDS_IN_PACKAGE_BITRANGE.lsb_index, - ebx: 0, - ecx: 0, - edx: 0, - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[1], cpuid_f4); - } - let cpuid_f4 = kvm_cpuid_entry2 { - function: 0x4, - index: 0, - flags: 0, - eax: 0b10_0000 - & !(0b1111_1111_1111 - << leaf_0x4::eax::MAX_ADDR_IDS_SHARING_CACHE_BITRANGE.lsb_index) - | 1 << leaf_0x4::eax::MAX_ADDR_IDS_SHARING_CACHE_BITRANGE.lsb_index - & !(0b11_1111 << leaf_0x4::eax::MAX_ADDR_IDS_IN_PACKAGE_BITRANGE.lsb_index) - | u32::from(cpu_count - 1) - << leaf_0x4::eax::MAX_ADDR_IDS_IN_PACKAGE_BITRANGE.lsb_index, - ebx: 0, - ecx: 0, - edx: 0, - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[2], cpuid_f4); - } - let cpuid_f4 = kvm_cpuid_entry2 { - function: 0x4, - index: 0, - flags: 0, - eax: 0b100_0000 - & !(0b1111_1111_1111 - << leaf_0x4::eax::MAX_ADDR_IDS_SHARING_CACHE_BITRANGE.lsb_index) - | 1 << leaf_0x4::eax::MAX_ADDR_IDS_SHARING_CACHE_BITRANGE.lsb_index - & !(0b11_1111 << leaf_0x4::eax::MAX_ADDR_IDS_IN_PACKAGE_BITRANGE.lsb_index) - | u32::from(cpu_count - 1) - << leaf_0x4::eax::MAX_ADDR_IDS_IN_PACKAGE_BITRANGE.lsb_index, - ebx: 0, - ecx: 0, - edx: 0, - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[3], cpuid_f4); - } - let cpuid_f4 = kvm_cpuid_entry2 { - function: 0x4, - index: 0, - flags: 0, - eax: 0b110_0000 - & !(0b1111_1111_1111 - << leaf_0x4::eax::MAX_ADDR_IDS_SHARING_CACHE_BITRANGE.lsb_index) - | u32::from(cpu_count - 1) - << leaf_0x4::eax::MAX_ADDR_IDS_SHARING_CACHE_BITRANGE.lsb_index - & !(0b11_1111 << leaf_0x4::eax::MAX_ADDR_IDS_IN_PACKAGE_BITRANGE.lsb_index) - | u32::from(cpu_count - 1) - << leaf_0x4::eax::MAX_ADDR_IDS_IN_PACKAGE_BITRANGE.lsb_index, - ebx: 0, - ecx: 0, - edx: 0, - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[4], cpuid_f4); - } - let cpuid_f6 = kvm_cpuid_entry2 { - function: 0x6, - index: 0, - flags: 0, - eax: 1 & !(1 << leaf_0x6::eax::TURBO_BOOST_BITINDEX), - ebx: 0, - ecx: 1 & !(1 << leaf_0x6::ecx::EPB_BITINDEX), - edx: 0, - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[5], cpuid_f6); - } - let cpuid_fa = kvm_cpuid_entry2 { - function: 0xA, - index: 0, - flags: 0, - eax: 0, - ebx: 0, - ecx: 0, - edx: 0, - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[6], cpuid_fa); - } - let cpuid_fb = kvm_cpuid_entry2 { - function: 0xB, - index: 0, - flags: 0, - eax: 1, - ebx: 2, - ecx: leaf_0xb::LEVEL_TYPE_THREAD << leaf_0xb::ecx::LEVEL_TYPE_BITRANGE.lsb_index, - edx: 0, - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[7], cpuid_fb); - } - let cpuid_fb = kvm_cpuid_entry2 { - function: 0xB, - index: 1, - flags: 0, - eax: LEAFBH_INDEX1_APICID, - ebx: u32::from(cpu_count), - ecx: 1 | leaf_0xb::LEVEL_TYPE_CORE << leaf_0xb::ecx::LEVEL_TYPE_BITRANGE.lsb_index, - edx: 0, - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[8], cpuid_fb); - } - let cpuid_fb = kvm_cpuid_entry2 { - function: 0xB, - index: 2, - flags: 0, - eax: 0, - ebx: 0, - ecx: 2, - edx: 0, - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[9], cpuid_fb); - } - let bstr = get_brand_string().0; - let cpuid_fother = kvm_cpuid_entry2 { - function: 0x8000_0003, - index: 0, - flags: 0, - eax: bstr.get_reg_for_leaf(0x8000_0003, BsReg::EAX), - ebx: bstr.get_reg_for_leaf(0x8000_0003, BsReg::EBX), - ecx: bstr.get_reg_for_leaf(0x8000_0003, BsReg::ECX), - edx: bstr.get_reg_for_leaf(0x8000_0003, BsReg::EDX), - padding: [0, 0, 0], - }; - { - let entries = kvm_cpuid.mut_entries_slice(); - assert_eq!(entries[10], cpuid_fother); - } - } + Ok(()) } diff --git a/cpuid/src/c3_template.rs b/cpuid/src/template/c3.rs similarity index 100% rename from cpuid/src/c3_template.rs rename to cpuid/src/template/c3.rs diff --git a/cpuid/src/template/mod.rs b/cpuid/src/template/mod.rs new file mode 100644 index 00000000000..f494e62584a --- /dev/null +++ b/cpuid/src/template/mod.rs @@ -0,0 +1,7 @@ +// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/// Follows a C3 template in setting up the CPUID. +pub mod c3; +/// Follows a T2 template in setting up the CPUID. +pub mod t2; diff --git a/cpuid/src/t2_template.rs b/cpuid/src/template/t2.rs similarity index 100% rename from cpuid/src/t2_template.rs rename to cpuid/src/template/t2.rs diff --git a/cpuid/src/transformer/common.rs b/cpuid/src/transformer/common.rs new file mode 100644 index 00000000000..691b48f78aa --- /dev/null +++ b/cpuid/src/transformer/common.rs @@ -0,0 +1,112 @@ +// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +use super::*; +use bit_helper::BitHelper; + +// constants for setting the fields of kvm_cpuid2 structures +// CPUID bits in ebx, ecx, and edx. +const EBX_CLFLUSH_CACHELINE: u32 = 8; // Flush a cache line size. + +/// The maximum number of logical processors per package is computed as the closest power of 2 +/// higher or equal to the CPU count configured by the user. +fn get_max_cpus_per_package(cpu_count: u8) -> Result { + let mut max_cpus_per_package: u8 = 1; + while max_cpus_per_package < cpu_count { + max_cpus_per_package <<= 1; + + if max_cpus_per_package == 0 { + return Err(Error::VcpuCountOverflow); + } + } + + Ok(max_cpus_per_package) +} + +pub fn update_feature_info_entry( + entry: &mut kvm_cpuid_entry2, + vm_spec: &VmSpec, +) -> Result<(), Error> { + use cpu_leaf::leaf_0x1::*; + + let max_cpus_per_package = u32::from(common::get_max_cpus_per_package(vm_spec.cpu_count)?); + + // X86 hypervisor feature + entry + .ecx + .write_bit(ecx::TSC_DEADLINE_TIMER_BITINDEX, true) + .write_bit(ecx::HYPERVISOR_BITINDEX, true); + + entry + .ebx + .write_bits_in_range(&ebx::APICID_BITRANGE, u32::from(vm_spec.cpu_id)) + .write_bits_in_range(&ebx::CLFLUSH_SIZE_BITRANGE, EBX_CLFLUSH_CACHELINE) + .write_bits_in_range(&ebx::CPU_COUNT_BITRANGE, max_cpus_per_package); + + // A value of 1 for HTT indicates the value in CPUID.1.EBX[23:16] + // (the Maximum number of addressable IDs for logical processors in this package) + // is valid for the package + entry.edx.write_bit(edx::HTT, vm_spec.cpu_count > 1); + + Ok(()) +} + +pub fn update_brand_string_entry( + entry: &mut kvm_cpuid_entry2, + vm_spec: &VmSpec, +) -> Result<(), Error> { + let brand_string = vm_spec.brand_string(); + entry.eax = brand_string.get_reg_for_leaf(entry.function, BsReg::EAX); + entry.ebx = brand_string.get_reg_for_leaf(entry.function, BsReg::EBX); + entry.ecx = brand_string.get_reg_for_leaf(entry.function, BsReg::ECX); + entry.edx = brand_string.get_reg_for_leaf(entry.function, BsReg::EDX); + + Ok(()) +} + +#[cfg(test)] +mod test { + use super::*; + use kvm_bindings::kvm_cpuid_entry2; + use transformer::VmSpec; + + #[test] + fn test() { + assert_eq!(get_max_cpus_per_package(1).unwrap(), 1); + assert_eq!(get_max_cpus_per_package(2).unwrap(), 2); + assert_eq!(get_max_cpus_per_package(4).unwrap(), 4); + assert_eq!(get_max_cpus_per_package(6).unwrap(), 8); + + assert!(get_max_cpus_per_package(u8::max_value()).is_err()); + } + + fn check_update_feature_info_entry(cpu_count: u8, expected_htt: bool) { + use cpu_leaf::leaf_0x1::*; + + let vm_spec = VmSpec::new(0, cpu_count, false); + let mut entry = &mut kvm_cpuid_entry2 { + function: 0x0, + index: 0, + flags: 0, + eax: 0, + ebx: 0, + ecx: 0, + edx: 0, + padding: [0, 0, 0], + }; + + assert!(update_feature_info_entry(&mut entry, &vm_spec).is_ok()); + + assert!(entry.edx.read_bit(edx::HTT) == expected_htt) + } + + #[test] + fn test_1vcpu() { + check_update_feature_info_entry(1, false); + } + + #[test] + fn test_2vcpu() { + check_update_feature_info_entry(2, true); + } +} diff --git a/cpuid/src/transformer/intel.rs b/cpuid/src/transformer/intel.rs new file mode 100644 index 00000000000..c5bf377327b --- /dev/null +++ b/cpuid/src/transformer/intel.rs @@ -0,0 +1,337 @@ +// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +use super::*; +use bit_helper::BitHelper; + +// The APIC ID shift in leaf 0xBh specifies the number of bits to shit the x2APIC ID to get a +// unique topology of the next level. This allows 64 logical processors/package. +const LEAFBH_INDEX1_APICID: u32 = 6; + +fn update_deterministic_cache_entry( + entry: &mut kvm_cpuid_entry2, + vm_spec: &VmSpec, +) -> Result<(), Error> { + use cpu_leaf::leaf_0x4::*; + + match entry.eax.read_bits_in_range(&eax::CACHE_LEVEL_BITRANGE) { + // L1 & L2 Cache + 1 | 2 => { + // The L1 & L2 cache is shared by at most 2 hyperthreads + entry.eax.write_bits_in_range( + &eax::MAX_CPUS_PER_CORE_BITRANGE, + (vm_spec.cpu_count > 1 && vm_spec.ht_enabled) as u32, + ); + } + // L3 Cache + 3 => { + // The L3 cache is shared among all the logical threads + entry.eax.write_bits_in_range( + &eax::MAX_CPUS_PER_CORE_BITRANGE, + u32::from(vm_spec.cpu_count - 1), + ); + } + _ => (), + } + + // Put all the cores in the same socket + entry.eax.write_bits_in_range( + &eax::MAX_CORES_PER_PACKAGE_BITRANGE, + u32::from(vm_spec.cpu_count - 1), + ); + + Ok(()) +} + +fn update_power_management_entry( + entry: &mut kvm_cpuid_entry2, + _vm_spec: &VmSpec, +) -> Result<(), Error> { + use cpu_leaf::leaf_0x6::*; + + entry.eax.write_bit(eax::TURBO_BOOST_BITINDEX, false); + // Clear X86 EPB feature. No frequency selection in the hypervisor. + entry.ecx.write_bit(ecx::EPB_BITINDEX, false); + + Ok(()) +} + +fn update_perf_mon_entry(entry: &mut kvm_cpuid_entry2, _vm_spec: &VmSpec) -> Result<(), Error> { + // Architectural Performance Monitor Leaf + // Disable PMU + entry.eax = 0; + entry.ebx = 0; + entry.ecx = 0; + entry.edx = 0; + + Ok(()) +} + +fn update_extended_cache_topology_entry( + entry: &mut kvm_cpuid_entry2, + vm_spec: &VmSpec, +) -> Result<(), Error> { + use cpu_leaf::leaf_0xb::*; + + //reset eax, ebx, ecx + entry.eax = 0 as u32; + entry.ebx = 0 as u32; + entry.ecx = 0 as u32; + // EDX bits 31..0 contain x2APIC ID of current logical processor + // x2APIC increases the size of the APIC ID from 8 bits to 32 bits + entry.edx = u32::from(vm_spec.cpu_id); + match entry.index { + // Thread Level Topology; index = 0 + 0 => { + // To get the next level APIC ID, shift right with at most 1 because we have + // maximum 2 hyperthreads per core that can be represented by 1 bit. + entry.eax.write_bits_in_range( + &eax::APICID_BITRANGE, + (vm_spec.cpu_count > 1 && vm_spec.ht_enabled) as u32, + ); + // When cpu_count == 1 or HT is disabled, there is 1 logical core at this level + // Otherwise there are 2 + entry.ebx.write_bits_in_range( + &ebx::NUM_LOGICAL_PROCESSORS_BITRANGE, + 1 + (vm_spec.cpu_count > 1 && vm_spec.ht_enabled) as u32, + ); + + entry.ecx.write_bits_in_range(&ecx::LEVEL_TYPE_BITRANGE, { + if vm_spec.cpu_count == 1 { + // There are no hyperthreads for 1 VCPU, set the level type = 2 (Core) + LEVEL_TYPE_CORE + } else { + LEVEL_TYPE_THREAD + } + }); + } + // Core Level Processor Topology; index = 1 + 1 => { + entry + .eax + .write_bits_in_range(&eax::APICID_BITRANGE, LEAFBH_INDEX1_APICID); + entry + .ecx + .write_bits_in_range(&ecx::LEVEL_NUMBER_BITRANGE, entry.index as u32); + if vm_spec.cpu_count == 1 { + // For 1 vCPU, this level is invalid + entry + .ecx + .write_bits_in_range(&ecx::LEVEL_TYPE_BITRANGE, LEVEL_TYPE_INVALID); + } else { + entry.ebx.write_bits_in_range( + &ebx::NUM_LOGICAL_PROCESSORS_BITRANGE, + u32::from(vm_spec.cpu_count), + ); + entry + .ecx + .write_bits_in_range(&ecx::LEVEL_TYPE_BITRANGE, LEVEL_TYPE_CORE); + } + } + // Core Level Processor Topology; index >=2 + // No other levels available; This should already be set to correctly, + // and it is added here as a "re-enforcement" in case we run on + // different hardware + level => { + entry.ecx = level; + } + } + + Ok(()) +} + +pub fn transform_entry(entry: &mut kvm_cpuid_entry2, vm_spec: &VmSpec) -> Result<(), Error> { + let maybe_transformer_fn: Option = match entry.function { + 0x1 => Some(common::update_feature_info_entry), + 0x4 => Some(intel::update_deterministic_cache_entry), + 0x6 => Some(intel::update_power_management_entry), + 0xA => Some(intel::update_perf_mon_entry), + 0xB => Some(intel::update_extended_cache_topology_entry), + 0x8000_0002..=0x8000_0004 => Some(common::update_brand_string_entry), + _ => None, + }; + + if let Some(transformer_fn) = maybe_transformer_fn { + return transformer_fn(entry, vm_spec); + } + + Ok(()) +} + +#[cfg(test)] +mod test { + use super::*; + use cpu_leaf::leaf_0xb::LEVEL_TYPE_CORE; + use cpu_leaf::leaf_0xb::LEVEL_TYPE_INVALID; + use cpu_leaf::leaf_0xb::LEVEL_TYPE_THREAD; + use kvm_bindings::kvm_cpuid_entry2; + use transformer::VmSpec; + + fn check_update_deterministic_cache_entry( + cpu_count: u8, + ht_enabled: bool, + cache_level: u32, + expected_max_cpus_per_core: u32, + expected_max_cores_per_package: u32, + ) { + use cpu_leaf::leaf_0x4::*; + + let vm_spec = VmSpec::new(0, cpu_count, ht_enabled); + let mut entry = &mut kvm_cpuid_entry2 { + function: 0x0, + index: 0, + flags: 0, + eax: *(0 as u32).write_bits_in_range(&eax::CACHE_LEVEL_BITRANGE, cache_level), + ebx: 0, + ecx: 0, + edx: 0, + padding: [0, 0, 0], + }; + + assert!(update_deterministic_cache_entry(&mut entry, &vm_spec).is_ok()); + + assert!( + entry + .eax + .read_bits_in_range(&eax::MAX_CPUS_PER_CORE_BITRANGE) + == expected_max_cpus_per_core + ); + assert!( + entry + .eax + .read_bits_in_range(&eax::MAX_CORES_PER_PACKAGE_BITRANGE) + == expected_max_cores_per_package + ); + } + + fn check_update_extended_cache_topology_entry( + cpu_count: u8, + ht_enabled: bool, + index: u32, + expected_apicid: u32, + expected_num_logical_processors: u32, + expected_level_type: u32, + ) { + use cpu_leaf::leaf_0xb::*; + + let vm_spec = VmSpec::new(0, cpu_count, ht_enabled); + let mut entry = &mut kvm_cpuid_entry2 { + function: 0x0, + index, + flags: 0, + eax: 0, + ebx: 0, + ecx: 0, + edx: 0, + padding: [0, 0, 0], + }; + + assert!(update_extended_cache_topology_entry(&mut entry, &vm_spec).is_ok()); + + assert!(entry.eax.read_bits_in_range(&eax::APICID_BITRANGE) == expected_apicid); + assert!( + entry + .ebx + .read_bits_in_range(&ebx::NUM_LOGICAL_PROCESSORS_BITRANGE) + == expected_num_logical_processors + ); + assert!(entry.ecx.read_bits_in_range(&ecx::LEVEL_TYPE_BITRANGE) == expected_level_type); + assert!(entry.ecx.read_bits_in_range(&ecx::LEVEL_NUMBER_BITRANGE) == index); + } + + #[test] + fn test_1vcpu_ht_off() { + // test update_deterministic_cache_entry + // test L1 + check_update_deterministic_cache_entry(1, false, 1, 0, 0); + // test L2 + check_update_deterministic_cache_entry(1, false, 2, 0, 0); + // test L3 + check_update_deterministic_cache_entry(1, false, 3, 0, 0); + + // test update_extended_cache_topology_entry + // index 0 + check_update_extended_cache_topology_entry(1, false, 0, 0, 1, LEVEL_TYPE_CORE); + // index 1 + check_update_extended_cache_topology_entry( + 1, + false, + 1, + LEAFBH_INDEX1_APICID, + 0, + LEVEL_TYPE_INVALID, + ); + } + + #[test] + fn test_1vcpu_ht_on() { + // test update_deterministic_cache_entry + // test L1 + check_update_deterministic_cache_entry(1, true, 1, 0, 0); + // test L2 + check_update_deterministic_cache_entry(1, true, 2, 0, 0); + // test L3 + check_update_deterministic_cache_entry(1, true, 3, 0, 0); + + // test update_extended_cache_topology_entry + // index 0 + check_update_extended_cache_topology_entry(1, true, 0, 0, 1, LEVEL_TYPE_CORE); + // index 1 + check_update_extended_cache_topology_entry( + 1, + true, + 1, + LEAFBH_INDEX1_APICID, + 0, + LEVEL_TYPE_INVALID, + ); + } + + #[test] + fn test_2vcpu_ht_off() { + // test update_deterministic_cache_entry + // test L1 + check_update_deterministic_cache_entry(2, false, 1, 0, 1); + // test L2 + check_update_deterministic_cache_entry(2, false, 2, 0, 1); + // test L3 + check_update_deterministic_cache_entry(2, false, 3, 1, 1); + + // test update_extended_cache_topology_entry + // index 0 + check_update_extended_cache_topology_entry(2, false, 0, 0, 1, LEVEL_TYPE_THREAD); + // index 1 + check_update_extended_cache_topology_entry( + 2, + false, + 1, + LEAFBH_INDEX1_APICID, + 2, + LEVEL_TYPE_CORE, + ); + } + + #[test] + fn test_2vcpu_ht_on() { + // test update_deterministic_cache_entry + // test L1 + check_update_deterministic_cache_entry(2, true, 1, 1, 1); + // test L2 + check_update_deterministic_cache_entry(2, true, 2, 1, 1); + // test L3 + check_update_deterministic_cache_entry(2, true, 3, 1, 1); + + // test update_extended_cache_topology_entry + // index 0 + check_update_extended_cache_topology_entry(2, true, 0, 1, 2, LEVEL_TYPE_THREAD); + // index 1 + check_update_extended_cache_topology_entry( + 2, + true, + 1, + LEAFBH_INDEX1_APICID, + 2, + LEVEL_TYPE_CORE, + ); + } +} diff --git a/cpuid/src/transformer/mod.rs b/cpuid/src/transformer/mod.rs new file mode 100644 index 00000000000..fe68148f953 --- /dev/null +++ b/cpuid/src/transformer/mod.rs @@ -0,0 +1,70 @@ +// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +pub mod common; +pub mod intel; + +use brand_string::BrandString; +use brand_string::Reg as BsReg; +pub use kvm_bindings::kvm_cpuid_entry2; + +const DEFAULT_BRAND_STRING: &[u8] = b"Intel(R) Xeon(R) Processor"; + +/// Generates the emulated brand string. +/// TODO: Add non-Intel CPU support. +/// +/// For non-Intel CPUs, we'll just expose DEFAULT_BRAND_STRING. +/// +/// For Intel CPUs, the brand string we expose will be: +/// "Intel(R) Xeon(R) Processor @ {host freq}" +/// where {host freq} is the CPU frequency, as present in the +/// host brand string (e.g. 4.01GHz). +/// +/// This is safe because we know DEFAULT_BRAND_STRING to hold valid data +/// (allowed length and holding only valid ASCII chars). +pub fn build_brand_string() -> BrandString { + let mut bstr = BrandString::from_bytes_unchecked(DEFAULT_BRAND_STRING); + if let Ok(host_bstr) = BrandString::from_host_cpuid() { + if host_bstr.starts_with(b"Intel") { + if let Some(freq) = host_bstr.find_freq() { + bstr.push_bytes(b" @ ").unwrap(); + bstr.push_bytes(freq) + .expect("Unexpected frequency information in host CPUID"); + } + } + } + + bstr +} + +pub struct VmSpec { + pub cpu_id: u8, + pub cpu_count: u8, + pub ht_enabled: bool, + brand_string: BrandString, +} + +impl VmSpec { + pub fn new(cpu_id: u8, cpu_count: u8, ht_enabled: bool) -> VmSpec { + VmSpec { + cpu_id, + cpu_count, + ht_enabled, + brand_string: build_brand_string(), + } + } + + pub fn brand_string(&self) -> &BrandString { + &self.brand_string + } +} + +/// Errors associated with processing the CPUID leaves. +#[derive(Debug, Clone)] +pub enum Error { + /// The maximum number of addressable logical CPUs cannot be stored in an `u8`. + VcpuCountOverflow, +} + +pub type EntryTransformerFn = + fn(entry: &mut kvm_cpuid_entry2, vm_spec: &VmSpec) -> Result<(), Error>; diff --git a/tests/integration_tests/build/test_coverage.py b/tests/integration_tests/build/test_coverage.py index 20feb593d06..b4408ea64f9 100644 --- a/tests/integration_tests/build/test_coverage.py +++ b/tests/integration_tests/build/test_coverage.py @@ -18,8 +18,7 @@ import host_tools.cargo_build as host # pylint: disable=import-error - -COVERAGE_TARGET_PCT = 82.5 +COVERAGE_TARGET_PCT = 82.7 COVERAGE_MAX_DELTA = 0.01 CARGO_KCOV_REL_PATH = os.path.join(host.CARGO_BUILD_REL_PATH, 'kcov') diff --git a/vmm/src/vstate.rs b/vmm/src/vstate.rs index 1f58f04a694..b3877a08c69 100644 --- a/vmm/src/vstate.rs +++ b/vmm/src/vstate.rs @@ -17,10 +17,9 @@ use kvm_bindings::{kvm_pit_config, kvm_userspace_memory_region, KVM_PIT_SPEAKER_ use super::KvmContext; use arch; #[cfg(target_arch = "x86_64")] -use cpuid::{c3_template, filter_cpuid, t2_template}; +use cpuid::{c3, filter_cpuid, t2}; use kvm::*; use logger::{LogOption, LOGGER}; -use logger::{Metric, METRICS}; use memory_model::{GuestAddress, GuestMemory, GuestMemoryError}; use sys_util::EventFd; #[cfg(target_arch = "x86_64")] @@ -228,29 +227,20 @@ impl Vcpu { vm: &Vm, ) -> Result<()> { // the MachineConfiguration has defaults for ht_enabled and vcpu_count hence it is safe to unwrap - if let Err(e) = filter_cpuid( + filter_cpuid( self.id, machine_config .vcpu_count .ok_or(Error::VcpuCountNotInitialized)?, machine_config.ht_enabled.ok_or(Error::HTNotInitialized)?, &mut self.cpuid, - ) { - // For the moment, we do not have a showstopper error returned by the `filter_cpuid`. - METRICS.vcpu.fitler_cpuid.inc(); - error!( - "Failure in configuring CPUID for vcpu {:?}: {:?}", - self.id, e - ); - } + ) + .map_err(Error::CpuId)?; + if let Some(template) = machine_config.cpu_template { match template { - CpuFeaturesTemplate::T2 => { - t2_template::set_cpuid_entries(self.cpuid.mut_entries_slice()) - } - CpuFeaturesTemplate::C3 => { - c3_template::set_cpuid_entries(self.cpuid.mut_entries_slice()) - } + CpuFeaturesTemplate::T2 => t2::set_cpuid_entries(self.cpuid.mut_entries_slice()), + CpuFeaturesTemplate::C3 => c3::set_cpuid_entries(self.cpuid.mut_entries_slice()), } }