From 7c16e0fbf90bc31240941bc1ea83418541514e6d Mon Sep 17 00:00:00 2001 From: Jonathan Woollett-Light Date: Tue, 17 Jan 2023 13:36:36 +0000 Subject: [PATCH] feat: Updated CPUID Reworked the CPUID crate to support extensive CPU templates, simplifying the existing flow and adding accessor functions and types. Signed-off-by: Jonathan Woollett-Light --- Cargo.lock | 128 +- Cargo.toml | 2 +- src/arch/src/x86_64/msr.rs | 2 +- src/bit-fields/src/bit_range.rs | 3 +- src/cpuid-templates/Cargo.toml | 9 + src/cpuid-templates/src/lib.rs | 1780 +++++++++++++++++ src/cpuid/Cargo.toml | 7 +- src/cpuid/build.rs | 14 + src/cpuid/src/amd/indexing.rs | 53 + src/cpuid/src/amd/leaves.rs | 34 + src/cpuid/src/amd/mod.rs | 67 + src/cpuid/src/amd/normalize.rs | 320 +++ src/cpuid/src/amd/registers.rs | 449 +++++ src/cpuid/src/bit_helper.rs | 593 ------ src/cpuid/src/brand_string.rs | 490 ----- src/cpuid/src/common.rs | 376 ++-- src/cpuid/src/cpu_leaf.rs | 352 ---- src/cpuid/src/cpuid_ffi.rs | 610 ++++++ src/cpuid/src/indexing.rs | 109 + src/cpuid/src/intel/indexing.rs | 459 +++++ src/cpuid/src/intel/leaves.rs | 285 +++ src/cpuid/src/intel/mod.rs | 169 ++ src/cpuid/src/intel/normalize.rs | 306 +++ src/cpuid/src/intel/registers.rs | 1715 ++++++++++++++++ src/cpuid/src/leaves.rs | 86 + src/cpuid/src/lib.rs | 652 +++++- src/cpuid/src/normalize.rs | 151 ++ src/cpuid/src/registers.rs | 258 +++ src/cpuid/src/template/intel/c3.rs | 185 -- src/cpuid/src/template/intel/mod.rs | 132 -- src/cpuid/src/template/intel/t2.rs | 186 -- src/cpuid/src/template/intel/t2s.rs | 51 - src/cpuid/src/template/mod.rs | 24 - src/cpuid/src/transformer/amd.rs | 382 ---- src/cpuid/src/transformer/common.rs | 307 --- src/cpuid/src/transformer/intel.rs | 296 --- src/cpuid/src/transformer/mod.rs | 168 -- src/logger/src/metrics.rs | 2 - src/vmm/Cargo.toml | 1 + src/vmm/src/builder.rs | 42 +- src/vmm/src/persist.rs | 15 +- src/vmm/src/vmm_config/machine_config.rs | 6 +- src/vmm/src/vstate/vcpu/x86_64.rs | 168 +- src/vmm/tests/integration_tests.rs | 2 +- tests/framework/dependencies.txt | 27 +- .../integration_tests/build/test_coverage.py | 4 +- .../integration_tests/functional/test_api.py | 5 +- .../functional/test_cpu_features.py | 50 +- 48 files changed, 7879 insertions(+), 3653 deletions(-) create mode 100644 src/cpuid-templates/Cargo.toml create mode 100644 src/cpuid-templates/src/lib.rs create mode 100644 src/cpuid/build.rs create mode 100644 src/cpuid/src/amd/indexing.rs create mode 100644 src/cpuid/src/amd/leaves.rs create mode 100644 src/cpuid/src/amd/mod.rs create mode 100644 src/cpuid/src/amd/normalize.rs create mode 100644 src/cpuid/src/amd/registers.rs delete mode 100644 src/cpuid/src/bit_helper.rs delete mode 100644 src/cpuid/src/brand_string.rs delete mode 100644 src/cpuid/src/cpu_leaf.rs create mode 100644 src/cpuid/src/cpuid_ffi.rs create mode 100644 src/cpuid/src/indexing.rs create mode 100644 src/cpuid/src/intel/indexing.rs create mode 100644 src/cpuid/src/intel/leaves.rs create mode 100644 src/cpuid/src/intel/mod.rs create mode 100644 src/cpuid/src/intel/normalize.rs create mode 100644 src/cpuid/src/intel/registers.rs create mode 100644 src/cpuid/src/leaves.rs create mode 100644 src/cpuid/src/normalize.rs create mode 100644 src/cpuid/src/registers.rs delete mode 100644 src/cpuid/src/template/intel/c3.rs delete mode 100644 src/cpuid/src/template/intel/mod.rs delete mode 100644 src/cpuid/src/template/intel/t2.rs delete mode 100644 src/cpuid/src/template/intel/t2s.rs delete mode 100644 src/cpuid/src/template/mod.rs delete mode 100644 src/cpuid/src/transformer/amd.rs delete mode 100644 src/cpuid/src/transformer/common.rs delete mode 100644 src/cpuid/src/transformer/intel.rs delete mode 100644 src/cpuid/src/transformer/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 965d3f91f54..dec4df791a0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -314,15 +314,23 @@ dependencies = [ name = "cpuid" version = "0.1.0" dependencies = [ - "arch", "arch_gen", - "derive_more", + "bit-fields", "kvm-bindings", "kvm-ioctls", + "log", + "serde", "thiserror", "utils", ] +[[package]] +name = "cpuid-templates" +version = "0.1.0" +dependencies = [ + "cpuid", +] + [[package]] name = "crc64" version = "1.0.0" @@ -617,9 +625,13 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "0.6.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9448015e586b611e5d322f6703812bbca2f1e709d5773ecd38ddb4e3bb649504" +checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c" +dependencies = [ + "libc", + "windows-sys", +] [[package]] name = "io_uring" @@ -700,9 +712,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.137" +version = "0.2.138" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" +checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" [[package]] name = "libloading" @@ -725,9 +737,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.0.46" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d2456c373231a208ad294c33dc5bff30051eafd954cd4caae83a712b12854d" +checksum = "8f9f08d8963a6c613f4b1a78f4f4a4dbfadf8e6545b2d72861731e4858b8b47f" [[package]] name = "log" @@ -816,9 +828,9 @@ version = "0.1.0" [[package]] name = "nix" -version = "0.23.1" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6" +checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c" dependencies = [ "bitflags", "cc", @@ -934,9 +946,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.43" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" +checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" dependencies = [ "unicode-ident", ] @@ -1080,16 +1092,16 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" -version = "0.34.8" +version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2079c267b8394eb529872c3cf92e181c378b41fea36e68130357b52493701d2e" +checksum = "a3807b5d10909833d3e9acd1eb5fb988f79376ff10fce42937de71a449c4c588" dependencies = [ "bitflags", "errno", "io-lifetimes", "libc", "linux-raw-sys", - "winapi", + "windows-sys", ] [[package]] @@ -1127,18 +1139,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.144" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860" +checksum = "256b9932320c590e707b94576e3cc1f7c9024d0ee6612dfbcf1cb106cbe8e055" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.144" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00" +checksum = "b4eae9b04cbffdfd550eb462ed33bc6a1b68c935127d008b27444d08380f94e4" dependencies = [ "proc-macro2", "quote", @@ -1147,9 +1159,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.85" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" +checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" dependencies = [ "itoa", "ryu", @@ -1181,9 +1193,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.99" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" +checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908" dependencies = [ "proc-macro2", "quote", @@ -1218,9 +1230,9 @@ dependencies = [ [[package]] name = "timerfd" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29f85a7c965b8e7136952f59f2a359694c78f105b2d2ff99cf6c2c404bf7e33f" +checksum = "0664936efa25f2bbe03ca25b62c50f5f492abec07e59d6dcf45131014b33483f" dependencies = [ "rustix", ] @@ -1246,15 +1258,15 @@ dependencies = [ [[package]] name = "typenum" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] name = "unicode-ident" -version = "1.0.3" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" [[package]] name = "unicode-segmentation" @@ -1396,6 +1408,7 @@ version = "0.1.0" dependencies = [ "arch", "cpuid", + "cpuid-templates", "criterion", "derive_more", "devices", @@ -1544,3 +1557,60 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" diff --git a/Cargo.toml b/Cargo.toml index 2d61a1114f5..8c3056c9046 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["src/firecracker", "src/jailer", "src/seccompiler", "src/rebase-snap", "src/bit-fields"] +members = ["src/firecracker", "src/jailer", "src/seccompiler", "src/rebase-snap"] default-members = ["src/firecracker"] [profile.dev] diff --git a/src/arch/src/x86_64/msr.rs b/src/arch/src/x86_64/msr.rs index 4f08c9ab915..87c1c9ff8f4 100644 --- a/src/arch/src/x86_64/msr.rs +++ b/src/arch/src/x86_64/msr.rs @@ -282,7 +282,7 @@ pub fn create_boot_msr_entries() -> Vec { } /// Error type for [`set_msrs`]. -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, Eq, PartialEq)] pub enum SetMSRsError { /// Failed to create [`vmm_sys_util::fam::FamStructWrapper`] for MSRs. #[error("Could not create `vmm_sys_util::fam::FamStructWrapper` for MSRs")] diff --git a/src/bit-fields/src/bit_range.rs b/src/bit-fields/src/bit_range.rs index d957415b0b6..a40bae26e56 100644 --- a/src/bit-fields/src/bit_range.rs +++ b/src/bit-fields/src/bit_range.rs @@ -496,8 +496,7 @@ macro_rules! shift { } else if $x == $max { $ty } else { - // TODO Use `unreachable!()` here when panicking in const context is stabilized. - 0 + unreachable!() } }}; } diff --git a/src/cpuid-templates/Cargo.toml b/src/cpuid-templates/Cargo.toml new file mode 100644 index 00000000000..f55c35e9b10 --- /dev/null +++ b/src/cpuid-templates/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "cpuid-templates" +version = "0.1.0" +authors = ["Amazon Firecracker team "] +edition = "2021" +license = "Apache-2.0" + +[dependencies] +cpuid = { path="../cpuid" } diff --git a/src/cpuid-templates/src/lib.rs b/src/cpuid-templates/src/lib.rs new file mode 100644 index 00000000000..e621f4323bd --- /dev/null +++ b/src/cpuid-templates/src/lib.rs @@ -0,0 +1,1780 @@ +// Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +use cpuid::{Cpuid, CpuidEntry, CpuidKey, CpuidRegisters, IntelCpuid, KvmCpuidFlags}; + +pub fn c3() -> Cpuid { + Cpuid::Intel(IntelCpuid({ + let mut map = std::collections::BTreeMap::new(); + map.insert( + CpuidKey { + leaf: 0u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 13u32, + ebx: 1970169159u32, + ecx: 1818588270u32, + edx: 1231384169u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 1u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 198372u32, + ebx: 133120u32, + ecx: 4156170755u32, + edx: 395049983u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 2u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 1979933441u32, + ebx: 15775231u32, + ecx: 0u32, + edx: 12779520u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 3u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 0u32, + ebx: 0u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 4u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 67109153u32, + ebx: 29360191u32, + ecx: 63u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 4u32, + subleaf: 1u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 67109154u32, + ebx: 29360191u32, + ecx: 63u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 4u32, + subleaf: 2u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 67109187u32, + ebx: 62914623u32, + ecx: 1023u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 4u32, + subleaf: 3u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 67125603u32, + ebx: 41943103u32, + ecx: 53247u32, + edx: 5u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 4u32, + subleaf: 4u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 67108864u32, + ebx: 0u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 5u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 0u32, + ebx: 0u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 6u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 4u32, + ebx: 0u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 7u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 0u32, + ebx: 1049219u32, + ecx: 0u32, + edx: 2885682176u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 8u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 0u32, + ebx: 0u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 9u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 0u32, + ebx: 0u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 10u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 0u32, + ebx: 0u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 11u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 0u32, + ebx: 1u32, + ecx: 256u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 11u32, + subleaf: 1u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 7u32, + ebx: 2u32, + ecx: 513u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 11u32, + subleaf: 2u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 0u32, + ebx: 0u32, + ecx: 2u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 12u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 0u32, + ebx: 0u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 13u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 7u32, + ebx: 2696u32, + ecx: 2696u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 13u32, + subleaf: 1u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 1u32, + ebx: 2568u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 13u32, + subleaf: 2u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 256u32, + ebx: 576u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 13u32, + subleaf: 3u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 64u32, + ebx: 960u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 13u32, + subleaf: 4u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 64u32, + ebx: 1024u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 13u32, + subleaf: 5u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 64u32, + ebx: 1088u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 13u32, + subleaf: 6u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 512u32, + ebx: 1152u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 13u32, + subleaf: 7u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 1024u32, + ebx: 1664u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 13u32, + subleaf: 9u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 8u32, + ebx: 2688u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 1073741824u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 1073741825u32, + ebx: 1263359563u32, + ecx: 1447775574u32, + edx: 77u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 1073741825u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 16809723u32, + ebx: 0u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 2147483648u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 2147483656u32, + ebx: 0u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 2147483649u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 0u32, + ebx: 0u32, + ecx: 1u32, + edx: 672139264u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 2147483650u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 1702129225u32, + ebx: 693250156u32, + ecx: 1868912672u32, + edx: 693250158u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 2147483651u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 1869762592u32, + ebx: 1936942435u32, + ecx: 1075868271u32, + edx: 808334112u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 2147483652u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 2051557168u32, + ebx: 0u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 2147483653u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 0u32, + ebx: 0u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 2147483654u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 0u32, + ebx: 0u32, + ecx: 16801856u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 2147483655u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 0u32, + ebx: 0u32, + ecx: 0u32, + edx: 256u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 2147483656u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 12334u32, + ebx: 16830464u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map + })) +} + +pub fn t2() -> Cpuid { + Cpuid::Intel(IntelCpuid({ + let mut map = std::collections::BTreeMap::new(); + map.insert( + CpuidKey { + leaf: 0u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 13u32, + ebx: 1970169159u32, + ecx: 1818588270u32, + edx: 1231384169u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 1u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 198386u32, + ebx: 133120u32, + ecx: 4160369155u32, + edx: 395049983u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 2u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 1979933441u32, + ebx: 15775231u32, + ecx: 0u32, + edx: 12779520u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 3u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 0u32, + ebx: 0u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 4u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 67109153u32, + ebx: 29360191u32, + ecx: 63u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 4u32, + subleaf: 1u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 67109154u32, + ebx: 29360191u32, + ecx: 63u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 4u32, + subleaf: 2u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 67109187u32, + ebx: 62914623u32, + ecx: 1023u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 4u32, + subleaf: 3u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 67125603u32, + ebx: 41943103u32, + ecx: 53247u32, + edx: 5u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 4u32, + subleaf: 4u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 67108864u32, + ebx: 0u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 5u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 0u32, + ebx: 0u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 6u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 4u32, + ebx: 0u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 7u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 0u32, + ebx: 1050539u32, + ecx: 0u32, + edx: 2885682176u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 8u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 0u32, + ebx: 0u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 9u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 0u32, + ebx: 0u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 10u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 0u32, + ebx: 0u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 11u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 0u32, + ebx: 1u32, + ecx: 256u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 11u32, + subleaf: 1u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 7u32, + ebx: 2u32, + ecx: 513u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 11u32, + subleaf: 2u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 0u32, + ebx: 0u32, + ecx: 2u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 12u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 0u32, + ebx: 0u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 13u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 7u32, + ebx: 2696u32, + ecx: 2696u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 13u32, + subleaf: 1u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 1u32, + ebx: 2568u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 13u32, + subleaf: 2u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 256u32, + ebx: 576u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 13u32, + subleaf: 3u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 64u32, + ebx: 960u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 13u32, + subleaf: 4u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 64u32, + ebx: 1024u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 13u32, + subleaf: 5u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 64u32, + ebx: 1088u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 13u32, + subleaf: 6u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 512u32, + ebx: 1152u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 13u32, + subleaf: 7u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 1024u32, + ebx: 1664u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 13u32, + subleaf: 9u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 8u32, + ebx: 2688u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 1073741824u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 1073741825u32, + ebx: 1263359563u32, + ecx: 1447775574u32, + edx: 77u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 1073741825u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 16809723u32, + ebx: 0u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 2147483648u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 2147483656u32, + ebx: 0u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 2147483649u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 0u32, + ebx: 0u32, + ecx: 33u32, + edx: 672139264u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 2147483650u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 1702129225u32, + ebx: 693250156u32, + ecx: 1868912672u32, + edx: 693250158u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 2147483651u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 1869762592u32, + ebx: 1936942435u32, + ecx: 1075868271u32, + edx: 808334112u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 2147483652u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 2051557168u32, + ebx: 0u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 2147483653u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 0u32, + ebx: 0u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 2147483654u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 0u32, + ebx: 0u32, + ecx: 16801856u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 2147483655u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 0u32, + ebx: 0u32, + ecx: 0u32, + edx: 256u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 2147483656u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 12334u32, + ebx: 16830464u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map + })) +} + +pub fn t2s() -> Cpuid { + Cpuid::Intel(IntelCpuid({ + let mut map = std::collections::BTreeMap::new(); + map.insert( + CpuidKey { + leaf: 0u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 13u32, + ebx: 1970169159u32, + ecx: 1818588270u32, + edx: 1231384169u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 1u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 198386u32, + ebx: 133120u32, + ecx: 4160369155u32, + edx: 395049983u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 2u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 1979933441u32, + ebx: 15775231u32, + ecx: 0u32, + edx: 12779520u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 3u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 0u32, + ebx: 0u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 4u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 67109153u32, + ebx: 29360191u32, + ecx: 63u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 4u32, + subleaf: 1u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 67109154u32, + ebx: 29360191u32, + ecx: 63u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 4u32, + subleaf: 2u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 67109187u32, + ebx: 62914623u32, + ecx: 1023u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 4u32, + subleaf: 3u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 67125603u32, + ebx: 41943103u32, + ecx: 53247u32, + edx: 5u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 4u32, + subleaf: 4u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 67108864u32, + ebx: 0u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 5u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 0u32, + ebx: 0u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 6u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 4u32, + ebx: 0u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 7u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 0u32, + ebx: 1050539u32, + ecx: 0u32, + edx: 2885682176u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 8u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 0u32, + ebx: 0u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 9u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 0u32, + ebx: 0u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 10u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 0u32, + ebx: 0u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 11u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 0u32, + ebx: 1u32, + ecx: 256u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 11u32, + subleaf: 1u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 7u32, + ebx: 2u32, + ecx: 513u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 11u32, + subleaf: 2u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 0u32, + ebx: 0u32, + ecx: 2u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 12u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 0u32, + ebx: 0u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 13u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 7u32, + ebx: 2696u32, + ecx: 2696u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 13u32, + subleaf: 1u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 1u32, + ebx: 2568u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 13u32, + subleaf: 2u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 256u32, + ebx: 576u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 13u32, + subleaf: 3u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 64u32, + ebx: 960u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 13u32, + subleaf: 4u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 64u32, + ebx: 1024u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 13u32, + subleaf: 5u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 64u32, + ebx: 1088u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 13u32, + subleaf: 6u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 512u32, + ebx: 1152u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 13u32, + subleaf: 7u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 1024u32, + ebx: 1664u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 13u32, + subleaf: 9u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(1u32), + result: CpuidRegisters { + eax: 8u32, + ebx: 2688u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 1073741824u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 1073741825u32, + ebx: 1263359563u32, + ecx: 1447775574u32, + edx: 77u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 1073741825u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 16809723u32, + ebx: 0u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 2147483648u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 2147483656u32, + ebx: 0u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 2147483649u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 0u32, + ebx: 0u32, + ecx: 33u32, + edx: 672139264u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 2147483650u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 1702129225u32, + ebx: 693250156u32, + ecx: 1868912672u32, + edx: 693250158u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 2147483651u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 1869762592u32, + ebx: 1936942435u32, + ecx: 1075868271u32, + edx: 808334112u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 2147483652u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 2051557168u32, + ebx: 0u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 2147483653u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 0u32, + ebx: 0u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 2147483654u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 0u32, + ebx: 0u32, + ecx: 16801856u32, + edx: 0u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 2147483655u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 0u32, + ebx: 0u32, + ecx: 0u32, + edx: 256u32, + }, + }, + ); + map.insert( + CpuidKey { + leaf: 2147483656u32, + subleaf: 0u32, + }, + CpuidEntry { + flags: KvmCpuidFlags(0u32), + result: CpuidRegisters { + eax: 12334u32, + ebx: 16830464u32, + ecx: 0u32, + edx: 0u32, + }, + }, + ); + map + })) +} diff --git a/src/cpuid/Cargo.toml b/src/cpuid/Cargo.toml index 0305b7e0ae7..c08133e3b61 100644 --- a/src/cpuid/Cargo.toml +++ b/src/cpuid/Cargo.toml @@ -8,9 +8,12 @@ license = "Apache-2.0" [dependencies] kvm-bindings = { version = "0.6.0", features = ["fam-wrappers"] } kvm-ioctls = "0.12.0" -derive_more = { version = "0.99.17", default-features = false, features = ["from"] } thiserror = "1.0.32" utils = { path = "../utils"} -arch = { path = "../arch" } arch_gen = { path = "../arch_gen" } + +bit-fields = { path = "../bit-fields" } + +serde = { version = "1.0.138", features=["derive"] } +log = "0.4.17" diff --git a/src/cpuid/build.rs b/src/cpuid/build.rs new file mode 100644 index 00000000000..125eeaa2e24 --- /dev/null +++ b/src/cpuid/build.rs @@ -0,0 +1,14 @@ +// Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +fn main() { + // Sets a `--cfg` flag for conditional compilation. + // + // TODO: Use `core::arch::x86_64::has_cpuid` + // (https://github.com/firecracker-microvm/firecracker/issues/3271). + #[cfg(any( + all(target_arch = "x86", target_feature = "sse", not(target_env = "sgx")), + all(target_arch = "x86_64", not(target_env = "sgx")) + ))] + println!("cargo:rustc-cfg=cpuid"); +} diff --git a/src/cpuid/src/amd/indexing.rs b/src/cpuid/src/amd/indexing.rs new file mode 100644 index 00000000000..4575fa724ec --- /dev/null +++ b/src/cpuid/src/amd/indexing.rs @@ -0,0 +1,53 @@ +// Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +#![allow(clippy::transmute_ptr_to_ptr, clippy::needless_lifetimes)] + +use std::mem::transmute; + +#[allow(clippy::wildcard_imports)] +use super::leaves::*; +use crate::{index_leaf, transmute_vec, AmdCpuid, CpuidKey, IndexLeaf, IndexLeafMut}; + +index_leaf!(0x7, Leaf7, AmdCpuid); + +index_leaf!(0x80000000, Leaf80000000, AmdCpuid); + +index_leaf!(0x80000001, Leaf80000001, AmdCpuid); + +index_leaf!(0x80000008, Leaf80000008, AmdCpuid); + +impl IndexLeaf<0x8000001d> for AmdCpuid { + type Output<'a> = Leaf8000001d<'a>; + + #[inline] + fn index_leaf<'a>(&'a self) -> Self::Output<'a> { + // SAFETY: Transmuting reference to same sized types is safe. + unsafe { + Leaf8000001d(transmute_vec( + self.0 + .range(CpuidKey::leaf(0x8000001d)..CpuidKey::leaf(0x8000001e)) + .map(|(_, v)| transmute::<_, &'a Leaf8000001dSubleaf>(&v.result)) + .collect(), + )) + } + } +} + +impl IndexLeafMut<0x8000001d> for AmdCpuid { + type Output<'a> = Leaf8000001dMut<'a>; + + #[inline] + fn index_leaf_mut<'a>(&'a mut self) -> Self::Output<'a> { + // SAFETY: Transmuting reference to same sized types is safe. + unsafe { + Leaf8000001dMut(transmute_vec( + self.0 + .range_mut(CpuidKey::leaf(0x8000001d)..CpuidKey::leaf(0x8000001e)) + .map(|(_, v)| transmute::<_, &'a mut Leaf8000001dSubleaf>(&mut v.result)) + .collect(), + )) + } + } +} + +index_leaf!(0x8000001E, Leaf8000001e, AmdCpuid); diff --git a/src/cpuid/src/amd/leaves.rs b/src/cpuid/src/amd/leaves.rs new file mode 100644 index 00000000000..51d35f31e34 --- /dev/null +++ b/src/cpuid/src/amd/leaves.rs @@ -0,0 +1,34 @@ +// Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +#![allow(clippy::similar_names, clippy::module_name_repetitions)] + +#[allow(clippy::wildcard_imports)] +use super::registers::*; +use crate::Leaf; + +/// Leaf 07H +pub type Leaf7 = Leaf; + +/// Leaf 80000000H +pub type Leaf80000000 = Leaf; + +/// Leaf 80000001H +pub type Leaf80000001 = Leaf; + +/// Leaf 80000008H +pub type Leaf80000008 = Leaf; + +/// Leaf 8000001DH +#[derive(Debug, PartialEq, Eq)] +pub struct Leaf8000001d<'a>(pub Vec<&'a Leaf8000001dSubleaf>); + +/// Leaf 8000001DH +#[derive(Debug, PartialEq, Eq)] +pub struct Leaf8000001dMut<'a>(pub Vec<&'a mut Leaf8000001dSubleaf>); + +/// Leaf 8000001DH sub-leaf +pub type Leaf8000001dSubleaf = + Leaf; + +/// Leaf 8000001EH +pub type Leaf8000001e = Leaf; diff --git a/src/cpuid/src/amd/mod.rs b/src/cpuid/src/amd/mod.rs new file mode 100644 index 00000000000..6b8b86904b2 --- /dev/null +++ b/src/cpuid/src/amd/mod.rs @@ -0,0 +1,67 @@ +// Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +#![allow(clippy::similar_names, clippy::unreadable_literal)] + +use super::{CpuidEntry, CpuidKey, CpuidTrait, RawCpuid, RawKvmCpuidEntry}; + +/// Indexing implementations. +mod indexing; + +/// Leaf structs. +mod leaves; + +/// CPUID normalize implementation. +#[cfg(cpuid)] +mod normalize; +#[cfg(cpuid)] +pub use normalize::{ + ExtendedApicIdError, ExtendedCacheTopologyError, FeatureEntryError, NormalizeCpuidError, +}; + +/// Register bit fields. +mod registers; + +/// A structure matching the AMD CPUID specification as described in +/// [AMD64 Architecture Programmerā€™s Manual Volume 3: General-Purpose and System Instructions](https://www.amd.com/system/files/TechDocs/24594.pdf) +/// . +#[allow(clippy::module_name_repetitions)] +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct AmdCpuid(pub std::collections::BTreeMap); + +impl CpuidTrait for AmdCpuid { + /// Gets a given sub-leaf. + #[inline] + fn get(&self, key: &CpuidKey) -> Option<&CpuidEntry> { + self.0.get(key) + } + + /// Gets a given sub-leaf. + #[inline] + fn get_mut(&mut self, key: &CpuidKey) -> Option<&mut CpuidEntry> { + self.0.get_mut(key) + } +} + +impl From for AmdCpuid { + #[inline] + fn from(raw_cpuid: RawCpuid) -> Self { + let map = raw_cpuid + .iter() + .cloned() + .map(<(CpuidKey, CpuidEntry)>::from) + .collect(); + Self(map) + } +} + +impl From for RawCpuid { + #[inline] + fn from(amd_cpuid: AmdCpuid) -> Self { + let entries = amd_cpuid + .0 + .into_iter() + .map(RawKvmCpuidEntry::from) + .collect::>(); + Self::from(entries) + } +} diff --git a/src/cpuid/src/amd/normalize.rs b/src/cpuid/src/amd/normalize.rs new file mode 100644 index 00000000000..d0a4e64159d --- /dev/null +++ b/src/cpuid/src/amd/normalize.rs @@ -0,0 +1,320 @@ +// Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +use bit_fields::CheckedAssignError; + +use crate::{CpuidEntry, CpuidKey, CpuidRegisters, CpuidTrait, KvmCpuidFlags}; + +/// Error type for [`AmdCpuid::normalize`]. +#[allow(clippy::module_name_repetitions)] +#[derive(Debug, thiserror::Error, Eq, PartialEq)] +pub enum NormalizeCpuidError { + /// Provided `cpu_bits` is >=8. + #[error("Provided `cpu_bits` is >=8: {0}.")] + CpuBits(u8), + /// Missing leaf 0x80000000. + #[error("Missing leaf 0x80000000.")] + MissingLeaf0x80000000, + /// Missing leaf 0x80000001. + #[error("Missing leaf 0x80000001.")] + MissingLeaf0x80000001, + /// Failed to set feature entry leaf. + #[error("Failed to set feature entry leaf: {0}")] + FeatureEntry(#[from] FeatureEntryError), + /// Failed to set extended cache topology leaf. + #[error("Failed to set extended cache topology leaf: {0}")] + ExtendedCacheTopology(#[from] ExtendedCacheTopologyError), + /// Failed to set extended APIC ID leaf. + #[error("Failed to set extended APIC ID leaf: {0}")] + ExtendedApicId(#[from] ExtendedApicIdError), + /// Failed to set brand string. + #[error("Failed to set brand string: {0}")] + BrandString(crate::MissingBrandStringLeaves), +} + +/// Error type for setting leaf 0x80000008 section of [`AmdCpuid::normalize`]. +#[derive(Debug, thiserror::Error, Eq, PartialEq)] +pub enum FeatureEntryError { + /// Missing leaf 0x80000008. + #[error("Missing leaf 0x80000008.")] + MissingLeaf0x80000008, + /// Failed to set `nt` (number of physical threads) due to overflow. + #[error("Failed to set `nt` (number of physical threads) due to overflow.")] + NumberOfPhysicalThreadsOverflow, + /// Failed to set `nt` (number of physical threads). + #[error("Failed to set `nt` (number of physical threads).")] + NumberOfPhysicalThreads(CheckedAssignError), +} + +/// Error type for setting leaf 0x8000001d section of [`AmdCpuid::normalize`]. +#[derive(Debug, thiserror::Error, Eq, PartialEq)] +pub enum ExtendedCacheTopologyError { + /// Missing leaf 0x8000001d. + #[error("Missing leaf 0x8000001d.")] + MissingLeaf0x8000001d, + /// Failed to set `num_sharing_cache` due to overflow. + #[error("Failed to set `num_sharing_cache` due to overflow.")] + NumSharingCacheOverflow, + /// Failed to set `num_sharing_cache`. + #[error("Failed to set `num_sharing_cache`: {0}")] + NumSharingCache(CheckedAssignError), +} + +/// Error type for setting leaf 0x8000001e section of [`AmdCpuid::normalize`]. +#[derive(Debug, thiserror::Error, Eq, PartialEq)] +pub enum ExtendedApicIdError { + /// Missing leaf 0x8000001d. + #[error("Missing leaf 0x8000001e.")] + MissingLeaf0x8000001e, + /// Failed to set `extended_apic_id`. + #[error("Failed to set `extended_apic_id`: {0}")] + ExtendedApicId(CheckedAssignError), + /// Failed to set `compute_unit_id`. + #[error("Failed to set `compute_unit_id`: {0}")] + ComputeUnitId(CheckedAssignError), + /// Failed to set `threads_per_compute_unit`. + #[error("Failed to set `threads_per_compute_unit`: {0}")] + ThreadPerComputeUnit(CheckedAssignError), +} + +// We use this 2nd implementation so we can conveniently define functions only used within +// `normalize`. +#[allow(clippy::multiple_inherent_impl)] +impl super::AmdCpuid { + /// We always use this brand string. + const DEFAULT_BRAND_STRING: &[u8; crate::BRAND_STRING_LENGTH] = + b"AMD EPYC\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; + + /// Applies required modifications to CPUID respective of a vCPU. + /// + /// # Errors + /// + /// When attempting to access missing leaves or set fields within leaves to values that don't + /// fit. + #[inline] + pub fn normalize( + &mut self, + // The index of the current logical CPU in the range [0..cpu_count]. + cpu_index: u8, + // The total number of logical CPUs. + cpu_count: u8, + // The number of bits needed to enumerate logical CPUs per core. + cpu_bits: u8, + ) -> Result<(), NormalizeCpuidError> { + let cpus_per_core = 1u8 + .checked_shl(u32::from(cpu_bits)) + .ok_or(NormalizeCpuidError::CpuBits(cpu_bits))?; + + self.process_cpuid(); + self.update_largest_extended_fn_entry()?; + self.update_extended_feature_fn_entry()?; + self.update_amd_feature_entry(cpu_count)?; + self.update_extended_cache_topology_entry(cpu_count, cpus_per_core)?; + self.update_extended_apic_id_entry(cpu_index, cpus_per_core)?; + self.update_brand_string_entry()?; + + Ok(()) + } + + /// Process CPUID. + fn process_cpuid(&mut self) { + // Some versions of kernel may return the 0xB leaf for AMD even if this is an + // Intel-specific leaf. Remove it. + self.0.remove(&CpuidKey::leaf(0xB)); + + // Pass-through host CPUID for leaves 0x8000001e and 0x8000001d. + { + // 0x8000001e - Processor Topology Information + self.0.insert( + CpuidKey::leaf(0x8000001e), + CpuidEntry { + flags: KvmCpuidFlags::empty(), + // SAFETY: Safe as `cfg(cpuid)` ensure CPUID is supported. + result: CpuidRegisters::from(unsafe { + core::arch::x86_64::__cpuid(0x8000001e) + }), + }, + ); + + // 0x8000001d - Cache Topology Information + for subleaf in 0.. { + // SAFETY: Safe as `cfg(cpuid)` ensure CPUID is supported. + let result = CpuidRegisters::from(unsafe { + core::arch::x86_64::__cpuid_count(0x8000001d, subleaf) + }); + // From 'AMD64 Architecture Programmerā€™s Manual Volume 3: General-Purpose and System + // Instructions': + // > To gather information for all cache levels, software must repeatedly execute + // > CPUID with 8000_001Dh in EAX and ECX set to increasing values beginning with 0 + // > until a value of 00h is returned in the field CacheType (EAX[4:0]) indicating + // > no more cache descriptions are available for this processor. If CPUID + // > Fn8000_0001_ECX[TopologyExtensions] = 0, then CPUID Fn8000_001Dh is reserved. + if super::registers::Leaf8000001dEax::from(result.eax).cache_type() == 0 { + break; + } + self.0.insert( + CpuidKey::subleaf(0x8000001d, subleaf), + CpuidEntry { + flags: KvmCpuidFlags::SIGNIFICANT_INDEX, + result, + }, + ); + } + } + } + + /// Update largest extended fn entry. + fn update_largest_extended_fn_entry(&mut self) -> Result<(), NormalizeCpuidError> { + // KVM sets the largest extended function to 0x80000000. Change it to 0x8000001f + // Since we also use the leaf 0x8000001d (Extended Cache Topology). + let leaf_80000000 = self + .leaf_mut::<0x80000000>() + .ok_or(NormalizeCpuidError::MissingLeaf0x80000000)?; + // SAFETY: Safe, as `0x8000_001f` is within the known range. + unsafe { + leaf_80000000 + .eax + .l_func_ext_mut() + .unchecked_assign(0x8000_001f); + } + Ok(()) + } + + /// Updated extended feature fn entry. + fn update_extended_feature_fn_entry(&mut self) -> Result<(), NormalizeCpuidError> { + // set the Topology Extension bit since we use the Extended Cache Topology leaf + let leaf_80000001 = self + .leaf_mut::<0x80000001>() + .ok_or(NormalizeCpuidError::MissingLeaf0x80000001)?; + leaf_80000001.ecx.topology_extensions_mut().on(); + Ok(()) + } + + /// Update AMD feature entry. + fn update_amd_feature_entry(&mut self, cpu_count: u8) -> Result<(), FeatureEntryError> { + /// This value allows at most 64 logical threads within a package. + const THREAD_ID_MAX_SIZE: u32 = 7; + + // We don't support more then 128 threads right now. + // It's safe to put them all on the same processor. + let leaf_80000008 = self + .leaf_mut::<0x80000008>() + .ok_or(FeatureEntryError::MissingLeaf0x80000008)?; + + // SAFETY: `THREAD_ID_MAX_SIZE` is within the known range and always safe. + unsafe { + leaf_80000008 + .ecx + .apic_id_size_mut() + .unchecked_assign(THREAD_ID_MAX_SIZE); + } + leaf_80000008 + .ecx + .nt_mut() + .checked_assign(u32::from( + cpu_count + .checked_sub(1) + .ok_or(FeatureEntryError::NumberOfPhysicalThreadsOverflow)?, + )) + .map_err(FeatureEntryError::NumberOfPhysicalThreads)?; + Ok(()) + } + + /// Update extended cache topology entry. + fn update_extended_cache_topology_entry( + &mut self, + cpu_count: u8, + cpus_per_core: u8, + ) -> Result<(), ExtendedCacheTopologyError> { + let leaf_8000001d: super::leaves::Leaf8000001dMut = self.leaf_mut::<0x8000001d>(); + for subleaf in leaf_8000001d.0 { + match u32::from(&subleaf.eax.cache_level()) { + // L1 & L2 Cache + // The L1 & L2 cache is shared by at most 2 hyper-threads + 1 | 2 => subleaf + .eax + .num_sharing_cache_mut() + // SAFETY: We know `cpus_per_core > 0` therefore this is always safe. + .checked_assign(u32::from(unsafe { + cpus_per_core.checked_sub(1).unwrap_unchecked() + })) + .map_err(ExtendedCacheTopologyError::NumSharingCache)?, + // L3 Cache + // The L3 cache is shared among all the logical threads + 3 => subleaf + .eax + .num_sharing_cache_mut() + .checked_assign(u32::from( + cpu_count + .checked_sub(1) + .ok_or(ExtendedCacheTopologyError::NumSharingCacheOverflow)?, + )) + .map_err(ExtendedCacheTopologyError::NumSharingCache)?, + _ => (), + } + } + Ok(()) + } + + /// Update extended apic id entry + fn update_extended_apic_id_entry( + &mut self, + cpu_index: u8, + cpus_per_core: u8, + ) -> Result<(), ExtendedApicIdError> { + /// 1 node per processor. + const NODES_PER_PROCESSOR: u32 = 0; + + // When hyper-threading is enabled each pair of 2 consecutive logical CPUs + // will have the same core id since they represent 2 threads in the same core. + // For Example: + // logical CPU 0 -> core id: 0 + // logical CPU 1 -> core id: 0 + // logical CPU 2 -> core id: 1 + // logical CPU 3 -> core id: 1 + let core_id = + // SAFETY: We know `cpus_per_core != 0` therefore this is always safe. + unsafe { u32::from(cpu_index.checked_div(cpus_per_core).unwrap_unchecked()) }; + + let leaf_8000001e = self + .leaf_mut::<0x8000001e>() + .ok_or(ExtendedApicIdError::MissingLeaf0x8000001e)?; + leaf_8000001e + .eax + .extended_apic_id_mut() + .checked_assign(u32::from(cpu_index)) + .map_err(ExtendedApicIdError::ExtendedApicId)?; + + leaf_8000001e + .ebx + .compute_unit_id_mut() + .checked_assign(core_id) + .map_err(ExtendedApicIdError::ComputeUnitId)?; + leaf_8000001e + .ebx + .threads_per_compute_unit_mut() + // SAFETY: We know `cpus_per_core > 0` therefore this is always safe. + .checked_assign(u32::from(unsafe { + cpus_per_core.checked_sub(1).unwrap_unchecked() + })) + .map_err(ExtendedApicIdError::ThreadPerComputeUnit)?; + + // SAFETY: We know the value always fits within the range and thus is always safe. + unsafe { + // Set nodes per processor. + leaf_8000001e + .ecx + .nodes_per_processor_mut() + .unchecked_assign(NODES_PER_PROCESSOR); + // Put all the cpus in the same node. + leaf_8000001e.ecx.node_id_mut().unchecked_assign(0); + } + Ok(()) + } + + /// Update brand string entry + fn update_brand_string_entry(&mut self) -> Result<(), NormalizeCpuidError> { + self.apply_brand_string(Self::DEFAULT_BRAND_STRING) + .map_err(NormalizeCpuidError::BrandString)?; + Ok(()) + } +} diff --git a/src/cpuid/src/amd/registers.rs b/src/cpuid/src/amd/registers.rs new file mode 100644 index 00000000000..8bec2561c4c --- /dev/null +++ b/src/cpuid/src/amd/registers.rs @@ -0,0 +1,449 @@ +// Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +use bit_fields::bitfield; + +// ------------------------------------------------------------------------------------------------- +// Leaf 7 +// ------------------------------------------------------------------------------------------------- + +bitfield!(Leaf7Eax, u32, { + /// Returns the number of subfunctions supported. + max_sub_fn: 0..32, +}); + +bitfield!(Leaf7Ebx, u32, { + /// FS and GS base read/write instruction support. + fsgsbase: 0, + // Reserved + /// Bit manipulation group 1 instruction support. + bmi1: 3, + // Reserved + /// A value of 1 indicates that the processor supports SSE4.1. + avx2: 5, + // Reserved + /// Supervisor mode execution prevention. + smep: 7, + /// Bit manipulation group 2 instruction support. + bmi2: 8, + // Reserved + /// INVPCID instruction support. + invpcid: 10, + // Reserved + /// RDSEED instruction support. + rdseed: 18, + /// ADCX, ADOX instruction support. + adx: 19, + /// Supervisor mode access prevention. + smap: 20, + // Reserved + /// RDPID instruction and TSC_AUX MSR support. + rdpid: 22, + /// CLFLUSHOPT instruction support. + clflushopt: 23, + /// CLWB instruction support. + clwb: 24, + // Reserved + /// Secure Hash Algorithm instruction extension. + sha: 29, + // Reserved +}); + +bitfield!(Leaf7Ecx, u32, { + // Reserved + /// User mode instruction prevention support. + umip: 0, + /// Memory Protection Keys supported. + pku: 3, + /// OS has enabled Memory Protection Keys and use of the RDPKRU/WRPKRU + /// instructions by setting CR4.PKE=1. + ospke: 4, + // Reserved + /// Shadow Stacks supported. + cet_ss: 7, + // Reserved + /// Support for VAES 256-bit instructions. + vaes: 9, + /// Support for VPCLMULQDQ 256-bit instruction. + vpcmulqdq: 10, + // Reserved +}); + +bitfield!(Leaf7Edx, u32, { + // Reserved +}); + +// ------------------------------------------------------------------------------------------------- +// Leaf 80000000 +// ------------------------------------------------------------------------------------------------- + +bitfield!(Leaf80000000Eax, u32, { + /// Largest extended function. The largest CPUID extended function input value supported by the processor implementation. + l_func_ext: 0..32, +}); + +bitfield!(Leaf80000000Ebx, u32, { + /// Four characters of the 12-byte character string (encoded in ASCII) ā€œAuthenticAMDā€. + vendor: 0..32, +}); + +bitfield!(Leaf80000000Ecx, u32, { + /// Four characters of the 12-byte character string (encoded in ASCII) ā€œAuthenticAMDā€. + vendor: 0..32, +}); + +bitfield!(Leaf80000000Edx, u32, { + /// Four characters of the 12-byte character string (encoded in ASCII) ā€œAuthenticAMDā€. + vendor: 0..32, +}); +// ------------------------------------------------------------------------------------------------- +// Leaf 80000001 +// ------------------------------------------------------------------------------------------------- + +bitfield!(Leaf80000001Eax, u32, { + family_model_stepping: 0..32, +}); + +bitfield!(Leaf80000001Ebx, u32, { + /// Brand ID. This field, in conjunction with CPUID Fn0000_0001_EBX[8BitBrandId], is used by system firmware to generate the processor name string. See your processor revision guide for how to program the processor name string. + brand_id: 0..16, + // Reserved. + /// Package type. If (Family[7:0] >= 10h), this field is valid. If (Family[7:0]<10h), this field is reserved. + pkg_type: 28..32, +}); + +bitfield!(Leaf80000001Ecx, u32, { + /// LAHF and SAHF instruction support in 64-bit mode. See ā€œLAHFā€ and ā€œSAHFā€ in APM3. + lahf_sahf: 0, + /// Core multi-processing legacy mode. See ā€œLegacy Methodā€ on page 633. + cmp_legacy: 1, + /// Secure virtual machine. See ā€œSecure Virtual Machineā€ in APM2. + svm: 2, + /// Extended APIC space. This bit indicates the presence of extended APIC register space starting at offset 400h from the ā€œAPIC Base Address Register,ā€ as specified in the BKDG. + ext_apic_space: 3, + /// LOCK MOV CR0 means MOV CR8. See ā€œMOV(CRn)ā€ in APM3. + alt_mov_cr_8: 4, + /// Advanced bit manipulation. LZCNT instruction support. See ā€œLZCNTā€ in APM3. + abm: 5, + /// EXTRQ, INSERTQ, MOVNTSS, and MOVNTSD instruction support. See ā€œEXTRQā€, ā€œINSERTQā€, ā€œMOVNTSSā€, and ā€œMOVNTSDā€ in APM4. + sse4a: 6, + /// Misaligned SSE mode. See ā€œMisaligned Access Support Added for SSE Instructionsā€ in APM1. + mis_align_sse: 7, + /// PREFETCH and PREFETCHW instruction support. See ā€œPREFETCHā€ and ā€œPREFETCHWā€ in APM3. + d3_now_prefetch: 8, + /// OS visible workaround. Indicates OS-visible workaround support. See ā€œOS Visible Work-around (OSVW) Informationā€ in APM2. + osvw: 9, + /// Instruction based sampling. See ā€œInstruction Based Samplingā€ in APM2. + ibs: 10, + /// Extended operation support. + xop: 11, + /// SKINIT and STGI are supported. Indicates support for SKINIT and STGI, independent of the value of MSRC000_0080[SVME]. See APM2 and APM3. + skinit: 12, + /// Watchdog timer support. See APM2 and APM3. Indicates support for MSRC001_0074. + wdt: 13, + // Reserved. + /// Lightweight profiling support. See ā€œLightweight Profilingā€ in APM2 and reference pages for individual LWP instructions in APM3. + lwp: 15, + /// Four-operand FMA instruction support. + fma4: 16, + /// Translation Cache Extension support. + tce: 17, + // Reserved. + /// Trailing bit manipulation instruction support. + tbm: 21, + /// Topology extensions support. Indicates support for CPUID Fn8000_001D_EAX_x[N:0]-CPUID Fn8000_001E_EDX. + topology_extensions: 22, + /// Processor performance counter extensions support. Indicates support for MSRC001_020[A,8,6,4,2,0] and MSRC001_020[B,9,7,5,3,1]. + perf_ctr_ext_core: 23, + /// NB performance counter extensions support. Indicates support for MSRC001_024[6,4,2,0] and MSRC001_024[7,5,3,1]. + perf_ctr_ext_nb: 24, + // Reserved. + /// Data access breakpoint extension. Indicates support for MSRC001_1027 and MSRC001_101[B:9]. + data_bkpt_ext: 26, + /// Performance time-stamp counter. Indicates support for MSRC001_0280 [Performance Time Stamp Counter]. + perf_tsc: 27, + /// Support for L3 performance counter extension. + perf_ctr_ext_llc: 28, + /// Support for MWAITX and MONITORX instructions. + monitor_x: 29, + /// Breakpoint Addressing masking extended to bit 31. + add_mask_ext: 30, + // Reserved. +}); + +bitfield!(Leaf80000001Edx, u32, { + /// x87 floating-point unit on-chip. Same as CPUID Fn0000_0001_EDX[FPU]. + fpu: 0, + /// Virtual-mode enhancements. Same as CPUID Fn0000_0001_EDX[VME]. + vme: 1, + /// Debugging extensions. Same as CPUID Fn0000_0001_EDX[DE]. + de: 2, + /// Page-size extensions. Same as CPUID Fn0000_0001_EDX[PSE]. + pse: 3, + /// Time stamp counter. Same as CPUID Fn0000_0001_EDX[TSC]. + tsc: 4, + /// AMD model-specific registers. Same as CPUID Fn0000_0001_EDX[MSR]. + msr: 5, + /// Physical-address extensions. Same as CPUID Fn0000_0001_EDX[PAE]. + pae: 6, + /// Machine check exception. Same as CPUID Fn0000_0001_EDX[MCE]. + mce: 7, + /// CMPXCHG8B instruction. Same as CPUID Fn0000_0001_EDX[CMPXCHG8B]. + cmpxchg8b: 8, + /// Advanced programmable interrupt controller. Same as CPUID Fn0000_0001_EDX[APIC]. + apic: 9, + // Reserved. + /// SYSCALL and SYSRET instructions. See ā€œSYSCALLā€ and ā€œSYSRETā€ in APM3. + sys_call_sys_ret: 11, + /// Memory-type range registers. Same as CPUID Fn0000_0001_EDX[MTRR]. + mtrr: 12, + /// Page global extension. Same as CPUID Fn0000_0001_EDX[PGE]. + pge: 13, + /// Machine check architecture. Same as CPUID Fn0000_0001_EDX[MCA]. + mca: 14, + /// Conditional move instructions. Same as CPUID Fn0000_0001_EDX[CMOV]. + cmov: 15, + /// Page attribute table. Same as CPUID Fn0000_0001_EDX[PAT]. + pat: 16, + /// Page-size extensions. Same as CPUID Fn0000_0001_EDX[PSE36]. + pse36: 17, + // Reserved. + /// No-execute page protection. See ā€œPage Translation and Protectionā€ in APM2. + nx: 20, + // Reserved. + /// AMD extensions to MMX instructions. See Appendix D ā€œInstruction Subsets and CPUID Feature Setsā€ in APM3 and ā€œ128-Bit Media and Scientific Programmingā€ in APM1. + mmx_ext: 22, + /// MMXā„¢ instructions. Same as CPUID Fn0000_0001_EDX[MMX]. + mmx: 23, + /// FXSAVE and FXRSTOR instructions. Same as CPUID Fn0000_0001_EDX[FXSR]. + fxsr: 24, + /// FXSAVE and FXRSTOR instruction optimizations. See ā€œFXSAVEā€ and ā€œFXRSTORā€ in APM5. + ffxsr: 25, + /// 1-GB large page support. See ā€œ1-GB Paging Supportā€ in APM2. + page_1gb: 26, + /// RDTSCP instruction. See ā€œRDTSCPā€ in APM3. + rdtscp: 27, + // Reserved. + /// Long mode. See ā€œProcessor Initialization and Long-Mode Activationā€ in APM2. + lm: 29, + /// AMD extensions to 3DNow! instructions. See Appendix D ā€œInstruction Subsets and CPUID Feature Setsā€ in APM3. + d3_now_ext: 30, + /// 3DNow!ā„¢ instructions. See Appendix D ā€œInstruction Subsets and CPUID Feature Setsā€ in APM3. + d3_now: 31, +}); +// ------------------------------------------------------------------------------------------------- +// Leaf 80000008 +// ------------------------------------------------------------------------------------------------- + +bitfield!(Leaf80000008Eax, u32, { + /// Maximum physical address size in bits. When GuestPhysAddrSize is zero, this field also indicates the maximum guest physical address size. + phys_addr_size: 0..8, + /// Maximum linear address size in bits. + lin_addr_size: 8..16, + /// Maximum guest physical address size in bits. This number applies only to guests using nested paging. When this field is zero, refer to the PhysAddrSize field for the maximum guest physical address size. See ā€œSecure Virtual Machineā€ in APM2. + guest_phys_addr_size: 16..24, + // Reserved. +}); + +bitfield!(Leaf80000008Ebx, u32, { + /// CLZERO instruction supported + clzero: 0, + /// Instruction Retired Counter MSR available + inst_ret_cnt_msr: 1, + /// FP Error Pointers Restored by XRSTOR + rstr_fp_err_ptrs: 2, + /// INVLPGB and TLBSYNC instruction supported + invlpgb: 3, + /// RDPRU instruction supported + rdpru: 4, + // Reserved. + /// MCOMMIT instruction supported + mcommit: 8, + /// WBNOINVD instruction supported + wbnoinvd: 9, + // Reserved. + /// Indirect Branch Prediction Barrier + ibpd: 12, + /// WBINVD/WBNOINVD are interruptible. + int_wbinvd: 13, + /// Indirect Branch Restricted Speculation + ibrs: 14, + /// Single Thread Indirect Branch Prediction mode + stibp: 15, + /// Processor prefers that IBRS be left on + ibrs_always_on: 16, + /// Processor prefers that STIBP be left on + stibp_always_on: 17, + /// IBRS is preferred over software solution + ibrs_preferred: 18, + /// IBRS provides same mode speculation limits + ibrs_same_mode: 19, + /// EFER.LMSLE is unsupported. + efer_lmsle_unsupported: 20, + /// INVLPGB support for invalidating guest nested translations + invlpgb_nested_pages: 21, + // Reserved. + /// Speculative Store Bypass Disable + ssbd: 24, + /// Use VIRT_SPEC_CTL for SSBD + ssbd_virt_spec_ctrl: 25, + /// SSBD not needed on this processor + ssbd_not_required: 26, + // Reserved. + /// Predictive Store Forward Disable + psfd: 28, + // Reserved. + +}); + +bitfield!(Leaf80000008Ecx, u32, { + /// Number of physical threads - 1. The number of threads in the processor is NT+1 + /// (e.g., if NT = 0, then there is one thread). See ā€œLegacy Methodā€ on page 633. + nt: 0..8, + // Reserved. + /// APIC ID size. The number of bits in the initial APIC20[ApicId] value that indicate + /// logical processor ID within a package. The size of this field determines the + /// maximum number of logical processors (MNLP) that the package could + /// theoretically support, and not the actual number of logical processors that are + /// implemented or enabled in the package, as indicated by CPUID + /// Fn8000_0008_ECX[NC]. A value of zero indicates that legacy methods must be + /// used to determine the maximum number of logical processors, as indicated by + /// CPUID Fn8000_0008_ECX[NC]. + apic_id_size: 12..16, + /// Performance time-stamp counter size. Indicates the size of MSRC001_0280[PTSC]. + /// ```text + /// Bits Description + /// 00b 40 bits + /// 01b 48 bits + /// 10b 56 bits + /// 11b 64 bits + /// ``` + pref_tsc_size: 16..18, + // Reserved. +}); + +bitfield!(Leaf80000008Edx, u32, { + /// Maximum page count for INVLPGB instruction. + invlpgb_count_max: 0..16, + /// The maximum ECX value recognized by RDPRU. + max_rdpru_id: 16..32, +}); +// ------------------------------------------------------------------------------------------------- +// Leaf 8000001D +// ------------------------------------------------------------------------------------------------- + +bitfield!(Leaf8000001dEax, u32, { + /// Cache type. Identifies the type of cache. + /// ```text + /// Bits Description + /// 00h Null; no more caches. + /// 01h Data cache + /// 02h Instruction cache + /// 03h Unified cache + /// 1Fh-04h Reserved. + /// ``` + cache_type: 0..4, + /// Cache level. Identifies the level of this cache. Note that the enumeration value is + /// not necessarily equal to the cache level. + /// ```text + /// Bits Description + /// 000b Reserved. + /// 001b Level 1 + /// 010b Level 2 + /// 011b Level 3 + /// 111b-100b Reserved. + /// ``` + cache_level: 5..8, + /// Self-initializing cache. When set, indicates that the cache is self initializing; + /// software initialization not required. If 0 is returned in this field, hardware does not + /// initialize this cache. + self_initialization: 8, + /// Fully associative cache. When set, indicates that the cache is fully associative. If + /// 0 is returned in this field, the cache is set associative. + fully_associative: 9, + // Reserved. + /// Specifies the number of logical processors sharing the cache enumerated by N, + /// the value passed to the instruction in ECX. The number of logical processors + /// sharing this cache is the value of this field incremented by 1. To determine which + /// logical processors are sharing a cache, determine a Share Id for each processor + /// as follows: + /// + /// ShareId = LocalApicId >> log2(NumSharingCache+1) + /// + /// Logical processors with the same ShareId then share a cache. If + /// NumSharingCache+1 is not a power of two, round it up to the next power of two. + num_sharing_cache: 14..26, + // Reserved. +}); + +bitfield!(Leaf8000001dEbx, u32, { + /// Cache line size. The cache line size in bytes is the value returned in this field + /// incremented by 1. + cache_line_size: 0..12, + /// Number of physical line partitions. The number of physical line partitions is the + /// value returned in this field incremented by 1. + cache_phys_partitions: 12..22, + /// Number of ways for this cache. The number of ways is the value returned in this + /// field incremented by 1. + cache_num_ways: 22..32, + +}); + +bitfield!(Leaf8000001dEcx, u32, { + /// Number of ways for set associative cache. Number of ways is the value returned in + /// this field incremented by 1. Only valid for caches that are not fully associative + /// (Fn8000_001D_EAX_xn[FullyAssociative] = 0). + cache_num_sets: 0..32, +}); + +bitfield!(Leaf8000001dEdx, u32, { + /// Write-Back Invalidate/Invalidate execution scope. A value of 0 returned in this field + /// indicates that the WBINVD/INVD instruction invalidates all lower level caches of + /// non-originating logical processors sharing this cache. When set, this field indicates + /// that the WBINVD/INVD instruction is not guaranteed to invalidate all lower level + /// caches of non-originating logical processors sharing this cache. + wbinvd: 0, + /// Cache inclusivity. A value of 0 indicates that this cache is not inclusive of lower + /// cache levels. A value of 1 indicates that the cache is inclusive of lower cache + /// levels. + cache_inclusive: 1, + // Reserved. +}); +// ------------------------------------------------------------------------------------------------- +// Leaf 8000001E +// ------------------------------------------------------------------------------------------------- + +bitfield!(Leaf8000001eEax, u32, { + /// Extended APIC ID. If MSR0000_001B[ApicEn] = 0, this field is reserved.. + extended_apic_id: 0..32, +}); + +bitfield!(Leaf8000001eEbx, u32, { + compute_unit_id: 0..8, + /// Threads per compute unit (zero-based count). The actual number of threads + /// per compute unit is the value of this field + 1. To determine which logical + /// processors (threads) belong to a given Compute Unit, determine a ShareId + /// for each processor as follows: + /// + /// ShareId = LocalApicId >> log2(ThreadsPerComputeUnit+1) + /// + /// Logical processors with the same ShareId then belong to the same Compute + /// Unit. (If ThreadsPerComputeUnit+1 is not a power of two, round it up to the + /// next power of two). + threads_per_compute_unit: 8..16, + // Reserved. + +}); + +bitfield!(Leaf8000001eEcx, u32, { + /// Specifies the ID of the node containing the current logical processor. NodeId + /// values are unique across the system.. + node_id: 0..8, + /// Specifies the number of nodes in the package/socket in which this logical + /// processor resides. Node in this context corresponds to a processor die. + /// Encoding is N-1, where N is the number of nodes present in the socket. + nodes_per_processor: 8..10, +}); + +bitfield!(Leaf8000001eEdx, u32, { + // Reserved. +}); diff --git a/src/cpuid/src/bit_helper.rs b/src/cpuid/src/bit_helper.rs deleted file mode 100644 index 36385454329..00000000000 --- a/src/cpuid/src/bit_helper.rs +++ /dev/null @@ -1,593 +0,0 @@ -// 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 -/// -/// ``` -/// use cpuid::bit_helper::*; -/// -/// let range = BitRange { -/// msb_index: 7, -/// lsb_index: 3, -/// }; -/// ``` -/// The BitRange specified above will represent the following part of the number 72: -/// +-------------------------------------+---+---+---+---+---+---+---+---+---+---+ -/// | Base 2 Representation of the number | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | -/// +-------------------------------------+---+---+---+---+---+---+---+---+---+---+ -/// | bits indexes | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | -/// +-------------------------------------+---+---+---+---+---+---+---+---+---+---+ -/// | BitRange | | | * | * | * | * | * | | | | -/// +-------------------------------------+---+---+---+---+---+---+---+---+---+---+ -pub struct BitRange { - /// most significant bit index - pub msb_index: u32, - /// least significant bit index - pub lsb_index: u32, -} - -/// Trait containing helper methods for [`BitRange`](struct.BitRange.html) -/// -/// The methods are needed for: -/// - checking if the `BitRange` is valid for a type `T` -/// - creating masks for a type `T` -pub trait BitRangeExt { - /// Returns a value of type `T` that has all the bits in the specified bit range set to 1. - /// - /// # Example - /// - /// ``` - /// use cpuid::bit_helper::*; - /// - /// let range = BitRange { - /// msb_index: 7, - /// lsb_index: 3, - /// }; - /// println!("binary value: {:b}", range.get_mask()); - /// ``` - /// The code above will print: - /// ```bash - /// binary value: 11111000 - /// ``` - fn get_mask(&self) -> T; - - /// Checks if the current BitRange is valid for type `T`. - fn is_valid(&self) -> bool; - - /// Asserts if `self.is_valid()` returns true. - fn check(&self) { - assert!(self.is_valid(), "Invalid BitRange"); - } -} - -const MAX_U32_BIT_INDEX: u32 = 31; - -impl BitRangeExt for BitRange { - fn get_mask(&self) -> u32 { - self.check(); - - ((((1_u64) << (self.msb_index - self.lsb_index + 1)) - 1) << self.lsb_index) as u32 - } - - fn is_valid(&self) -> bool { - self.msb_index >= self.lsb_index && self.msb_index <= MAX_U32_BIT_INDEX - } -} - -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; - - /// Reads the value stored within the specified range of bits - /// - /// # Example - /// - /// ``` - /// use cpuid::bit_helper::*; - /// - /// let val: u32 = 0b000010001000; - /// let range = BitRange { - /// msb_index: 7, - /// lsb_index: 3, - /// }; - /// println!("binary value: {:b}", val.read_bits_in_range(&range)); - /// ``` - /// The code above will print: - /// ```bash - /// binary value: 10001 - /// ``` - fn read_bits_in_range(&self, bit_range: &BitRange) -> Self; - - /// Stores a value within the specified range of bits - /// - /// # Example - /// - /// ``` - /// use cpuid::bit_helper::*; - /// - /// let mut val: u32 = 0; - /// let range = BitRange { - /// msb_index: 7, - /// lsb_index: 3, - /// }; - /// val.write_bits_in_range(&range, 0b10001 as u32); - /// println!("binary value: {:b}", val); - /// ``` - /// The code above will print: - /// ```bash - /// binary value: 10001000 - /// ``` - fn write_bits_in_range(&mut self, bit_range: &BitRange, val: Self) -> &mut Self; -} - -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"); - - *self &= !(1 << pos); - *self |= (u32::from(val)) << pos; - self - } - - fn read_bits_in_range(&self, range: &BitRange) -> Self { - range.check(); - - (self & range.get_mask()) >> range.lsb_index - } - - fn write_bits_in_range(&mut self, range: &BitRange, val: Self) -> &mut Self { - range.check(); - let mask = range.get_mask(); - let max_val = mask >> range.lsb_index; - assert!(val <= max_val, "Invalid val"); - - *self &= !mask; - *self |= val << range.lsb_index; - self - } -} - -#[cfg(test)] -mod tests { - use crate::bit_helper::*; - - #[test] - #[should_panic] - fn test_invalid_msb_index() { - let range = BitRange { - msb_index: 32, - lsb_index: 2, - }; - range.check(); - } - - #[test] - #[should_panic] - fn test_invalid_range() { - let range = BitRange { - msb_index: 10, - lsb_index: 15, - }; - range.check(); - } - - #[test] - #[should_panic] - fn test_invalid_write_bit() { - // Set bit to 1 - let mut val: u32 = 0; - val.write_bit(32, true); - } - - #[test] - fn test_simple_write_bit() { - // Set bit to 1 - let mut val: u32 = 0; - val.write_bit(5, true); - assert!(val == 1 << 5); - - // Set bit to 0 - val = 1 << 5; - val.write_bit(5, false); - 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 = 0b10_0000; - assert!(val.read_bit(5)); - assert!(!val.read_bit(4)); - } - - #[test] - fn test_chained_write_bit() { - let mut val: u32 = 1 << 12; - - val.write_bit(5, true) - .write_bit(10, true) - .write_bit(15, true) - .write_bit(12, false); - assert!(val == 1 << 5 | 1 << 10 | 1 << 15); - } - - #[test] - fn test_get_u32_mask_for_range() { - // Test a couple of successive ranges - assert!( - BitRange { - msb_index: 3, - lsb_index: 2 - } - .get_mask() - == 0b1100 - ); - assert!( - BitRange { - msb_index: 4, - lsb_index: 2 - } - .get_mask() - == 0b11100 - ); - assert!( - BitRange { - msb_index: 5, - lsb_index: 2 - } - .get_mask() - == 0b11_1100 - ); - assert!( - BitRange { - msb_index: 6, - lsb_index: 2 - } - .get_mask() - == 0b111_1100 - ); - assert!( - BitRange { - msb_index: 7, - lsb_index: 2 - } - .get_mask() - == 0b1111_1100 - ); - } - - #[test] - #[should_panic] - fn test_invalid_read_bits() { - let val: u32 = 30; - val.read_bits_in_range(&BitRange { - msb_index: 32, - lsb_index: 2, - }); - } - - #[test] - fn test_read_bits() { - let val: u32 = 0b1000_0000_0000_0000_0011_0101_0001_0000; - - // Test a couple of successive ranges - assert!( - val.read_bits_in_range(&BitRange { - msb_index: 3, - lsb_index: 2 - }) == 0b00 - ); - assert!( - val.read_bits_in_range(&BitRange { - msb_index: 4, - lsb_index: 2 - }) == 0b100 - ); - assert!( - val.read_bits_in_range(&BitRange { - msb_index: 5, - lsb_index: 2 - }) == 0b0100 - ); - assert!( - val.read_bits_in_range(&BitRange { - msb_index: 6, - lsb_index: 2 - }) == 0b00100 - ); - assert!( - val.read_bits_in_range(&BitRange { - msb_index: 7, - lsb_index: 2 - }) == 0b00_0100 - ); - assert!( - val.read_bits_in_range(&BitRange { - msb_index: 8, - lsb_index: 2 - }) == 0b100_0100 - ); - assert!( - val.read_bits_in_range(&BitRange { - msb_index: 9, - lsb_index: 2 - }) == 0b0100_0100 - ); - assert!( - val.read_bits_in_range(&BitRange { - msb_index: 10, - lsb_index: 2 - }) == 0b1_0100_0100 - ); - assert!( - val.read_bits_in_range(&BitRange { - msb_index: 11, - lsb_index: 2 - }) == 0b01_0100_0100 - ); - assert!( - val.read_bits_in_range(&BitRange { - msb_index: 12, - lsb_index: 2 - }) == 0b101_0100_0100 - ); - assert!( - val.read_bits_in_range(&BitRange { - msb_index: 13, - lsb_index: 2 - }) == 0b1101_0100_0100 - ); - - // Test max left and max right - assert!( - val.read_bits_in_range(&BitRange { - msb_index: 31, - lsb_index: 15 - }) == 0b1_0000_0000_0000_0000 - ); - assert!( - val.read_bits_in_range(&BitRange { - msb_index: 14, - lsb_index: 0 - }) == 0b011_0101_0001_0000 - ); - assert!( - val.read_bits_in_range(&BitRange { - msb_index: 31, - lsb_index: 0 - }) == 0b1000_0000_0000_0000_0011_0101_0001_0000 - ); - } - - #[test] - #[should_panic] - fn test_invalid_write_bits() { - let mut val: u32 = 0; - - val.write_bits_in_range( - &BitRange { - msb_index: 32, - lsb_index: 2, - }, - 0b100, - ); - } - - #[test] - #[should_panic] - fn test_overflow_write_bits() { - let mut val: u32 = 0; - - val.write_bits_in_range( - &BitRange { - msb_index: 3, - lsb_index: 2, - }, - 0b100, - ); - } - - #[test] - fn test_simple_write_bits() { - let mut val: u32 = 0; - - // Test a couple of successive ranges - assert!( - val.write_bits_in_range( - &BitRange { - msb_index: 3, - lsb_index: 2 - }, - 0b00 - ) == &0b0000 - ); - assert!( - val.write_bits_in_range( - &BitRange { - msb_index: 4, - lsb_index: 2 - }, - 0b100 - ) == &0b10000 - ); - assert!( - val.write_bits_in_range( - &BitRange { - msb_index: 5, - lsb_index: 2 - }, - 0b0100 - ) == &0b01_0000 - ); - assert!( - val.write_bits_in_range( - &BitRange { - msb_index: 6, - lsb_index: 2 - }, - 0b0_0100 - ) == &0b001_0000 - ); - assert!( - val.write_bits_in_range( - &BitRange { - msb_index: 7, - lsb_index: 2 - }, - 0b00_0100 - ) == &0b0001_0000 - ); - assert!( - val.write_bits_in_range( - &BitRange { - msb_index: 8, - lsb_index: 2 - }, - 0b100_0100 - ) == &0b1_0001_0000 - ); - assert!( - val.write_bits_in_range( - &BitRange { - msb_index: 9, - lsb_index: 2 - }, - 0b0100_0100 - ) == &0b01_0001_0000 - ); - assert!( - val.write_bits_in_range( - &BitRange { - msb_index: 10, - lsb_index: 2 - }, - 0b1_0100_0100 - ) == &0b101_0001_0000 - ); - assert!( - val.write_bits_in_range( - &BitRange { - msb_index: 11, - lsb_index: 2 - }, - 0b01_0100_0100 - ) == &0b0101_0001_0000 - ); - assert!( - val.write_bits_in_range( - &BitRange { - msb_index: 12, - lsb_index: 2 - }, - 0b101_0100_0100 - ) == &0b1_0101_0001_0000 - ); - assert!( - val.write_bits_in_range( - &BitRange { - msb_index: 13, - lsb_index: 2 - }, - 0b1101_0100_0100 - ) == &0b11_0101_0001_0000 - ); - - // Test max left and max right - val = 0; - assert!( - val.write_bits_in_range( - &BitRange { - msb_index: 31, - lsb_index: 15 - }, - 0b1_0000_0000_0000_0000 - ) == &0b1000_0000_0000_0000_0000_0000_0000_0000 - ); - assert!( - val.write_bits_in_range( - &BitRange { - msb_index: 14, - lsb_index: 0 - }, - 0b011_0101_0001_0000 - ) == &0b1000_0000_0000_0000_0011_0101_0001_0000 - ); - assert!( - val.write_bits_in_range( - &BitRange { - msb_index: 31, - lsb_index: 0 - }, - 0b1000_0000_0000_0000_0011_0101_0001_0000 - ) == &0b1000_0000_0000_0000_0011_0101_0001_0000 - ); - } - - #[test] - fn test_chained_write_bits() { - let mut val: u32 = 0; - - // Test a couple of ranges - val.write_bits_in_range( - &BitRange { - msb_index: 4, - lsb_index: 2, - }, - 0b100, - ) - .write_bits_in_range( - &BitRange { - msb_index: 12, - lsb_index: 10, - }, - 0b110, - ) - .write_bits_in_range( - &BitRange { - msb_index: 24, - lsb_index: 20, - }, - 0b10101, - ) - .write_bits_in_range( - &BitRange { - msb_index: 31, - lsb_index: 28, - }, - 0b1011, - ); - - assert!(val == 0b1011_0001_0101_0000_0001_1000_0001_0000); - } -} diff --git a/src/cpuid/src/brand_string.rs b/src/cpuid/src/brand_string.rs deleted file mode 100644 index ca3e9c1e20d..00000000000 --- a/src/cpuid/src/brand_string.rs +++ /dev/null @@ -1,490 +0,0 @@ -// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -use std::arch::x86_64::__cpuid as host_cpuid; -use std::slice; - -use crate::common::{VENDOR_ID_AMD, VENDOR_ID_INTEL}; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum Error { - NotSupported, - Overflow(String), -} - -/// Register designations used to get/set specific register values within the brand string buffer. -pub enum Reg { - Eax = 0, - Ebx = 1, - Ecx = 2, - Edx = 3, -} - -const BRAND_STRING_INTEL: &[u8] = b"Intel(R) Xeon(R) Processor"; -const BRAND_STRING_AMD: &[u8] = b"AMD EPYC"; - -/// A CPUID brand string wrapper, providing some efficient manipulation primitives. -/// -/// 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. - /// - /// It has the following layout: - /// reg_buf[0] = leaf_0x80000002.EAX - /// reg_buf[1] = leaf_0x80000002.EBX - /// reg_buf[2] = leaf_0x80000002.ECX - /// reg_buf[3] = leaf_0x80000002.EDX - /// reg_buf[4] = leaf_0x80000003.EAX - /// ... - /// reg_buf[10] = leaf_0x80000004.ECX - /// reg_buf[11] = leaf_0x80000004.EDX - /// When seen as a byte-array, this buffer holds the ASCII-encoded CPU brand string. - reg_buf: [u32; BrandString::REG_BUF_SIZE], - - /// Actual string length, in bytes. - /// - /// E.g. For "Intel CPU", this would be `strlen("Intel CPU") == 9`. - len: usize, -} - -impl BrandString { - /// Register buffer size (in number of registers). - /// - /// There are 3 leaves (0x800000002 through 0x80000004), each with 4 regs (EAX, EBX, ECX, EDX). - const REG_BUF_SIZE: usize = 3 * 4; - - /// Max Brand string length, in bytes (also in chars, since it is ASCII-encoded). - /// - /// The string is NULL-terminated, so the max string length is actually one byte - /// less than the buffer size in bytes - const MAX_LEN: usize = Self::REG_BUF_SIZE * 4 - 1; - - /// Creates an empty brand string (0-initialized) - fn new() -> Self { - Self { - reg_buf: [0; Self::REG_BUF_SIZE], - len: 0, - } - } - - /// Generates the emulated 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). - /// - /// For AMD CPUs, the brand string we expose will be AMD EPYC. - /// - /// For other CPUs, we'll just expose an empty string. - /// - /// This is safe because we know BRAND_STRING_INTEL and BRAND_STRING_AMD to hold valid data - /// (allowed length and holding only valid ASCII chars). - pub fn from_vendor_id(vendor_id: &[u8; 12]) -> BrandString { - match vendor_id { - VENDOR_ID_INTEL => { - let mut this = BrandString::from_bytes_unchecked(BRAND_STRING_INTEL); - if let Ok(host_bstr) = BrandString::from_host_cpuid() { - if let Some(freq) = host_bstr.find_freq() { - this.push_bytes(b" @ ").unwrap(); - this.push_bytes(freq) - .expect("Unexpected frequency information in host CPUID"); - } - } - this - } - VENDOR_ID_AMD => BrandString::from_bytes_unchecked(BRAND_STRING_AMD), - _ => BrandString::from_bytes_unchecked(b""), - } - } - - /// Creates a brand string, initialized from the CPUID leaves 0x80000002 through 0x80000004 - /// of the host CPU. - pub fn from_host_cpuid() -> Result { - let mut this = Self::new(); - // This operation is safe as long as the processor implements this - // funcion of CPUID. - // 0x8000_0000 is the defined code getting the highest extended function - // implemented by the processor. - // It is hinted by the [spec](https://doc.rust-lang.org/beta/core/arch/x86_64/fn.__cpuid_count.html) - // that this is always available, but this is unclear. - #[allow(clippy::undocumented_unsafe_blocks)] - let mut cpuid_regs = unsafe { host_cpuid(0x8000_0000) }; - - if cpuid_regs.eax < 0x8000_0004 { - // Brand string not supported by the host CPU - return Err(Error::NotSupported); - } - - for leaf in 0x8000_0002..=0x8000_0004 { - // SAFETY: This is safe because we checked that the highest - // extended function includes the processor brand string. - cpuid_regs = unsafe { host_cpuid(leaf) }; - this.set_reg_for_leaf(leaf, Reg::Eax, cpuid_regs.eax); - this.set_reg_for_leaf(leaf, Reg::Ebx, cpuid_regs.ebx); - this.set_reg_for_leaf(leaf, Reg::Ecx, cpuid_regs.ecx); - this.set_reg_for_leaf(leaf, Reg::Edx, cpuid_regs.edx); - } - - this.len = null_terminator_index(this.as_bytes()); - - Ok(this) - } - - /// Creates a (custom) brand string, initialized from `src`. - /// - /// No checks are performed on the length of `src` or its contents (`src` should be an - /// ASCII-encoded string). - #[inline] - pub fn from_bytes_unchecked(src: &[u8]) -> Self { - let mut this = Self::new(); - this.len = src.len(); - this.as_bytes_mut()[..src.len()].copy_from_slice(src); - this - } - - /// Returns the given register value for the given CPUID leaf. - /// - /// `leaf` must be between 0x80000002 and 0x80000004. - #[inline] - pub fn get_reg_for_leaf(&self, leaf: u32, reg: Reg) -> u32 { - // It's ok not to validate parameters here, leaf and reg should - // both be compile-time constants. If there's something wrong with them, - // that's a programming error and we should panic anyway. - self.reg_buf[(leaf - 0x8000_0002) as usize * 4 + reg as usize] - } - - /// Sets the value for the given leaf/register pair. - /// - /// `leaf` must be between 0x80000002 and 0x80000004. - #[inline] - fn set_reg_for_leaf(&mut self, leaf: u32, reg: Reg, val: u32) { - // It's ok not to validate parameters here, leaf and reg should - // both be compile-time constants. If there's something wrong with them, - // that's a programming error and we should panic anyway. - self.reg_buf[(leaf - 0x8000_0002) as usize * 4 + reg as usize] = val; - } - - /// Gets an immutable `u8` slice view into the brand string buffer. - #[inline] - fn as_bytes(&self) -> &[u8] { - // SAFETY: This is actually safe, because self.reg_buf has a fixed, known size, - // and also there's no risk of misalignment, since we're downgrading - // alignment constraints from dword to byte. - unsafe { slice::from_raw_parts(self.reg_buf.as_ptr().cast::(), Self::REG_BUF_SIZE * 4) } - } - - /// Gets a mutable `u8` slice view into the brand string buffer. - #[inline] - fn as_bytes_mut(&mut self) -> &mut [u8] { - // SAFETY: This is actually safe, because self.reg_buf has a fixed, known size, - // and also there's no risk of misalignment, since we're downgrading - // alignment constraints from dword to byte. - // Returned mut reference is exclusive because the mut self reference is also exclusive. - unsafe { - slice::from_raw_parts_mut( - self.reg_buf.as_mut_ptr().cast::(), - Self::REG_BUF_SIZE * 4, - ) - } - } - - /// Asserts whether or not there is enough room to append `src` to the brand string. - fn check_push(&mut self, src: &[u8]) -> bool { - src.len() <= Self::MAX_LEN - self.len - } - - /// Appends `src` to the brand string if there is enough room to append it. - pub fn push_bytes(&mut self, src: &[u8]) -> Result<(), Error> { - if !self.check_push(src) { - // No room to push all of src. - return Err(Error::Overflow( - "Appending to the brand string failed.".to_string(), - )); - } - let start = self.len; - let count = src.len(); - self.len += count; - self.as_bytes_mut()[start..(start + count)].copy_from_slice(src); - Ok(()) - } - - /// Searches the brand string for the CPU frequency data it may contain (e.g. 4.01GHz), - /// and, if found, returns it as an `u8` slice. - /// - /// Basically, we're implementing a search for this regex: "([0-9]+\.[0-9]+[MGT]Hz)". - pub fn find_freq(&self) -> Option<&[u8]> { - // The algorithm for matching the regular expression above is based - // on a Moore machine, and 'stage' represents the current state of - // the machine. - enum Stages { - /// Initial state, looking for a digit. - Initial, - /// Found integer part of the frequency. - FoundFreqIntPart, - /// Found the decimal point. - FoundFreqDecimalPoint, - /// Found the decimal part. - FoundFreqDecimalPart, - /// Found the unit size. - FoundFreqUnitSize, - /// Found the H in 'Hz'. - FoundH, - } - - let mut freq_start = 0; - let mut decimal_start = 0; - - let mut stage = Stages::Initial; - - for (i, &ch) in self.as_bytes().iter().enumerate() { - match stage { - Stages::Initial => { - // Looking for one or more digits. - if ch.is_ascii_digit() { - freq_start = i; - stage = Stages::FoundFreqIntPart; - } - } - Stages::FoundFreqIntPart => { - // Looking for a decimal point. - if !ch.is_ascii_digit() { - if ch == b'.' { - stage = Stages::FoundFreqDecimalPoint; - } else { - stage = Stages::Initial; - } - } - } - Stages::FoundFreqDecimalPoint => { - // Looking for the decimal part. - if ch.is_ascii_digit() { - stage = Stages::FoundFreqDecimalPart; - decimal_start = i; - } else { - stage = Stages::Initial; - } - } - Stages::FoundFreqDecimalPart => { - // Looking for the unit of measure. - if !ch.is_ascii_digit() { - if ch == b'.' { - stage = Stages::FoundFreqDecimalPoint; - freq_start = decimal_start; - } else if ch == b'M' || ch == b'G' || ch == b'T' { - stage = Stages::FoundFreqUnitSize; - } else { - stage = Stages::Initial; - } - } - } - Stages::FoundFreqUnitSize => { - // Looking for the 'H' in 'Hz'. - if ch == b'H' { - stage = Stages::FoundH; - } else if ch.is_ascii_digit() { - stage = Stages::FoundFreqIntPart; - freq_start = i; - } else { - stage = Stages::Initial; - } - } - Stages::FoundH => { - // Looking for the 'z' in 'Hz'. - // If found, we stop the search and return the slice. - if ch == b'z' { - let freq_end = i + 1; - return Some(&self.as_bytes()[freq_start..freq_end]); - } else if ch.is_ascii_digit() { - stage = Stages::FoundFreqIntPart; - freq_start = i; - } else { - stage = Stages::Initial; - } - } - }; - } - None - } -} - -/// Find the position of the first null-terminator (\b'0') in a u8 slice, -/// or return 0 if they're all null-terminators. -/// This function is roughly equivalent to libc strlen. -fn null_terminator_index(slice: &[u8]) -> usize { - slice - .iter() - .rposition(|&b| b != b'\0') - .map_or(0, |idx| idx + 1) -} - -#[cfg(test)] -mod tests { - #![allow(clippy::undocumented_unsafe_blocks)] - use std::iter::repeat; - - use super::*; - - #[test] - fn test_brand_string() { - #[inline] - fn pack_u32(src: &[u8]) -> u32 { - assert!(src.len() >= 4); - u32::from(src[0]) - | (u32::from(src[1]) << 8) - | (u32::from(src[2]) << 16) - | (u32::from(src[3]) << 24) - } - - const TEST_STR: &[u8] = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - let mut bstr = BrandString::from_bytes_unchecked(TEST_STR); - - // Test the immutable bitwise casts - // - { - for i in 0_usize..=1_usize { - let eax_offs = (4 * 4) * i; - let ebx_offs = (4 * 4) * i + 4; - let ecx_offs = (4 * 4) * i + 8; - let edx_offs = (4 * 4) * i + 12; - assert_eq!( - bstr.get_reg_for_leaf(0x8000_0002 + i as u32, Reg::Eax), - pack_u32(&TEST_STR[eax_offs..(eax_offs + 4)]) - ); - assert_eq!( - bstr.get_reg_for_leaf(0x8000_0002 + i as u32, Reg::Ebx), - pack_u32(&TEST_STR[ebx_offs..(ebx_offs + 4)]) - ); - assert_eq!( - bstr.get_reg_for_leaf(0x8000_0002 + i as u32, Reg::Ecx), - pack_u32(&TEST_STR[ecx_offs..(ecx_offs + 4)]) - ); - assert_eq!( - bstr.get_reg_for_leaf(0x8000_0002 + i as u32, Reg::Edx), - pack_u32(&TEST_STR[edx_offs..(edx_offs + 4)]) - ); - } - } - - // Test find_freq() failure path - // - assert!(bstr.find_freq().is_none()); - - // Test mutable bitwise casting and finding the frequency substring - // - bstr.set_reg_for_leaf(0x8000_0003, Reg::Ebx, pack_u32(b"5.20")); - bstr.set_reg_for_leaf(0x8000_0003, Reg::Ecx, pack_u32(b"GHz ")); - assert_eq!(bstr.find_freq().unwrap(), b"5.20GHz"); - - let _overflow: [u8; 50] = [b'a'; 50]; - - // Test BrandString::check_push() - // - bstr = BrandString::new(); - assert!(bstr.check_push(b"Hello")); - bstr.push_bytes(b"Hello").unwrap(); - assert!(bstr.check_push(b", world!")); - bstr.push_bytes(b", world!").unwrap(); - - assert!(!bstr.check_push(&_overflow)); - - // Test BrandString::push_bytes() - // - let actual_len = bstr.as_bytes().len(); - let mut old_bytes: Vec = repeat(0).take(actual_len).collect(); - old_bytes.copy_from_slice(bstr.as_bytes()); - assert_eq!( - bstr.push_bytes(&_overflow), - Err(Error::Overflow( - "Appending to the brand string failed.".to_string() - )) - ); - assert!(bstr.as_bytes().to_vec() == old_bytes); - - // Test BrandString::from_host_cpuid() and get_reg_for_leaf() - // - match BrandString::from_host_cpuid() { - Ok(bstr) => { - for leaf in 0x8000_0002..=0x8000_0004_u32 { - let host_regs = unsafe { host_cpuid(leaf) }; - assert_eq!(bstr.get_reg_for_leaf(leaf, Reg::Eax), host_regs.eax); - assert_eq!(bstr.get_reg_for_leaf(leaf, Reg::Ebx), host_regs.ebx); - assert_eq!(bstr.get_reg_for_leaf(leaf, Reg::Ecx), host_regs.ecx); - assert_eq!(bstr.get_reg_for_leaf(leaf, Reg::Edx), host_regs.edx); - } - } - Err(Error::NotSupported) => { - // from_host_cpuid() should only fail if the host CPU doesn't support - // CPUID leaves up to 0x80000004, so let's make sure that's what happened. - let host_regs = unsafe { host_cpuid(0x8000_0000) }; - assert!(host_regs.eax < 0x8000_0004); - } - _ => panic!("This function should not return another type of error"), - } - - // Test BrandString::from_vendor_id() - let bstr = BrandString::from_vendor_id(VENDOR_ID_INTEL); - assert!(bstr.as_bytes().starts_with(BRAND_STRING_INTEL)); - let bstr = BrandString::from_vendor_id(VENDOR_ID_AMD); - assert!(bstr.as_bytes().starts_with(BRAND_STRING_AMD)); - let bstr = BrandString::from_vendor_id(b"............"); - assert!(bstr.as_bytes() == vec![b'\0'; 48].as_slice()); - } - - #[test] - /// Prevent against https://github.com/firecracker-microvm/firecracker/issues/2914 - fn test_null_terminator_index() { - let bytes = vec![b'\0'; 48]; - assert_eq!(null_terminator_index(bytes.as_slice()), 0); - - let bytes = vec![b'h', b'i', b'\0']; - assert_eq!(null_terminator_index(bytes.as_slice()), 2); - - let bytes = vec![b'h', b'i', b'\0', b'\0']; - assert_eq!(null_terminator_index(bytes.as_slice()), 2); - } - - #[test] - fn test_find_freq_fails() { - let bstr_thz = BrandString::from_bytes_unchecked(b"5.20THz"); - assert_eq!(bstr_thz.find_freq().unwrap(), b"5.20THz"); - - let bstr_unused_end = BrandString::from_bytes_unchecked(b"AAA5.20MHzXz"); - assert_eq!(bstr_unused_end.find_freq().unwrap(), b"5.20MHz"); - - let bstr_faulty_unit = BrandString::from_bytes_unchecked(b"5.20BHz "); - assert!(bstr_faulty_unit.find_freq().is_none()); - - let short_bstr = BrandString::from_bytes_unchecked(b"z"); - assert!(short_bstr.find_freq().is_none()); - - let skip_from_unit = BrandString::from_bytes_unchecked(b"Mz"); - assert!(skip_from_unit.find_freq().is_none()); - - let short_bstr = BrandString::from_bytes_unchecked(b"Hz"); - assert!(short_bstr.find_freq().is_none()); - - let short_bstr = BrandString::from_bytes_unchecked(b"GHz"); - assert!(short_bstr.find_freq().is_none()); - - let multiple_points_bstr = BrandString::from_bytes_unchecked(b"50.5.20GHz"); - assert_eq!(multiple_points_bstr.find_freq().unwrap(), b"5.20GHz"); - - let no_decimal_bstr = BrandString::from_bytes_unchecked(b"5GHz"); - assert!(no_decimal_bstr.find_freq().is_none()); - - let interrupted_bstr = BrandString::from_bytes_unchecked(b"500.00M5.20GHz"); - assert_eq!(interrupted_bstr.find_freq().unwrap(), b"5.20GHz"); - - let split_bstr = BrandString::from_bytes_unchecked(b"5.30AMHz"); - assert!(split_bstr.find_freq().is_none()); - - let long_bstr = BrandString::from_bytes_unchecked(b"1.12bc5.30MaHz2.4.25THz"); - assert_eq!(long_bstr.find_freq().unwrap(), b"4.25THz"); - - let found_h_bstr = BrandString::from_bytes_unchecked(b"1.A5.2MH3.20GHx4.30GHz"); - assert_eq!(found_h_bstr.find_freq().unwrap(), b"4.30GHz"); - } -} diff --git a/src/cpuid/src/common.rs b/src/cpuid/src/common.rs index 78da507689c..76940a0af31 100644 --- a/src/cpuid/src/common.rs +++ b/src/cpuid/src/common.rs @@ -1,77 +1,54 @@ // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 - -#[cfg(target_arch = "x86")] -use std::arch::x86::{CpuidResult, __cpuid_count, __get_cpuid_max}; -#[cfg(target_arch = "x86_64")] -use std::arch::x86_64::{CpuidResult, __cpuid_count, __get_cpuid_max}; - -#[cfg(target_arch = "x86_64")] -use kvm_bindings::CpuId; - -use crate::bit_helper::BitHelper; -use crate::cpu_leaf::*; - -/// Intel brand string. -pub const VENDOR_ID_INTEL: &[u8; 12] = b"GenuineIntel"; -/// AMD brand string. -pub const VENDOR_ID_AMD: &[u8; 12] = b"AuthenticAMD"; - -/// cpuid related error. -#[derive(Clone, Debug, thiserror::Error, PartialEq, Eq)] -pub enum Error { - /// The function was called with invalid parameters. - #[error("The function was called with invalid parameters.")] - InvalidParameters(String), - /// Function not supported on the current architecture. - #[error("Function not supported on the current architecture.")] - NotSupported, +#![allow(clippy::restriction)] + +#[cfg(cpuid)] +use super::CpuidTrait; + +/// Error type for [`get_cpuid`]. +#[cfg(cpuid)] +#[derive(Debug, thiserror::Error, PartialEq, Eq)] +pub enum GetCpuidError { + /// Invalid leaf. + #[error("Un-supported leaf: {0}")] + UnsupportedLeaf(u32), + /// Invalid subleaf. + #[error("Invalid subleaf: {0}")] + InvalidSubleaf(u32), } /// Extract entry from the cpuid. -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -pub fn get_cpuid(function: u32, count: u32) -> Result { - // TODO: Use `core::arch::x86_64::has_cpuid` - // (https://github.com/firecracker-microvm/firecracker/issues/3271) - #[cfg(target_env = "sgx")] - { - return Err(Error::NotSupported); - } - // For x86 the host supports the `cpuid` instruction if SSE is enabled. Otherwise it's hard to - // check. - #[cfg(target_arch = "x86")] - { - #[cfg(not(target_feature = "sse"))] - { - return Err(Error::NotSupported); - } - } - - // SAFETY: this is safe because the host supports the `cpuid` instruction - let max_function = unsafe { __get_cpuid_max(function & leaf_0x80000000::LEAF_NUM).0 }; - if function > max_function { - return Err(Error::InvalidParameters(format!( - "Function not supported: 0x{:x}", - function - ))); +/// +/// # Errors +/// +/// - When the given `leaf` is more than `max_leaf` supported by CPUID. +/// - When the the CPUID leaf `sub-leaf` is invalid (all its register equal 0). +#[cfg(cpuid)] +pub fn get_cpuid(leaf: u32, subleaf: u32) -> Result { + let max_leaf = + // SAFETY: This is safe because the host supports the `cpuid` instruction + unsafe { std::arch::x86_64::__get_cpuid_max(leaf & 0x8000_0000).0 }; + if leaf > max_leaf { + return Err(GetCpuidError::UnsupportedLeaf(leaf)); } - // SAFETY: this is safe because the host supports the `cpuid` instruction - let entry = unsafe { __cpuid_count(function, count) }; + // SAFETY: This is safe because the host supports the `cpuid` instruction + let entry = unsafe { std::arch::x86_64::__cpuid_count(leaf, subleaf) }; if entry.eax == 0 && entry.ebx == 0 && entry.ecx == 0 && entry.edx == 0 { - return Err(Error::InvalidParameters(format!( - "Invalid count: {}", - count - ))); + return Err(GetCpuidError::InvalidSubleaf(subleaf)); } Ok(entry) } /// Extracts the CPU vendor id from leaf 0x0. -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -pub fn get_vendor_id_from_host() -> Result<[u8; 12], Error> { - // SAFETY: This is safe because the resulting type has a lower alignment requirement +/// +/// # Errors +/// +/// When CPUID leaf 0 is not supported. +#[cfg(cpuid)] +pub fn get_vendor_id_from_host() -> Result<[u8; 12], GetCpuidError> { + // SAFETY: Always safe. get_cpuid(0, 0).map(|vendor_entry| unsafe { std::mem::transmute::<[u32; 3], [u8; 12]>([ vendor_entry.ebx, @@ -81,197 +58,112 @@ pub fn get_vendor_id_from_host() -> Result<[u8; 12], Error> { }) } -/// Extracts the CPU vendor id from leaf 0x0. -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -pub fn get_vendor_id_from_cpuid(cpuid: &CpuId) -> Result<[u8; 12], Error> { - // Search for vendor id entry. - for entry in cpuid.as_slice().iter() { - if entry.function == 0 && entry.index == 0 { - let cpu_vendor_id: [u8; 12] = - // SAFETY: This is safe because the resulting type has a lower alignment requirement - unsafe { std::mem::transmute([entry.ebx, entry.edx, entry.ecx]) }; - return Ok(cpu_vendor_id); - } - } - - Err(Error::NotSupported) -} - -/// Validates that the provided CPUID belongs to a CPU of the same -/// model as the host's. -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -pub fn is_same_model(cpuid: &CpuId) -> bool { - // Try to get the vendor IDs from the host and the CPUID struct. - if let (Ok(host_vendor_id), Ok(cpuid_vendor_id)) = - (get_vendor_id_from_host(), get_vendor_id_from_cpuid(cpuid)) - { - // If the vendor IDs aren't the same, the CPUs are not identical. - if host_vendor_id != cpuid_vendor_id { - return false; - } - } else { - // This only fails when CPUID is not supported, in which case - // we can't tell if the CPUs are identical. - return false; - } - - // Try to get the feature information leaf from the host CPUID. - let host_feature_info_leaf = get_cpuid(leaf_0x1::LEAF_NUM, 0); - - // The relevant information for this comparison is in the EAX register. - let host_feature_info_leaf_eax = match host_feature_info_leaf { - Ok(leaf) => leaf.eax, - Err(_) => { - // If this fails, we can't tell if the CPUs are identical. - return false; - } - }; - - // Search for the entry for leaf0x1. - let feature_info_leaf = cpuid - .as_slice() - .iter() - .find(|entry| entry.function == leaf_0x1::LEAF_NUM); - - // The relevant information is in EAX. - let feature_info_leaf_eax = match feature_info_leaf { - Some(leaf) => leaf.eax, - None => { - // Fail fast if we can't retrieve the relevant - // information from CPUID. - return false; - } - }; - - // Validate that all of these properties are the same. - for elem in &[ - leaf_0x1::eax::EXTENDED_FAMILY_ID_BITRANGE, - leaf_0x1::eax::EXTENDED_PROCESSOR_MODEL_BITRANGE, - leaf_0x1::eax::PROCESSOR_FAMILY_BITRANGE, - leaf_0x1::eax::PROCESSOR_MODEL_BITRANGE, - leaf_0x1::eax::STEPPING_BITRANGE, - ] { - if feature_info_leaf_eax.read_bits_in_range(elem) - != host_feature_info_leaf_eax.read_bits_in_range(elem) - { - return false; - } +/// Error type for [`msrs_to_save_by_cpuid`]. +#[cfg(cpuid)] +#[derive(Debug, PartialEq, Eq, thiserror::Error)] +#[error("Leaf 0 not found in given `CpuId`.")] +pub struct Leaf0NotFoundInCpuid; + +/// Returns MSRs to be saved based on CPUID features that are enabled. +/// +/// # Errors +/// +/// When CPUID leaf 0 is not supported. +#[cfg(cpuid)] +pub fn msrs_to_save_by_cpuid( + cpuid: &kvm_bindings::CpuId, +) -> Result, Leaf0NotFoundInCpuid> { + let vendor_id = cpuid.manufacturer_id().ok_or(Leaf0NotFoundInCpuid)?; + match &vendor_id { + super::VENDOR_ID_INTEL => Ok(intel_msrs_to_save_by_cpuid(cpuid)), + // We don't have MSR-CPUID dependencies set for other vendors yet. + _ => Ok(std::collections::HashSet::new()), } - - true } -/// Scans through the CPUID and determines if a feature bit is set. -// TODO: This currently involves a linear search which would be improved -// when we'll refactor the cpuid crate. -#[macro_export] -macro_rules! cpuid_is_feature_set { - ($cpuid:ident, $leaf:expr, $index:expr, $reg:tt, $feature_bit:expr) => {{ - let mut res = false; - for entry in $cpuid.as_slice().iter() { - if entry.function == $leaf && entry.index == $index { - if entry.$reg & (1 << $feature_bit) != 0 { - res = true; - break; +/// Returns MSRs to be saved based on the Intel CPUID features that are enabled. +#[cfg(cpuid)] +#[must_use] +pub fn intel_msrs_to_save_by_cpuid(cpuid: &kvm_bindings::CpuId) -> std::collections::HashSet { + /// Memory Protection Extensions + const MPX_BITINDEX: u32 = 14; + + /// Memory Type Range Registers + const MTRR_BITINDEX: u32 = 12; + + /// Memory Check Exception + const MCE_BITINDEX: u32 = 7; + + /// Scans through the CPUID and determines if a feature bit is set. + // TODO: This currently involves a linear search which would be improved + // when we'll refactor the cpuid crate. + macro_rules! cpuid_is_feature_set { + ($cpuid:ident, $leaf:expr, $index:expr, $reg:tt, $feature_bit:expr) => {{ + let mut res = false; + for entry in $cpuid.as_slice().iter() { + if entry.function == $leaf && entry.index == $index { + if entry.$reg & (1 << $feature_bit) != 0 { + res = true; + break; + } } } - } - res - }}; -} - -#[cfg(test)] -pub mod tests { - use crate::common::*; - - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - pub fn get_topoext_fn() -> u32 { - let vendor_id = get_vendor_id_from_host(); - assert!(vendor_id.is_ok()); - let function = match &vendor_id.ok().unwrap() { - VENDOR_ID_INTEL => leaf_0x4::LEAF_NUM, - VENDOR_ID_AMD => leaf_0x8000001d::LEAF_NUM, - _ => 0, - }; - assert!(function != 0); - - function + res + }}; } - #[test] - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - fn test_get_cpu_id() { - // get_cpu_id should work correctly here - let topoext_fn = get_topoext_fn(); + let mut msrs = std::collections::HashSet::new(); - // check that get_cpuid works for valid parameters - match get_cpuid(topoext_fn, 0) { - Ok(topoext_entry) => { - assert!(topoext_entry.eax != 0); + // Macro used for easy definition of CPUID-MSR dependencies. + macro_rules! cpuid_msr_dep { + ($leaf:expr, $index:expr, $reg:tt, $feature_bit:expr, $msr:expr) => { + if cpuid_is_feature_set!(cpuid, $leaf, $index, $reg, $feature_bit) { + msrs.extend($msr) } - _ => panic!("Wrong behavior"), - } - - // check that get_cpuid returns correct error for invalid `function` - match get_cpuid(0x9000_0000, 0) { - Err(Error::InvalidParameters(s)) => { - assert!(s == "Function not supported: 0x90000000"); - } - _ => panic!("Wrong behavior"), - } - - // check that get_cpuid returns correct error for invalid `count` - match get_cpuid(topoext_fn, 100) { - Err(Error::InvalidParameters(s)) => { - assert!(s == "Invalid count: 100"); - } - _ => panic!("Wrong behavior"), - } - } - - #[test] - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - fn test_get_vendor_id() { - let vendor_id = get_vendor_id_from_host(); - assert!(vendor_id.is_ok()); - matches!(&vendor_id.ok().unwrap(), VENDOR_ID_INTEL | VENDOR_ID_AMD); + }; } - #[test] - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - fn test_is_same_model() { - let mut curr_cpuid = CpuId::new(2).unwrap(); - - // Add the vendor ID leaf. - let vendor = get_cpuid(0x0, 0).unwrap(); - curr_cpuid.as_mut_slice()[0].function = 0x0; - curr_cpuid.as_mut_slice()[0].index = 0; - curr_cpuid.as_mut_slice()[0].ebx = vendor.ebx; - curr_cpuid.as_mut_slice()[0].ecx = vendor.ecx; - curr_cpuid.as_mut_slice()[0].edx = vendor.edx; - - // Add the feature info leaf. - let feature_info = get_cpuid(0x1, 0).unwrap(); - curr_cpuid.as_mut_slice()[1].function = 0x1; - curr_cpuid.as_mut_slice()[1].index = 0; - curr_cpuid.as_mut_slice()[1].eax = feature_info.eax; - - assert!(is_same_model(&curr_cpuid)); - - let mut diff_vendor_cpuid = curr_cpuid.clone(); - for mut entry in diff_vendor_cpuid.as_mut_slice() { - if entry.function == 0x0 && entry.index == 0 { - entry.ebx = 0xFFFF_FFFF; - } - } - assert!(!is_same_model(&diff_vendor_cpuid)); - - let mut diff_feature_cpuid = curr_cpuid; - for mut entry in diff_feature_cpuid.as_mut_slice() { - if entry.function == 0x1 && entry.index == 0 { - entry.eax ^= 0x1; - } - } - assert!(!is_same_model(&diff_feature_cpuid)); - } + // TODO: Add more dependencies. + cpuid_msr_dep!( + 0x7, + 0, + ebx, + MPX_BITINDEX, + [arch_gen::x86::msr_index::MSR_IA32_BNDCFGS] + ); + + // IA32_MTRR_PHYSBASEn, IA32_MTRR_PHYSMASKn + cpuid_msr_dep!(0x1, 0, edx, MTRR_BITINDEX, 0x200..0x210); + + // Other MTRR MSRs + cpuid_msr_dep!( + 0x1, + 0, + edx, + MTRR_BITINDEX, + [ + 0x250, // IA32_MTRR_FIX64K_00000 + 0x258, // IA32_MTRR_FIX16K_80000 + 0x259, // IA32_MTRR_FIX16K_A0000 + 0x268, // IA32_MTRR_FIX4K_C0000 + 0x269, // IA32_MTRR_FIX4K_C8000 + 0x26a, // IA32_MTRR_FIX4K_D0000 + 0x26b, // IA32_MTRR_FIX4K_D8000 + 0x26c, // IA32_MTRR_FIX4K_E0000 + 0x26d, // IA32_MTRR_FIX4K_E8000 + 0x26e, // IA32_MTRR_FIX4K_F0000 + 0x26f, // IA32_MTRR_FIX4K_F8000 + 0x277, // IA32_PAT + 0x2ff // IA32_MTRR_DEF_TYPE + ] + ); + + // MCE MSRs + // We are saving 32 MCE banks here as this is the maximum number supported by KVM + // and configured by default. + // The physical number of the MCE banks depends on the CPU. + // The number of emulated MCE banks can be configured via KVM_X86_SETUP_MCE. + cpuid_msr_dep!(0x1, 0, edx, MCE_BITINDEX, 0x400..0x480); + + msrs } diff --git a/src/cpuid/src/cpu_leaf.rs b/src/cpuid/src/cpu_leaf.rs deleted file mode 100644 index 18944969b77..00000000000 --- a/src/cpuid/src/cpu_leaf.rs +++ /dev/null @@ -1,352 +0,0 @@ -// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -// Basic CPUID Information -pub mod leaf_0x1 { - pub const LEAF_NUM: u32 = 0x1; - - pub mod eax { - use crate::bit_helper::BitRange; - - pub const EXTENDED_FAMILY_ID_BITRANGE: BitRange = bit_range!(27, 20); - pub const EXTENDED_PROCESSOR_MODEL_BITRANGE: BitRange = bit_range!(19, 16); - pub const PROCESSOR_TYPE_BITRANGE: BitRange = bit_range!(13, 12); - pub const PROCESSOR_FAMILY_BITRANGE: BitRange = bit_range!(11, 8); - pub const PROCESSOR_MODEL_BITRANGE: BitRange = bit_range!(7, 4); - pub const STEPPING_BITRANGE: BitRange = bit_range!(3, 0); - } - - pub mod ebx { - use crate::bit_helper::BitRange; - - // The bit-range containing the (fixed) default APIC ID. - pub const APICID_BITRANGE: BitRange = bit_range!(31, 24); - // The bit-range containing the logical processor count. - pub const CPU_COUNT_BITRANGE: BitRange = bit_range!(23, 16); - // The bit-range containing the number of bytes flushed when executing CLFLUSH. - pub const CLFLUSH_SIZE_BITRANGE: BitRange = bit_range!(15, 8); - } - - pub mod ecx { - // DTES64 = 64-bit debug store - pub const DTES64_BITINDEX: u32 = 2; - // MONITOR = Monitor/MWAIT - pub const MONITOR_BITINDEX: u32 = 3; - // CPL Qualified Debug Store - pub const DS_CPL_SHIFT: u32 = 4; - // Virtual Machine Extensions - pub const VMX_BITINDEX: u32 = 5; - // 6 = SMX (Safer Mode Extensions) - pub const SMX_BITINDEX: u32 = 6; - // 7 = EIST (Enhanced Intel SpeedStepĀ® technology) - pub const EIST_BITINDEX: u32 = 7; - // TM2 = Thermal Monitor 2 - pub const TM2_BITINDEX: u32 = 8; - // CNXT_ID = L1 Context ID (L1 data cache can be set to adaptive/shared mode) - pub const CNXT_ID_BITINDEX: u32 = 10; - // SDBG (cpu supports IA32_DEBUG_INTERFACE MSR for silicon debug) - pub const SDBG_BITINDEX: u32 = 11; - pub const FMA_BITINDEX: u32 = 12; - // XTPR_UPDATE = xTPR Update Control - pub const XTPR_UPDATE_BITINDEX: u32 = 14; - // PDCM = Perfmon and Debug Capability - pub const PDCM_BITINDEX: u32 = 15; - // 18 = DCA Direct Cache Access (prefetch data from a memory mapped device) - pub const DCA_BITINDEX: u32 = 18; - pub const MOVBE_BITINDEX: u32 = 22; - pub const TSC_DEADLINE_TIMER_BITINDEX: u32 = 24; - pub const OSXSAVE_BITINDEX: u32 = 27; - // Cpu is running on a hypervisor. - pub const HYPERVISOR_BITINDEX: u32 = 31; - } - - pub mod edx { - pub const MCE_BITINDEX: u32 = 7; // Memory Check Exception - pub const MTRR_BITINDEX: u32 = 12; // Memory Type Range Registers - pub const PSN_BITINDEX: u32 = 18; // Processor Serial Number - pub const SSE42_BITINDEX: u32 = 20; // SSE 4.2 - pub const DS_BITINDEX: u32 = 21; // Debug Store. - pub const ACPI_BITINDEX: u32 = 22; // Thermal Monitor and Software Controlled Clock Facilities. - pub const SS_BITINDEX: u32 = 27; // Self Snoop - pub const HTT_BITINDEX: u32 = 28; // Max APIC IDs reserved field is valid - pub const TM_BITINDEX: u32 = 29; // Thermal Monitor. - pub const IA64_BITINDEX: u32 = 30; // IA64 processor emulating x86 - pub const PBE_BITINDEX: u32 = 31; // Pending Break Enable. - } -} - -pub mod leaf_cache_parameters { - pub mod eax { - use crate::bit_helper::BitRange; - - pub const CACHE_LEVEL_BITRANGE: BitRange = bit_range!(7, 5); - pub const MAX_CPUS_PER_CORE_BITRANGE: BitRange = bit_range!(25, 14); - } -} - -// Deterministic Cache Parameters Leaf -pub mod leaf_0x4 { - pub const LEAF_NUM: u32 = 0x4; - - pub mod eax { - use crate::bit_helper::BitRange; - // inherit eax from leaf_cache_parameters - pub use crate::cpu_leaf::leaf_cache_parameters::eax::*; - - pub const MAX_CORES_PER_PACKAGE_BITRANGE: BitRange = bit_range!(31, 26); - } -} - -// Thermal and Power Management Leaf -pub mod leaf_0x6 { - pub const LEAF_NUM: u32 = 0x6; - - pub mod eax { - pub const TURBO_BOOST_BITINDEX: u32 = 1; - } - - pub mod ecx { - // "Energy Performance Bias" bit. - pub const EPB_BITINDEX: u32 = 3; - } -} - -// Structured Extended Feature Flags Enumeration Leaf -pub mod leaf_0x7 { - pub const LEAF_NUM: u32 = 0x7; - - pub mod index0 { - pub mod ebx { - // 1 = TSC_ADJUST - pub const SGX_BITINDEX: u32 = 2; - pub const BMI1_BITINDEX: u32 = 3; - pub const HLE_BITINDEX: u32 = 4; - pub const AVX2_BITINDEX: u32 = 5; - // FPU Data Pointer updated only on x87 exceptions if 1. - pub const FPDP_BITINDEX: u32 = 6; - // 7 = SMEP (Supervisor-Mode Execution Prevention if 1) - pub const BMI2_BITINDEX: u32 = 8; - // 9 = Enhanced REP MOVSB/STOSB if 1 - // 10 = INVPCID - pub const INVPCID_BITINDEX: u32 = 10; - pub const RTM_BITINDEX: u32 = 11; - // IntelĀ® Resource Director Technology (IntelĀ® RDT) Monitoring - pub const RDT_M_BITINDEX: u32 = 12; - // 13 = Deprecates FPU CS and FPU DS values if 1 - pub const FPU_CS_DS_DEPRECATE_BITINDEX: u32 = 13; - // Memory Protection Extensions - pub const MPX_BITINDEX: u32 = 14; - // RDT = IntelĀ® Resource Director Technology - pub const RDT_A_BITINDEX: u32 = 15; - // AVX-512 Foundation instructions - pub const AVX512F_BITINDEX: u32 = 16; - // AVX-512 Doubleword and Quadword Instructions - pub const AVX512DQ_BITINDEX: u32 = 17; - pub const RDSEED_BITINDEX: u32 = 18; - pub const ADX_BITINDEX: u32 = 19; - // 20 = SMAP (Supervisor-Mode Access Prevention) - // AVX512IFMA = AVX-512 Integer Fused Multiply-Add Instructions - pub const AVX512IFMA_BITINDEX: u32 = 21; - // 22 = PCOMMIT intruction - pub const PCOMMIT_BITINDEX: u32 = 22; - // CLFLUSHOPT (flushing multiple cache lines in parallel within a single logical - // processor) - pub const CLFLUSHOPT_BITINDEX: u32 = 23; - // CLWB = Cache Line Write Back - pub const CLWB_BITINDEX: u32 = 24; - // PT = Intel Processor Trace - pub const PT_BITINDEX: u32 = 25; - // AVX512PF = AVX512 Prefetch Instructions - pub const AVX512PF_BITINDEX: u32 = 26; - // AVX512ER = AVX-512 Exponential and Reciprocal Instructions - pub const AVX512ER_BITINDEX: u32 = 27; - // AVX512CD = AVX-512 Conflict Detection Instructions - pub const AVX512CD_BITINDEX: u32 = 28; - // Intel Secure Hash Algorithm Extensions - pub const SHA_BITINDEX: u32 = 29; - // AVX-512 Byte and Word Instructions - pub const AVX512BW_BITINDEX: u32 = 30; - // AVX-512 Vector Length Extensions - pub const AVX512VL_BITINDEX: u32 = 31; - } - - pub mod ecx { - // 0 = PREFETCHWT1 (move data closer to the processor in anticipation of future use) - // AVX512_VBMI = AVX-512 Vector Byte Manipulation Instructions - pub const AVX512_VBMI_BITINDEX: u32 = 1; - // UMIP (User Mode Instruction Prevention) - pub const UMIP_BITINDEX: u32 = 2; - // PKU = Protection Keys for user-mode pages - pub const PKU_BITINDEX: u32 = 3; - // OSPKE = If 1, OS has set CR4.PKE to enable protection keys - pub const OSPKE_BITINDEX: u32 = 4; - // 5 = WAITPKG - // 6 = AVX512_VBMI2 - // 7 reserved - // 8 = GFNI - // 9 = VAES - // 10 = VPCLMULQDQ - // AVX512_VNNI = Vector Neural Network Instructions - pub const AVX512_VNNI_BITINDEX: u32 = 11; - // 12 = AVX512_BITALG - // 13 = TME - // AVX512_VPOPCNTDQ = Vector population count instruction (IntelĀ® Xeon Phiā„¢ only.) - pub const AVX512_VPOPCNTDQ_BITINDEX: u32 = 14; - // LA57 = 5-level page tables. - pub const LA57: u32 = 16; - // 21 - 17 = The value of MAWAU used by the BNDLDX and BNDSTX instructions in 64-bit - // mode. Read Processor ID - pub const RDPID_BITINDEX: u32 = 22; - // 23 - 29 reserved - // SGX_LC = SGX Launch Configuration - pub const SGX_LC_BITINDEX: u32 = 30; - // 31 reserved - } - - pub mod edx { - // AVX-512 4-register Neural Network Instructions - pub const AVX512_4VNNIW_BITINDEX: u32 = 2; - // AVX-512 4-register Multiply Accumulation Single precision - pub const AVX512_4FMAPS_BITINDEX: u32 = 3; - pub const ARCH_CAPABILITIES_BITINDEX: u32 = 29; - } - } -} - -pub mod leaf_0xa { - pub const LEAF_NUM: u32 = 0xa; -} - -// Extended Topology Leaf -pub mod leaf_0xb { - pub const LEAF_NUM: u32 = 0xb; - - pub const LEVEL_TYPE_THREAD: u32 = 1; - pub const LEVEL_TYPE_CORE: u32 = 2; - - pub mod eax { - use crate::bit_helper::BitRange; - - // 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 = bit_range!(4, 0); - } - - pub mod ebx { - use crate::bit_helper::BitRange; - - // The bit-range containing the number of factory-configured logical processors - // at the current cache level - pub const NUM_LOGICAL_PROCESSORS_BITRANGE: BitRange = bit_range!(15, 0); - } - - pub mod ecx { - use crate::bit_helper::BitRange; - - pub const LEVEL_TYPE_BITRANGE: BitRange = bit_range!(15, 8); - pub const LEVEL_NUMBER_BITRANGE: BitRange = bit_range!(7, 0); - } -} - -// Processor Extended State Enumeration Sub-leaves -pub mod leaf_0xd { - pub const LEAF_NUM: u32 = 0xd; - - pub mod index0 { - pub mod eax { - use crate::bit_helper::BitRange; - - pub const MPX_STATE_BITRANGE: BitRange = bit_range!(4, 3); - pub const AVX512_STATE_BITRANGE: BitRange = bit_range!(7, 5); - pub const PKRU_BITINDEX: u32 = 9; - } - } - - pub mod index1 { - pub mod eax { - pub const XSAVEC_SHIFT: u32 = 1; - pub const XGETBV_SHIFT: u32 = 2; - pub const XSAVES_SHIFT: u32 = 3; - } - } -} - -pub mod leaf_0x80000000 { - pub const LEAF_NUM: u32 = 0x8000_0000; - - pub mod eax { - use crate::bit_helper::BitRange; - - pub const LARGEST_EXTENDED_FN_BITRANGE: BitRange = bit_range!(31, 0); - } -} - -pub mod leaf_0x80000001 { - pub const LEAF_NUM: u32 = 0x8000_0001; - - pub mod ecx { - pub const TOPOEXT_INDEX: u32 = 22; - pub const PREFETCH_BITINDEX: u32 = 8; // 3DNow! PREFETCH/PREFETCHW instructions - pub const LZCNT_BITINDEX: u32 = 5; // advanced bit manipulation - } - - pub mod edx { - pub const PDPE1GB_BITINDEX: u32 = 26; // 1-GByte pages are available if 1. - } -} - -pub mod leaf_0x80000008 { - pub const LEAF_NUM: u32 = 0x8000_0008; - - pub mod ecx { - use crate::bit_helper::BitRange; - - // The number of bits in the initial ApicId value that indicate thread ID within a package - // Possible values: - // 0-5 -> Reserved - // 6 -> up to 64 threads - // 7 -> up to 128 threads - pub const THREAD_ID_SIZE_BITRANGE: BitRange = bit_range!(15, 12); - // The number of threads in the package - 1 - pub const NUM_THREADS_BITRANGE: BitRange = bit_range!(7, 0); - } -} - -// Extended Cache Topology Leaf -pub mod leaf_0x8000001d { - pub const LEAF_NUM: u32 = 0x8000_001d; - - // inherit eax from leaf_cache_parameters - pub use crate::cpu_leaf::leaf_cache_parameters::eax; -} - -// Extended APIC ID Leaf -pub mod leaf_0x8000001e { - pub const LEAF_NUM: u32 = 0x8000_001e; - - pub mod eax { - use crate::bit_helper::BitRange; - - pub const EXTENDED_APIC_ID_BITRANGE: BitRange = bit_range!(31, 0); - } - - pub mod ebx { - use crate::bit_helper::BitRange; - - // The number of threads per core - 1 - pub const THREADS_PER_CORE_BITRANGE: BitRange = bit_range!(15, 8); - pub const CORE_ID_BITRANGE: BitRange = bit_range!(7, 0); - } - - pub mod ecx { - use crate::bit_helper::BitRange; - - // The number of nodes per processor. Possible values: - // 0 -> 1 node per processor - // 1 -> 2 nodes per processor - // 2 -> Reserved - // 3 -> 4 nodes per processor - pub const NODES_PER_PROCESSOR_BITRANGE: BitRange = bit_range!(10, 8); - pub const NODE_ID_BITRANGE: BitRange = bit_range!(7, 0); - } -} diff --git a/src/cpuid/src/cpuid_ffi.rs b/src/cpuid/src/cpuid_ffi.rs new file mode 100644 index 00000000000..4bc98b953e4 --- /dev/null +++ b/src/cpuid/src/cpuid_ffi.rs @@ -0,0 +1,610 @@ +// Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +use std::alloc::Layout; +use std::cmp::{Eq, PartialEq}; +use std::convert::TryFrom; +use std::fmt; +use std::marker::PhantomData; +use std::mem::{size_of, transmute, MaybeUninit}; +use std::ops::{Deref, DerefMut}; +use std::ptr::NonNull; + +use serde::{Deserialize, Serialize}; + +use super::{CpuidEntry, CpuidKey}; + +/// Mimic of the currently unstable +/// [`Vec::into_raw_parts`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.into_raw_parts) +/// . +fn vec_into_raw_parts(v: Vec) -> (*mut T, usize, usize) { + let mut me = std::mem::ManuallyDrop::new(v); + (me.as_mut_ptr(), me.len(), me.capacity()) +} + +/// A rusty mimic of +/// [`kvm_cpuid`](https://elixir.bootlin.com/linux/v5.10.129/source/arch/x86/include/uapi/asm/kvm.h#L226) +/// . +/// +/// [`RawCpuid`] has an identical memory layout to +/// [`kvm_cpuid`](https://elixir.bootlin.com/linux/v5.10.129/source/arch/x86/include/uapi/asm/kvm.h#L226) +/// . +/// +/// This allows [`RawCpuid`] to function as a simpler replacement for [`kvm_bindings::CpuId`]. In +/// the future it may replace [`kvm_bindings::CpuId`] fully. +/// +/// For implementation details see . +#[derive(Debug)] +#[repr(C)] +pub struct RawCpuid { + /// Number of entries. + nent: u32, + /// Padding. + padding: Padding<{ size_of::() }>, + /// Pointer to entries. + entries: NonNull, + /// Marker type. + _marker: PhantomData, +} + +/// Error type for [`RawCpuid::resize`]. +#[derive(Debug, PartialEq, Eq, thiserror::Error)] +#[error("Failed to resize: {0}")] +pub struct RawCpuidResizeError(std::alloc::LayoutError); + +impl super::CpuidTrait for RawCpuid { + /// Gets a given sub-leaf. + #[allow(clippy::transmute_ptr_to_ptr)] + #[inline] + fn get(&self, CpuidKey { leaf, subleaf }: &CpuidKey) -> Option<&CpuidEntry> { + let entry_opt = self + .iter() + .find(|entry| entry.function == *leaf && entry.index == *subleaf); + + entry_opt.map(|entry| { + // SAFETY: The `RawKvmCpuidEntry` and `CpuidEntry` are `repr(C)` with known sizes. + unsafe { + let arr: &[u8; size_of::()] = transmute(entry); + let arr2: &[u8; size_of::()] = + arr.get_unchecked(8..28).try_into().unwrap_unchecked(); + transmute::<_, &CpuidEntry>(arr2) + } + }) + } + + /// Gets a given sub-leaf. + #[allow(clippy::transmute_ptr_to_ptr)] + #[inline] + fn get_mut(&mut self, CpuidKey { leaf, subleaf }: &CpuidKey) -> Option<&mut CpuidEntry> { + let entry_opt = self + .iter_mut() + .find(|entry| entry.function == *leaf && entry.index == *subleaf); + entry_opt.map(|entry| { + // SAFETY: The `RawKvmCpuidEntry` and `CpuidEntry` are `repr(C)` with known sizes. + unsafe { + let arr: &mut [u8; size_of::()] = transmute(entry); + let arr2: &mut [u8; size_of::()] = + arr.get_unchecked_mut(8..28).try_into().unwrap_unchecked(); + transmute::<_, &mut CpuidEntry>(arr2) + } + }) + } +} + +impl RawCpuid { + /// Alias for [`RawCpuid::default()`]. + #[inline] + #[must_use] + pub fn new() -> Self { + Self::default() + } + /// Returns number of elements. + #[inline] + #[must_use] + pub fn nent(&self) -> u32 { + self.nent + } + /// Returns a reference to an entry for a given leaf (function) and sub-leaf (index). + /// + /// Returning `None` if it is not present. + #[inline] + #[must_use] + pub fn get(&self, leaf: u32, sub_leaf: u32) -> Option<&RawKvmCpuidEntry> { + self.iter() + .find(|entry| entry.function == leaf && entry.index == sub_leaf) + } + /// Returns a mutable reference entry for a given leaf (function) and sub-leaf (index). + /// + /// Returning `None` if it is not present. + #[inline] + #[must_use] + pub fn get_mut(&mut self, leaf: u32, sub_leaf: u32) -> Option<&mut RawKvmCpuidEntry> { + self.iter_mut() + .find(|entry| entry.function == leaf && entry.index == sub_leaf) + } + /// Resizes allocated memory. + /// + /// # Errors + /// + /// When failing to construct a layout. + #[allow(clippy::cast_ptr_alignment, clippy::else_if_without_else)] + fn resize(&mut self, n: u32) -> Result<(), RawCpuidResizeError> { + // alloc + if self.nent == 0 && n > 0 { + // SAFETY: `usize` will always be at least 32 bits, thus `u32` can always be safely + // converted into it. + let new_len = unsafe { usize::try_from(n).unwrap_unchecked() }; + + let new_layout = + Layout::array::(new_len).map_err(RawCpuidResizeError)?; + + // SAFETY: Always safe. + let new_ptr = unsafe { std::alloc::alloc(new_layout) }; + self.entries = match NonNull::new(new_ptr.cast::()) { + Some(p) => p, + None => std::alloc::handle_alloc_error(new_layout), + }; + } + // realloc + else if self.nent > 0 && n > 0 { + // SAFETY: `usize` will always be at least 32 bits, thus `u32` can always be safely + // converted into it. + let new_len = unsafe { usize::try_from(n).unwrap_unchecked() }; + + let new_layout = + Layout::array::(new_len).map_err(RawCpuidResizeError)?; + + // SAFETY: `usize` will always be at least 32 bits, thus `u32` can always be safely + // converted into it. + let len = unsafe { usize::try_from(self.nent).unwrap_unchecked() }; + + let old_layout = Layout::array::(len).map_err(RawCpuidResizeError)?; + let old_ptr = self.entries.as_ptr().cast::(); + // SAFETY: Always safe. + let new_ptr = unsafe { std::alloc::realloc(old_ptr, old_layout, new_layout.size()) }; + + self.entries = match NonNull::new(new_ptr.cast::()) { + Some(p) => p, + None => std::alloc::handle_alloc_error(new_layout), + }; + } + // dealloc + else if self.nent > 0 && n == 0 { + // SAFETY: `usize` will always be at least 32 bits, thus `u32` can always be safely + // converted into it. + let len = unsafe { usize::try_from(self.nent).unwrap_unchecked() }; + + let old_layout = Layout::array::(len).map_err(RawCpuidResizeError)?; + let old_ptr = self.entries.as_ptr().cast::(); + // SAFETY: Always safe. + unsafe { std::alloc::dealloc(old_ptr, old_layout) }; + self.entries = NonNull::dangling(); + } + self.nent = n; + Ok(()) + } + + /// Pushes entry onto end. + /// + /// # Errors + /// + /// On resize failure. + #[allow(clippy::integer_arithmetic, clippy::arithmetic_side_effects)] + #[inline] + pub fn push(&mut self, entry: RawKvmCpuidEntry) -> Result<(), RawCpuidResizeError> { + self.resize(self.nent + 1)?; + + // SAFETY: `usize` will always be at least 32 bits, thus `u32` can always be safely + // converted into it. + let len = unsafe { usize::try_from(self.nent).unwrap_unchecked() }; + + // SAFETY: Always safe. + unsafe { + std::ptr::write(self.entries.as_ptr().add(len), entry); + } + Ok(()) + } + /// Pops entry from end. + /// + /// # Panics + /// + /// On allocation failure. + #[inline] + pub fn pop(&mut self) -> Option { + (self.nent > 0).then(|| { + // SAFETY: `usize` will always be at least 32 bits, thus `u32` can always be safely + // converted into it. + let len = unsafe { usize::try_from(self.nent).unwrap_unchecked() }; + // SAFETY: When `self.entries.as_ptr().add(u_nent)` contains a valid value. + let rtn = unsafe { std::ptr::read(self.entries.as_ptr().add(len)) }; + + // SAFETY: We check before `self.nent > 0` therefore unwrapping here is safe. + let new_nent = unsafe { self.nent.checked_sub(1).unwrap_unchecked() }; + + // Since we are decreasing the size `resize` should never panic. + #[allow(clippy::unwrap_used)] + self.resize(new_nent).unwrap(); + + rtn + }) + } +} + +impl Clone for RawCpuid { + #[allow(clippy::indexing_slicing)] + #[inline] + fn clone(&self) -> Self { + let mut new_raw_cpuid = Self::new(); + + // Since we are cloning an existing structure with this size, we can presume resizing to + // this size is safe. + #[allow(clippy::unwrap_used)] + new_raw_cpuid.resize(self.nent).unwrap(); + + for i in 0..self.nent { + // SAFETY: `usize` will always be at least 32 bits, thus `u32` can always be safely + // converted into it. + let index = unsafe { usize::try_from(i).unwrap_unchecked() }; + + new_raw_cpuid[index] = self[index].clone(); + } + + new_raw_cpuid + } +} + +impl serde::Serialize for RawCpuid { + #[allow(clippy::indexing_slicing)] + #[inline] + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + use serde::ser::SerializeSeq; + // SAFETY: `usize` will always be at least 32 bits, thus `u32` can always be safely + // converted into it. + let len = unsafe { usize::try_from(self.nent).unwrap_unchecked() }; + let mut seq = serializer.serialize_seq(Some(len))?; + for i in 0..len { + seq.serialize_element(&self[i])?; + } + seq.end() + } +} + +/// Unit struct used in the `serde::de::Visitor` implementation of `RawCpuid`. +struct RawCpuidVisitor; + +impl<'de> serde::de::Visitor<'de> for RawCpuidVisitor { + type Value = RawCpuid; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("Expected sequence of RawKvmCpuidEntry") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: serde::de::SeqAccess<'de>, + { + let mut entries = Vec::new(); + while let Some(next) = seq.next_element::()? { + entries.push(next); + } + Ok(Self::Value::from(entries)) + } +} + +impl<'de> serde::Deserialize<'de> for RawCpuid { + #[inline] + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_seq(RawCpuidVisitor) + } +} + +impl PartialEq for RawCpuid { + #[allow(clippy::indexing_slicing)] + #[inline] + fn eq(&self, other: &Self) -> bool { + if self.nent == other.nent { + // SAFETY: `usize` will always be at least 32 bits, thus `u32` can always be safely + // converted into it. + let n = unsafe { usize::try_from(self.nent).unwrap_unchecked() }; + for i in 0..n { + if self[i] != other[i] { + return false; + } + } + true + } else { + false + } + } +} + +impl Eq for RawCpuid {} + +// SAFETY: Always safe. +unsafe impl Send for RawCpuid {} + +// SAFETY: Always safe. +unsafe impl Sync for RawCpuid {} + +impl Default for RawCpuid { + #[inline] + fn default() -> Self { + Self { + nent: 0, + padding: Padding::default(), + entries: NonNull::dangling(), + _marker: PhantomData, + } + } +} + +// We implement custom drop which drops all entries using `self.nent` +impl Drop for RawCpuid { + #[allow(clippy::unwrap_used)] + #[inline] + fn drop(&mut self) { + if self.nent != 0 { + // SAFETY: Always safe. + unsafe { + std::alloc::dealloc( + self.entries.as_ptr().cast::(), + Layout::array::(usize::try_from(self.nent).unwrap()).unwrap(), + ); + } + } + } +} + +impl Deref for RawCpuid { + type Target = [RawKvmCpuidEntry]; + + #[allow(clippy::unwrap_used)] + #[inline] + fn deref(&self) -> &Self::Target { + // SAFETY: Always safe. + unsafe { + std::slice::from_raw_parts(self.entries.as_ptr(), usize::try_from(self.nent).unwrap()) + } + } +} + +impl DerefMut for RawCpuid { + #[allow(clippy::unwrap_used)] + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + // SAFETY: Always safe. + unsafe { + std::slice::from_raw_parts_mut( + self.entries.as_ptr(), + usize::try_from(self.nent).unwrap(), + ) + } + } +} + +#[cfg(cpuid)] +impl From for RawCpuid { + #[allow(clippy::unwrap_used)] + #[inline] + fn from(value: kvm_bindings::CpuId) -> Self { + // As cannot acquire ownership of the underlying slice, we clone it. + let cloned = value.as_slice().to_vec(); + let (ptr, len, _cap) = vec_into_raw_parts(cloned); + Self { + nent: u32::try_from(len).unwrap(), + padding: Padding::default(), + entries: NonNull::new(ptr.cast::()).unwrap(), + _marker: PhantomData, + } + } +} + +impl From> for RawCpuid { + #[allow(clippy::unwrap_used)] + #[inline] + fn from(vec: Vec) -> Self { + let (ptr, len, _cap) = vec_into_raw_parts(vec); + Self { + nent: u32::try_from(len).unwrap(), + padding: Padding::default(), + entries: NonNull::new(ptr.cast::()).unwrap(), + _marker: PhantomData, + } + } +} + +impl FromIterator for RawCpuid { + #[inline] + fn from_iter(iter: T) -> Self + where + T: IntoIterator, + { + let vec = iter.into_iter().collect::>(); + Self::from(vec) + } +} + +#[cfg(cpuid)] +impl From for kvm_bindings::CpuId { + #[allow(clippy::transmute_ptr_to_ptr, clippy::unwrap_used)] + #[inline] + fn from(this: RawCpuid) -> Self { + // SAFETY: Always safe. + let cpuid_slice = unsafe { + std::slice::from_raw_parts(this.entries.as_ptr(), usize::try_from(this.nent).unwrap()) + }; + + // SAFETY: Always safe. + let kvm_bindings_slice = unsafe { transmute(cpuid_slice) }; + kvm_bindings::CpuId::from_entries(kvm_bindings_slice).unwrap() + } +} + +/// A structure for owning unused memory for padding. +/// +/// A wrapper around an uninitialized `N` element array of `u8`s (`MaybeUninit<[u8;N]>` constructed +/// with `Self(MaybeUninit::uninit())`). +#[derive(Debug, Clone)] +#[repr(C)] +pub struct Padding(MaybeUninit<[u8; N]>); + +impl Default for Padding { + #[inline] + fn default() -> Self { + Self(MaybeUninit::uninit()) + } +} + +impl serde::Serialize for Padding { + #[inline] + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_unit_struct("Padding") + } +} + +impl<'de, const N: usize> serde::Deserialize<'de> for Padding { + #[inline] + fn deserialize(_deserializer: D) -> Result, D::Error> + where + D: serde::Deserializer<'de>, + { + Ok(Padding(MaybeUninit::uninit())) + } +} + +impl PartialEq for Padding { + #[inline] + fn eq(&self, _other: &Self) -> bool { + true + } +} + +impl Eq for Padding {} + +bit_fields::bitfield!( + /// Definitions from `kvm/arch/x86/include/uapi/asm/kvm.h + KvmCpuidFlags, + u32, + { + /// Indicates if the `index` field is used for indexing sub-leaves (if false, this CPUID leaf + /// has no subleaves). + significant_index: 0, + /// Deprecated. + stateful_func: 1, + /// Deprecated. + state_read_next: 2, + } +); + +/// CPUID entry (a mimic of ). +#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] +#[repr(C)] +pub struct RawKvmCpuidEntry { + /// CPUID function (leaf). + pub function: u32, + /// CPUID index (subleaf). + pub index: u32, + /// KVM CPUID flags. + pub flags: KvmCpuidFlags, + /// EAX register. + pub eax: u32, + /// EBX register. + pub ebx: u32, + /// ECX register. + pub ecx: u32, + /// EDX register. + pub edx: u32, + /// CPUID entry padding. + pub padding: Padding<{ size_of::<[u32; 3]>() }>, +} +impl fmt::LowerHex for RawKvmCpuidEntry { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RawKvmCpuidEntry") + .field("function", &format!("{:x}", self.function)) + .field("index", &format!("{:x}", self.index)) + .field("flags", &format!("{:x}", self.flags.0)) + .field("eax", &format!("{:x}", self.eax)) + .field("ebx", &format!("{:x}", self.ebx)) + .field("ecx", &format!("{:x}", self.ecx)) + .field("edx", &format!("{:x}", self.edx)) + .finish() + } +} + +#[allow(clippy::unwrap_used)] +#[cfg(test)] +mod tests { + #[cfg(cpuid)] + use kvm_bindings::KVM_MAX_CPUID_ENTRIES; + + #[cfg(cpuid)] + use super::*; + + #[cfg(cpuid)] + #[test] + fn kvm_set_cpuid() { + let kvm = kvm_ioctls::Kvm::new().unwrap(); + let vm = kvm.create_vm().unwrap(); + let vcpu = vm.create_vcpu(0).unwrap(); + let kvm_cpuid = kvm.get_supported_cpuid(KVM_MAX_CPUID_ENTRIES).unwrap(); + + println!("kvm_cpuid:"); + for x in kvm_cpuid.as_slice() { + println!("\t{:?}", x); + } + + let cpuid = RawCpuid::from(kvm_cpuid.clone()); + println!("cpuid:"); + for x in cpuid.iter() { + println!("\t{:?}", x); + } + + let kvm_cpuid_2 = kvm_bindings::CpuId::from(cpuid); + println!("kvm_cpuid_2:"); + for x in kvm_cpuid_2.as_slice() { + println!("\t{:?}", x); + } + assert_eq!(kvm_cpuid.as_slice(), kvm_cpuid_2.as_slice()); + + vcpu.set_cpuid2(&kvm_cpuid_2).unwrap(); + + let kvm_cpuid_3 = vcpu.get_cpuid2(KVM_MAX_CPUID_ENTRIES).unwrap(); + println!("kvm_cpuid_3:"); + for x in kvm_cpuid_3.as_slice() { + println!("\t{:?}", x); + } + } + #[cfg(cpuid)] + #[test] + fn between_kvm() { + let kvm = kvm_ioctls::Kvm::new().unwrap(); + let kvm_cpuid = kvm + .get_supported_cpuid(kvm_bindings::KVM_MAX_CPUID_ENTRIES) + .unwrap(); + let raw_cpuid = RawCpuid::from(kvm_cpuid.clone()); + let kvm_cpuid_2 = kvm_bindings::CpuId::from(raw_cpuid); + + assert_eq!(kvm_cpuid.as_slice(), kvm_cpuid_2.as_slice()); + } + #[cfg(cpuid)] + #[test] + fn clone() { + let kvm = kvm_ioctls::Kvm::new().unwrap(); + let kvm_cpuid = kvm + .get_supported_cpuid(kvm_bindings::KVM_MAX_CPUID_ENTRIES) + .unwrap(); + let raw_cpuid = RawCpuid::from(kvm_cpuid); + let cloned = raw_cpuid.clone(); + + assert_eq!(raw_cpuid, cloned); + } +} diff --git a/src/cpuid/src/indexing.rs b/src/cpuid/src/indexing.rs new file mode 100644 index 00000000000..c7f8146dff9 --- /dev/null +++ b/src/cpuid/src/indexing.rs @@ -0,0 +1,109 @@ +// Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +#![allow(clippy::transmute_ptr_to_ptr, clippy::needless_lifetimes)] + +#[allow(clippy::wildcard_imports)] +use super::leaves::*; + +/// Indexs leaf. +pub trait IndexLeaf { + /// Leaf type. + type Output<'a> + where + Self: 'a; + /// Gets immutable reference to leaf. + fn index_leaf<'a>(&'a self) -> Self::Output<'a>; +} +/// Indexs leaf. +pub trait IndexLeafMut { + /// Leaf type. + type Output<'a> + where + Self: 'a; + /// Gets mutable reference to leaf. + fn index_leaf_mut<'a>(&'a mut self) -> Self::Output<'a>; +} + +/// Transmutes `Vec` into `Vec` where `size::() == size::()`. +pub(crate) unsafe fn transmute_vec(from: Vec) -> Vec { + let mut intermediate = std::mem::ManuallyDrop::new(from); + Vec::from_raw_parts( + intermediate.as_mut_ptr().cast(), + intermediate.len(), + intermediate.capacity(), + ) +} + +/// Convenience macro for indexing leaves. +macro_rules! index_leaf { + ($index: literal, $leaf: ty, $cpuid: ty) => { + impl $crate::IndexLeaf<$index> for $cpuid { + type Output<'a> = Option<&'a $leaf>; + #[inline] + fn index_leaf<'a>(&'a self) -> Self::Output<'a> { + self.0 + .get(&$crate::CpuidKey::leaf($index)) + // SAFETY: Transmuting reference to same sized types is safe. + .map(|entry| unsafe { std::mem::transmute::<_, &$leaf>(&entry.result) }) + } + } + impl $crate::IndexLeafMut<$index> for $cpuid { + type Output<'a> = Option<&'a mut $leaf>; + #[inline] + fn index_leaf_mut<'a>(&'a mut self) -> Self::Output<'a> { + self.0 + .get_mut(&$crate::CpuidKey::leaf($index)) + // SAFETY: Transmuting reference to same sized types is safe. + .map(|entry| unsafe { std::mem::transmute::<_, &mut $leaf>(&mut entry.result) }) + } + } + }; +} + +pub(crate) use index_leaf; + +/// Convenience macro for indexing shared leaves. +macro_rules! cpuid_index_leaf { + ($index: literal, $leaf: ty) => { + impl IndexLeaf<$index> for super::Cpuid { + type Output<'a> = Option<&'a $leaf>; + #[inline] + fn index_leaf<'a>(&'a self) -> Self::Output<'a> { + match self { + Self::Intel(intel_cpuid) => { + >::index_leaf(intel_cpuid) + } + Self::Amd(amd_cpuid) => { + >::index_leaf(amd_cpuid) + } + } + } + } + impl IndexLeafMut<$index> for super::Cpuid { + type Output<'a> = Option<&'a mut $leaf>; + #[inline] + fn index_leaf_mut<'a>(&'a mut self) -> Self::Output<'a> { + match self { + Self::Intel(intel_cpuid) => { + >::index_leaf_mut(intel_cpuid) + } + Self::Amd(amd_cpuid) => { + >::index_leaf_mut(amd_cpuid) + } + } + } + } + index_leaf!($index, $leaf, crate::AmdCpuid); + index_leaf!($index, $leaf, crate::IntelCpuid); + }; +} + +cpuid_index_leaf!(0x0, Leaf0); + +cpuid_index_leaf!(0x1, Leaf1); + +cpuid_index_leaf!(0x80000002, Leaf80000002); + +cpuid_index_leaf!(0x80000003, Leaf80000003); + +cpuid_index_leaf!(0x80000004, Leaf80000004); diff --git a/src/cpuid/src/intel/indexing.rs b/src/cpuid/src/intel/indexing.rs new file mode 100644 index 00000000000..61bf4926148 --- /dev/null +++ b/src/cpuid/src/intel/indexing.rs @@ -0,0 +1,459 @@ +// Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +#![allow(clippy::transmute_ptr_to_ptr, clippy::needless_lifetimes)] + +use std::mem::transmute; + +#[allow(clippy::wildcard_imports)] +use super::leaves::*; +use crate::{index_leaf, transmute_vec, CpuidKey, IndexLeaf, IndexLeafMut, IntelCpuid}; + +index_leaf!(0x2, Leaf2, IntelCpuid); + +index_leaf!(0x3, Leaf3, IntelCpuid); + +impl IndexLeaf<0x4> for IntelCpuid { + type Output<'a> = Leaf4<'a>; + + #[inline] + fn index_leaf<'a>(&'a self) -> Self::Output<'a> { + // SAFETY: Transmuting reference to same sized types is safe. + unsafe { + Leaf4(transmute_vec( + self.0 + .range(CpuidKey::leaf(0x4)..CpuidKey::leaf(0x5)) + .map(|(_, v)| transmute::<_, &'a Leaf4Subleaf>(&v.result)) + .collect(), + )) + } + } +} + +impl IndexLeafMut<0x4> for IntelCpuid { + type Output<'a> = Leaf4Mut<'a>; + + #[inline] + fn index_leaf_mut<'a>(&'a mut self) -> Self::Output<'a> { + // SAFETY: Transmuting reference to same sized types is safe. + unsafe { + Leaf4Mut(transmute_vec( + self.0 + .range_mut(CpuidKey::leaf(0x4)..CpuidKey::leaf(0x5)) + .map(|(_, v)| transmute::<_, &'a mut Leaf4Subleaf>(&mut v.result)) + .collect(), + )) + } + } +} + +index_leaf!(0x5, Leaf5, IntelCpuid); + +index_leaf!(0x6, Leaf6, IntelCpuid); + +impl IndexLeaf<0x7> for IntelCpuid { + type Output<'a> = Leaf7<'a>; + + #[inline] + fn index_leaf<'a>(&'a self) -> Self::Output<'a> { + Leaf7( + self.0 + .get(&CpuidKey::subleaf(0x7, 0x0)) + // SAFETY: Transmuting reference to same sized types is safe. + .map(|entry| unsafe { transmute::<_, &'a Leaf7Subleaf0>(&entry.result) }), + self.0 + .get(&CpuidKey::subleaf(0x7, 0x1)) + // SAFETY: Transmuting reference to same sized types is safe. + .map(|entry| unsafe { transmute::<_, &'a Leaf7Subleaf1>(&entry.result) }), + ) + } +} + +impl IndexLeafMut<0x7> for IntelCpuid { + type Output<'a> = Leaf7Mut<'a>; + + #[inline] + fn index_leaf_mut<'a>(&'a mut self) -> Self::Output<'a> { + Leaf7Mut( + self.0 + .get_mut(&CpuidKey::subleaf(0x7, 0x0)) + // SAFETY: Transmuting reference to same sized types is safe. + .map(|entry| unsafe { transmute::<_, &'a mut Leaf7Subleaf0>(&mut entry.result) }), + self.0 + .get_mut(&CpuidKey::subleaf(0x7, 0x1)) + // SAFETY: Transmuting reference to same sized types is safe. + .map(|entry| unsafe { transmute::<_, &'a mut Leaf7Subleaf1>(&mut entry.result) }), + ) + } +} + +index_leaf!(0x9, Leaf9, IntelCpuid); + +index_leaf!(0xA, LeafA, IntelCpuid); + +impl IndexLeaf<0xB> for IntelCpuid { + type Output<'a> = LeafB<'a>; + + #[inline] + fn index_leaf<'a>(&'a self) -> Self::Output<'a> { + // SAFETY: Transmuting reference to same sized types is safe. + unsafe { + LeafB(transmute_vec( + self.0 + .range(CpuidKey::leaf(0xB)..CpuidKey::leaf(0xC)) + .map(|(_, v)| transmute::<_, &'a LeafBSubleaf>(&v.result)) + .collect(), + )) + } + } +} + +impl IndexLeafMut<0xB> for IntelCpuid { + type Output<'a> = LeafBMut<'a>; + + #[inline] + fn index_leaf_mut<'a>(&'a mut self) -> Self::Output<'a> { + // SAFETY: Transmuting reference to same sized types is safe. + unsafe { + LeafBMut(transmute_vec( + self.0 + .range_mut(CpuidKey::leaf(0xB)..CpuidKey::leaf(0xC)) + .map(|(_, v)| transmute::<_, &'a mut LeafBSubleaf>(&mut v.result)) + .collect(), + )) + } + } +} + +impl IndexLeaf<0xF> for IntelCpuid { + type Output<'a> = LeafF<'a>; + + #[inline] + fn index_leaf<'a>(&'a self) -> Self::Output<'a> { + LeafF( + self.0 + .get(&CpuidKey::subleaf(0x7, 0x0)) + // SAFETY: Transmuting reference to same sized types is safe. + .map(|entry| unsafe { transmute::<_, &'a LeafFSubleaf0>(&entry.result) }), + self.0 + .get(&CpuidKey::subleaf(0x7, 0x1)) + // SAFETY: Transmuting reference to same sized types is safe. + .map(|entry| unsafe { transmute::<_, &'a LeafFSubleaf1>(&entry.result) }), + ) + } +} + +impl IndexLeafMut<0xF> for IntelCpuid { + type Output<'a> = LeafFMut<'a>; + + #[inline] + fn index_leaf_mut<'a>(&'a mut self) -> Self::Output<'a> { + LeafFMut( + self.0 + .get_mut(&CpuidKey::subleaf(0x7, 0x0)) + // SAFETY: Transmuting reference to same sized types is safe. + .map(|entry| unsafe { transmute::<_, &'a mut LeafFSubleaf0>(&mut entry.result) }), + self.0 + .get_mut(&CpuidKey::subleaf(0x7, 0x1)) + // SAFETY: Transmuting reference to same sized types is safe. + .map(|entry| unsafe { transmute::<_, &'a mut LeafFSubleaf1>(&mut entry.result) }), + ) + } +} + +impl IndexLeaf<0x10> for IntelCpuid { + type Output<'a> = Leaf10<'a>; + + #[inline] + fn index_leaf<'a>(&'a self) -> Self::Output<'a> { + Leaf10( + self.0 + .get(&CpuidKey::subleaf(0x10, 0x0)) + // SAFETY: Transmuting reference to same sized types is safe. + .map(|entry| unsafe { transmute::<_, &'a Leaf10Subleaf0>(&entry.result) }), + self.0 + .get(&CpuidKey::subleaf(0x10, 0x1)) + // SAFETY: Transmuting reference to same sized types is safe. + .map(|entry| unsafe { transmute::<_, &'a Leaf10Subleaf1>(&entry.result) }), + self.0 + .get(&CpuidKey::subleaf(0x10, 0x2)) + // SAFETY: Transmuting reference to same sized types is safe. + .map(|entry| unsafe { transmute::<_, &'a Leaf10Subleaf2>(&entry.result) }), + self.0 + .get(&CpuidKey::subleaf(0x10, 0x3)) + // SAFETY: Transmuting reference to same sized types is safe. + .map(|entry| unsafe { transmute::<_, &'a Leaf10Subleaf3>(&entry.result) }), + ) + } +} + +impl IndexLeafMut<0x10> for IntelCpuid { + type Output<'a> = Leaf10Mut<'a>; + + #[inline] + fn index_leaf_mut<'a>(&'a mut self) -> Self::Output<'a> { + Leaf10Mut( + self.0 + .get_mut(&CpuidKey::subleaf(0x10, 0x0)) + // SAFETY: Transmuting reference to same sized types is safe. + .map(|entry| unsafe { transmute::<_, &'a mut Leaf10Subleaf0>(&mut entry.result) }), + self.0 + .get_mut(&CpuidKey::subleaf(0x10, 0x1)) + // SAFETY: Transmuting reference to same sized types is safe. + .map(|entry| unsafe { transmute::<_, &'a mut Leaf10Subleaf1>(&mut entry.result) }), + self.0 + .get_mut(&CpuidKey::subleaf(0x10, 0x2)) + // SAFETY: Transmuting reference to same sized types is safe. + .map(|entry| unsafe { transmute::<_, &'a mut Leaf10Subleaf2>(&mut entry.result) }), + self.0 + .get_mut(&CpuidKey::subleaf(0x10, 0x3)) + // SAFETY: Transmuting reference to same sized types is safe. + .map(|entry| unsafe { transmute::<_, &'a mut Leaf10Subleaf3>(&mut entry.result) }), + ) + } +} + +impl IndexLeaf<0x12> for IntelCpuid { + type Output<'a> = Leaf12<'a>; + + #[inline] + fn index_leaf<'a>(&'a self) -> Self::Output<'a> { + Leaf12( + self.0 + .get(&CpuidKey::subleaf(0x12, 0x0)) + // SAFETY: Transmuting reference to same sized types is safe. + .map(|entry| unsafe { transmute::<_, &'a Leaf12Subleaf0>(&entry.result) }), + self.0 + .get(&CpuidKey::subleaf(0x12, 0x1)) + // SAFETY: Transmuting reference to same sized types is safe. + .map(|entry| unsafe { transmute::<_, &'a Leaf12Subleaf1>(&entry.result) }), + // SAFETY: Transmuting reference to same sized types is safe. + unsafe { + transmute_vec( + self.0 + .range(CpuidKey::leaf(0x12)..CpuidKey::leaf(0x2)) + .map(|(_, v)| transmute::<_, &'a Leaf12SubleafGt1>(&v.result)) + .collect(), + ) + }, + ) + } +} + +impl IndexLeafMut<0x12> for IntelCpuid { + type Output<'a> = Leaf12Mut<'a>; + + #[inline] + fn index_leaf_mut<'a>(&'a mut self) -> Self::Output<'a> { + Leaf12Mut( + self.0 + .get_mut(&CpuidKey::subleaf(0x12, 0x0)) + // SAFETY: Transmuting reference to same sized types is safe. + .map(|entry| unsafe { transmute::<_, &'a mut Leaf12Subleaf0>(&mut entry.result) }), + self.0 + .get_mut(&CpuidKey::subleaf(0x12, 0x1)) + // SAFETY: Transmuting reference to same sized types is safe. + .map(|entry| unsafe { transmute::<_, &'a mut Leaf12Subleaf1>(&mut entry.result) }), + // SAFETY: Transmuting reference to same sized types is safe. + unsafe { + transmute_vec( + self.0 + .range_mut(CpuidKey::leaf(0x12)..CpuidKey::leaf(0x2)) + .map(|(_, v)| transmute::<_, &'a mut Leaf12SubleafGt1>(&mut v.result)) + .collect(), + ) + }, + ) + } +} + +impl IndexLeaf<0x14> for IntelCpuid { + type Output<'a> = Leaf14<'a>; + + #[inline] + fn index_leaf<'a>(&'a self) -> Self::Output<'a> { + Leaf14( + self.0 + .get(&CpuidKey::subleaf(0x14, 0x0)) + // SAFETY: Transmuting reference to same sized types is safe. + .map(|entry| unsafe { transmute::<_, &'a Leaf14Subleaf0>(&entry.result) }), + self.0 + .get(&CpuidKey::subleaf(0x14, 0x1)) + // SAFETY: Transmuting reference to same sized types is safe. + .map(|entry| unsafe { transmute::<_, &'a Leaf14Subleaf1>(&entry.result) }), + ) + } +} + +impl IndexLeafMut<0x14> for IntelCpuid { + type Output<'a> = Leaf14Mut<'a>; + + #[inline] + fn index_leaf_mut<'a>(&'a mut self) -> Self::Output<'a> { + Leaf14Mut( + self.0 + .get_mut(&CpuidKey::subleaf(0x14, 0x0)) + // SAFETY: Transmuting reference to same sized types is safe. + .map(|entry| unsafe { transmute::<_, &'a mut Leaf14Subleaf0>(&mut entry.result) }), + self.0 + .get_mut(&CpuidKey::subleaf(0x14, 0x1)) + // SAFETY: Transmuting reference to same sized types is safe. + .map(|entry| unsafe { transmute::<_, &'a mut Leaf14Subleaf1>(&mut entry.result) }), + ) + } +} + +index_leaf!(0x15, Leaf15, IntelCpuid); + +index_leaf!(0x16, Leaf16, IntelCpuid); + +impl IndexLeaf<0x17> for IntelCpuid { + type Output<'a> = Leaf17<'a>; + + #[inline] + fn index_leaf<'a>(&'a self) -> Self::Output<'a> { + Leaf17( + self.0 + .get(&CpuidKey::subleaf(0x17, 0x0)) + // SAFETY: Transmuting reference to same sized types is safe. + .map(|entry| unsafe { transmute::<_, &'a Leaf17Subleaf0>(&entry.result) }), + self.0 + .get(&CpuidKey::subleaf(0x17, 0x1)) + // SAFETY: Transmuting reference to same sized types is safe. + .map(|entry| unsafe { transmute::<_, &'a Leaf17Subleaf1>(&entry.result) }), + self.0 + .get(&CpuidKey::subleaf(0x17, 0x2)) + // SAFETY: Transmuting reference to same sized types is safe. + .map(|entry| unsafe { transmute::<_, &'a Leaf17Subleaf2>(&entry.result) }), + self.0 + .get(&CpuidKey::subleaf(0x17, 0x3)) + // SAFETY: Transmuting reference to same sized types is safe. + .map(|entry| unsafe { transmute::<_, &'a Leaf17Subleaf3>(&entry.result) }), + ) + } +} + +impl IndexLeafMut<0x17> for IntelCpuid { + type Output<'a> = Leaf17Mut<'a>; + + #[inline] + fn index_leaf_mut<'a>(&'a mut self) -> Self::Output<'a> { + Leaf17Mut( + self.0 + .get_mut(&CpuidKey::subleaf(0x17, 0x0)) + // SAFETY: Transmuting reference to same sized types is safe. + .map(|entry| unsafe { transmute::<_, &'a mut Leaf17Subleaf0>(&mut entry.result) }), + self.0 + .get_mut(&CpuidKey::subleaf(0x17, 0x1)) + // SAFETY: Transmuting reference to same sized types is safe. + .map(|entry| unsafe { transmute::<_, &'a mut Leaf17Subleaf1>(&mut entry.result) }), + self.0 + .get_mut(&CpuidKey::subleaf(0x17, 0x2)) + // SAFETY: Transmuting reference to same sized types is safe. + .map(|entry| unsafe { transmute::<_, &'a mut Leaf17Subleaf2>(&mut entry.result) }), + self.0 + .get_mut(&CpuidKey::subleaf(0x17, 0x3)) + // SAFETY: Transmuting reference to same sized types is safe. + .map(|entry| unsafe { transmute::<_, &'a mut Leaf17Subleaf3>(&mut entry.result) }), + ) + } +} + +impl IndexLeaf<0x18> for IntelCpuid { + type Output<'a> = Leaf18<'a>; + + #[inline] + fn index_leaf<'a>(&'a self) -> Self::Output<'a> { + Leaf18( + self.0 + .get(&CpuidKey::subleaf(0x18, 0x0)) + // SAFETY: Transmuting reference to same sized types is safe. + .map(|entry| unsafe { transmute::<_, &'a Leaf18Subleaf0>(&entry.result) }), + // SAFETY: Transmuting reference to same sized types is safe. + unsafe { + transmute_vec( + self.0 + .range(CpuidKey::subleaf(0x18, 0x1)..CpuidKey::leaf(0x19)) + .map(|(_, v)| transmute::<_, &'a Leaf18SubleafGt0>(&v.result)) + .collect(), + ) + }, + ) + } +} + +impl IndexLeafMut<0x18> for IntelCpuid { + type Output<'a> = Leaf18Mut<'a>; + + #[inline] + fn index_leaf_mut<'a>(&'a mut self) -> Self::Output<'a> { + Leaf18Mut( + self.0 + .get_mut(&CpuidKey::subleaf(0x18, 0x0)) + // SAFETY: Transmuting reference to same sized types is safe. + .map(|entry| unsafe { transmute::<_, &'a mut Leaf18Subleaf0>(&mut entry.result) }), + // SAFETY: Transmuting reference to same sized types is safe. + unsafe { + transmute_vec( + self.0 + .range_mut(CpuidKey::subleaf(0x18, 0x1)..CpuidKey::leaf(0x19)) + .map(|(_, v)| transmute::<_, &'a mut Leaf18SubleafGt0>(&mut v.result)) + .collect(), + ) + }, + ) + } +} + +index_leaf!(0x19, Leaf19, IntelCpuid); + +index_leaf!(0x1A, Leaf1A, IntelCpuid); + +index_leaf!(0x1C, Leaf1C, IntelCpuid); + +impl IndexLeaf<0x1F> for IntelCpuid { + type Output<'a> = Leaf1F<'a>; + + #[inline] + fn index_leaf<'a>(&'a self) -> Self::Output<'a> { + // SAFETY: Transmuting reference to same sized types is safe. + Leaf1F(unsafe { + transmute_vec( + self.0 + .range(CpuidKey::leaf(0x1F)..CpuidKey::leaf(0x20)) + .map(|(_, v)| transmute::<_, &'a Leaf1FSubleaf>(&v.result)) + .collect(), + ) + }) + } +} + +impl IndexLeafMut<0x1F> for IntelCpuid { + type Output<'a> = Leaf1FMut<'a>; + + #[inline] + fn index_leaf_mut<'a>(&'a mut self) -> Self::Output<'a> { + // SAFETY: Transmuting reference to same sized types is safe. + Leaf1FMut(unsafe { + transmute_vec( + self.0 + .range_mut(CpuidKey::leaf(0x1F)..CpuidKey::leaf(0x20)) + .map(|(_, v)| transmute::<_, &'a mut Leaf1FSubleaf>(&mut v.result)) + .collect(), + ) + }) + } +} + +index_leaf!(0x20, Leaf20, IntelCpuid); + +index_leaf!(0x80000000, Leaf80000000, IntelCpuid); + +index_leaf!(0x80000001, Leaf80000001, IntelCpuid); + +index_leaf!(0x80000005, Leaf80000005, IntelCpuid); + +index_leaf!(0x80000006, Leaf80000006, IntelCpuid); + +index_leaf!(0x80000007, Leaf80000007, IntelCpuid); + +index_leaf!(0x80000008, Leaf80000008, IntelCpuid); diff --git a/src/cpuid/src/intel/leaves.rs b/src/cpuid/src/intel/leaves.rs new file mode 100644 index 00000000000..2d11f827bee --- /dev/null +++ b/src/cpuid/src/intel/leaves.rs @@ -0,0 +1,285 @@ +// Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +#![allow(clippy::similar_names, clippy::module_name_repetitions)] + +#[allow(clippy::wildcard_imports)] +use super::registers::*; +use crate::Leaf; + +impl From<(u32, u32, u32, u32)> for Leaf2 { + #[inline] + fn from((eax, ebx, ecx, edx): (u32, u32, u32, u32)) -> Self { + Self { + eax: eax.to_ne_bytes(), + ebx: ebx.to_ne_bytes(), + ecx: ecx.to_ne_bytes(), + edx: edx.to_ne_bytes(), + } + } +} + +// ------------------------------------------------------------------------------------------------- +// Leaf types +// ------------------------------------------------------------------------------------------------- + +/// Leaf 02H +pub type Leaf2 = Leaf<[u8; 4], [u8; 4], [u8; 4], [u8; 4]>; + +/// Leaf 03H +pub type Leaf3 = Leaf; + +/// Leaf 04H +#[derive(Debug, PartialEq, Eq)] +pub struct Leaf4<'a>(pub Vec<&'a Leaf4Subleaf>); + +/// Leaf 04H +#[derive(Debug, PartialEq, Eq)] +pub struct Leaf4Mut<'a>(pub Vec<&'a mut Leaf4Subleaf>); + +/// Leaf 04H subleaf +pub type Leaf4Subleaf = Leaf; + +/// Leaf 05H +pub type Leaf5 = Leaf; + +/// Leaf 06H +pub type Leaf6 = Leaf; + +/// Leaf 07H +#[derive(Debug, PartialEq, Eq)] +pub struct Leaf7<'a>(pub Option<&'a Leaf7Subleaf0>, pub Option<&'a Leaf7Subleaf1>); + +/// Leaf 07H +#[derive(Debug, PartialEq, Eq)] +pub struct Leaf7Mut<'a>( + pub Option<&'a mut Leaf7Subleaf0>, + pub Option<&'a mut Leaf7Subleaf1>, +); + +/// Leaf 07H subleaf 0 +pub type Leaf7Subleaf0 = + Leaf; + +/// Leaf 07H subleaf 1 +pub type Leaf7Subleaf1 = + Leaf; + +/// Leaf 09H +pub type Leaf9 = Leaf; + +/// Leaf 0AH +pub type LeafA = Leaf; + +/// Leaf 0BH +#[derive(Debug, PartialEq, Eq)] +pub struct LeafB<'a>(pub Vec<&'a LeafBSubleaf>); + +/// Leaf 0BH +#[derive(Debug, PartialEq, Eq)] +pub struct LeafBMut<'a>(pub Vec<&'a mut LeafBSubleaf>); + +/// Leaf 0BH subleaf +pub type LeafBSubleaf = Leaf; + +/// Leaf 0FH +#[derive(Debug, PartialEq, Eq)] +pub struct LeafF<'a>(pub Option<&'a LeafFSubleaf0>, pub Option<&'a LeafFSubleaf1>); + +/// Leaf 0FH +#[derive(Debug, PartialEq, Eq)] +pub struct LeafFMut<'a>( + pub Option<&'a mut LeafFSubleaf0>, + pub Option<&'a mut LeafFSubleaf1>, +); + +/// Leaf 0FH subleaf 0 +pub type LeafFSubleaf0 = + Leaf; + +/// Leaf 0FH subleaf 1 +pub type LeafFSubleaf1 = + Leaf; + +/// Leaf 10H +#[derive(Debug, PartialEq, Eq)] +pub struct Leaf10<'a>( + pub Option<&'a Leaf10Subleaf0>, + pub Option<&'a Leaf10Subleaf1>, + pub Option<&'a Leaf10Subleaf2>, + pub Option<&'a Leaf10Subleaf3>, +); + +/// Leaf 10H +#[derive(Debug, PartialEq, Eq)] +pub struct Leaf10Mut<'a>( + pub Option<&'a mut Leaf10Subleaf0>, + pub Option<&'a mut Leaf10Subleaf1>, + pub Option<&'a mut Leaf10Subleaf2>, + pub Option<&'a mut Leaf10Subleaf3>, +); + +/// Leaf 10H subleaf 0 +pub type Leaf10Subleaf0 = + Leaf; + +/// Leaf 10H subleaf 1 +pub type Leaf10Subleaf1 = + Leaf; + +/// Leaf 10H subleaf 2 +pub type Leaf10Subleaf2 = + Leaf; + +/// Leaf 10H subleaf 3 +pub type Leaf10Subleaf3 = + Leaf; + +/// Leaf 12H +#[derive(Debug, PartialEq, Eq)] +pub struct Leaf12<'a>( + pub Option<&'a Leaf12Subleaf0>, + pub Option<&'a Leaf12Subleaf1>, + pub Vec<&'a Leaf12SubleafGt1>, +); + +/// Leaf 12H +#[derive(Debug, PartialEq, Eq)] +pub struct Leaf12Mut<'a>( + pub Option<&'a mut Leaf12Subleaf0>, + pub Option<&'a mut Leaf12Subleaf1>, + pub Vec<&'a mut Leaf12SubleafGt1>, +); + +/// Leaf 12H subleaf 0 +pub type Leaf12Subleaf0 = + Leaf; + +/// Leaf 12H subleaf 1 +pub type Leaf12Subleaf1 = + Leaf; + +/// Leaf 12H subleaf >1 +pub type Leaf12SubleafGt1 = + Leaf; + +/// Leaf 14H +#[derive(Debug, PartialEq, Eq)] +pub struct Leaf14<'a>( + pub Option<&'a Leaf14Subleaf0>, + pub Option<&'a Leaf14Subleaf1>, +); + +/// Leaf 14H +#[derive(Debug, PartialEq, Eq)] +pub struct Leaf14Mut<'a>( + pub Option<&'a mut Leaf14Subleaf0>, + pub Option<&'a mut Leaf14Subleaf1>, +); +/// Leaf 14H subleaf 0 +pub type Leaf14Subleaf0 = + Leaf; + +/// Leaf 14H subleaf 1 +pub type Leaf14Subleaf1 = + Leaf; + +/// Leaf 15H +pub type Leaf15 = Leaf; + +/// Leaf 16H +pub type Leaf16 = Leaf; + +/// Leaf 17H +#[derive(Debug, PartialEq, Eq)] +pub struct Leaf17<'a>( + pub Option<&'a Leaf17Subleaf0>, + pub Option<&'a Leaf17Subleaf1>, + pub Option<&'a Leaf17Subleaf2>, + pub Option<&'a Leaf17Subleaf3>, +); + +/// Leaf 17H +#[derive(Debug, PartialEq, Eq)] +pub struct Leaf17Mut<'a>( + pub Option<&'a mut Leaf17Subleaf0>, + pub Option<&'a mut Leaf17Subleaf1>, + pub Option<&'a mut Leaf17Subleaf2>, + pub Option<&'a mut Leaf17Subleaf3>, +); + +/// Leaf 18H subleaf 0 +pub type Leaf17Subleaf0 = + Leaf; + +/// Leaf 17H subleaf 1 +pub type Leaf17Subleaf1 = + Leaf; + +/// Leaf 17H subleaf 2 +pub type Leaf17Subleaf2 = Leaf17Subleaf1; + +/// Leaf 17H subleaf 3 +pub type Leaf17Subleaf3 = Leaf17Subleaf1; + +/// Leaf 18H +#[derive(Debug, PartialEq, Eq)] +pub struct Leaf18<'a>( + pub Option<&'a Leaf18Subleaf0>, + pub Vec<&'a Leaf18SubleafGt0>, +); + +/// Leaf 18H +#[derive(Debug, PartialEq, Eq)] +pub struct Leaf18Mut<'a>( + pub Option<&'a mut Leaf18Subleaf0>, + pub Vec<&'a mut Leaf18SubleafGt0>, +); + +/// Leaf 18H subleaf 0 +pub type Leaf18Subleaf0 = + Leaf; + +/// Leaf 18H subleaf 1 +pub type Leaf18SubleafGt0 = + Leaf; + +/// Leaf 19H +pub type Leaf19 = Leaf; + +/// Leaf 1AH +pub type Leaf1A = Leaf; + +/// Leaf 1CH +pub type Leaf1C = Leaf; + +/// Leaf 1FH +#[derive(Debug, PartialEq, Eq)] +pub struct Leaf1F<'a>(pub Vec<&'a Leaf1FSubleaf>); + +/// Leaf 1FH +#[derive(Debug, PartialEq, Eq)] +pub struct Leaf1FMut<'a>(pub Vec<&'a mut Leaf1FSubleaf>); + +/// Leaf 1F subleaf 1 +pub type Leaf1FSubleaf = Leaf; + +/// Leaf 20H +pub type Leaf20 = Leaf; + +/// Leaf 80000000H +pub type Leaf80000000 = Leaf; + +/// Leaf 80000001H +pub type Leaf80000001 = Leaf; + +/// Leaf 80000005H +pub type Leaf80000005 = Leaf; + +/// Leaf 80000006H +pub type Leaf80000006 = Leaf; + +/// Leaf 80000007H +pub type Leaf80000007 = Leaf; + +/// Leaf 80000008H +pub type Leaf80000008 = Leaf; diff --git a/src/cpuid/src/intel/mod.rs b/src/cpuid/src/intel/mod.rs new file mode 100644 index 00000000000..ca3416ff829 --- /dev/null +++ b/src/cpuid/src/intel/mod.rs @@ -0,0 +1,169 @@ +// Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +#![allow( + clippy::similar_names, + clippy::module_name_repetitions, + clippy::unreadable_literal, + clippy::unsafe_derive_deserialize +)] + +#[cfg(cpuid)] +use std::convert::TryInto; + +/// Leaf structs. +mod leaves; + +/// Indexing implementations. +mod indexing; + +/// CPUID normalize implementation. +#[cfg(cpuid)] +mod normalize; +#[cfg(cpuid)] +pub use normalize::{DeterministicCacheError, ExtendedTopologyError, NormalizeCpuidError}; + +/// Register bit fields. +mod registers; + +use super::{CpuidEntry, CpuidKey, CpuidTrait, RawCpuid, RawKvmCpuidEntry}; + +/// A structure matching the Intel CPUID specification as described in +/// [IntelĀ® 64 and IA-32 Architectures Software Developer's Manual Combined Volumes 2A, 2B, 2C, and 2D: Instruction Set Reference, A-Z](https://cdrdv2.intel.com/v1/dl/getContent/671110) +/// . +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct IntelCpuid(pub std::collections::BTreeMap); + +impl CpuidTrait for IntelCpuid { + /// Gets a given sub-leaf. + #[inline] + fn get(&self, key: &CpuidKey) -> Option<&CpuidEntry> { + self.0.get(key) + } + + /// Gets a given sub-leaf. + #[inline] + fn get_mut(&mut self, key: &CpuidKey) -> Option<&mut CpuidEntry> { + self.0.get_mut(key) + } +} + +/// Error type for [`IntelCpuid::default_brand_string`]. +#[cfg(cpuid)] +#[derive(Debug, Eq, PartialEq, thiserror::Error)] +pub enum DefaultBrandStringError { + /// Missing frequency. + #[error("Missing frequency: {0:?}.")] + Missingfrequency([u8; crate::BRAND_STRING_LENGTH]), + /// Missing space. + #[error("Missing space: {0:?}.")] + MissingSpace([u8; crate::BRAND_STRING_LENGTH]), + /// Insufficient space in brand string. + #[error("Insufficient space in brand string.")] + Overflow, +} + +impl IntelCpuid { + /// Gets the brand string always used for Intel. + /// + /// # Errors + /// + /// When unable to parse the host brand string. + /// `brand_string.try_into().unwrap()` cannot panic as we know + /// `brand_string.len() == BRAND_STRING_LENGTH` + /// + /// # Panics + /// + /// Never. + // As we pass through host frequency, we require CPUID and thus `cfg(cpuid)`. + // TODO: Use `split_array_ref` + // (https://github.com/firecracker-microvm/firecracker/issues/3347) + #[allow( + clippy::indexing_slicing, + clippy::integer_arithmetic, + clippy::arithmetic_side_effects + )] + #[cfg(cpuid)] + #[inline] + pub fn default_brand_string( + ) -> Result<[u8; super::BRAND_STRING_LENGTH], DefaultBrandStringError> { + /// We always use this brand string. + const DEFAULT_BRAND_STRING_BASE: &[u8] = b"Intel(R) Xeon(R) Processor @"; + + // Get host brand string. + // This will look like b"Intel(4) Xeon(R) Processor @ 3.00GHz". + let host_brand_string: [u8; super::BRAND_STRING_LENGTH] = super::host_brand_string(); + + // The slice of the host string before the frequency suffix + // e.g. b"Intel(4) Xeon(R) Processor @ 3.00" and "GHz" + let (before, after) = 'outer: { + for i in 0..host_brand_string.len() { + // Find position of b"THz" or b"GHz" or b"MHz" + if let [b'T' | b'G' | b'M', b'H', b'z', ..] = host_brand_string[i..] { + break 'outer Ok(host_brand_string.split_at(i)); + } + } + Err(DefaultBrandStringError::Missingfrequency(host_brand_string)) + }?; + + // We iterate from the end until hitting a space, getting the frequency number + // e.g. b"Intel(4) Xeon(R) Processor @ " and "3.00" + let (_, frequency) = 'outer: { + for i in (0..before.len()).rev() { + if before[i] == b' ' { + break 'outer Ok(before.split_at(i)); + } + } + Err(DefaultBrandStringError::MissingSpace(host_brand_string)) + }?; + + // As `DEFAULT_BRAND_STRING_BASE.len() + frequency.len() + after.len()` is guaranteed + // to be less than or equal to `2*BRAND_STRING_LENGTH` and we know + // `2*BRAND_STRING_LENGTH <= usize::MAX` since `BRAND_STRING_LENGTH==48`, this is always + // safe. + let len = DEFAULT_BRAND_STRING_BASE.len() + frequency.len() + after.len(); + + let brand_string = DEFAULT_BRAND_STRING_BASE + .iter() + .copied() + // Include frequency e.g. "3.00" + .chain(frequency.iter().copied()) + // Include frequency suffix e.g. "GHz" + .chain(after.iter().copied()) + // Pad with 0s to `BRAND_STRING_LENGTH` + .chain( + std::iter::repeat(b'\0').take( + super::BRAND_STRING_LENGTH + .checked_sub(len) + .ok_or(DefaultBrandStringError::Overflow)?, + ), + ) + .collect::>(); + + // SAFETY: Padding ensures `brand_string.len() == BRAND_STRING_LENGTH`. + Ok(unsafe { brand_string.try_into().unwrap_unchecked() }) + } +} + +impl From for IntelCpuid { + #[inline] + fn from(raw_cpuid: RawCpuid) -> Self { + let map = raw_cpuid + .iter() + .cloned() + .map(<(CpuidKey, CpuidEntry)>::from) + .collect(); + Self(map) + } +} + +impl From for RawCpuid { + #[inline] + fn from(intel_cpuid: IntelCpuid) -> Self { + let entries = intel_cpuid + .0 + .into_iter() + .map(RawKvmCpuidEntry::from) + .collect::>(); + Self::from(entries) + } +} diff --git a/src/cpuid/src/intel/normalize.rs b/src/cpuid/src/intel/normalize.rs new file mode 100644 index 00000000000..99b369a6e96 --- /dev/null +++ b/src/cpuid/src/intel/normalize.rs @@ -0,0 +1,306 @@ +// Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +use bit_fields::CheckedAssignError; + +use super::registers; +use crate::CpuidTrait; + +/// Error type for [`IntelCpuid::normalize`]. +#[derive(Debug, thiserror::Error, Eq, PartialEq)] +pub enum NormalizeCpuidError { + /// Provided `cpu_bits` is >=8. + #[error("Provided `cpu_bits` is >=8: {0}.")] + CpuBits(u8), + /// Failed to set deterministic cache leaf. + #[error("Failed to set deterministic cache leaf: {0}")] + DeterministicCache(#[from] DeterministicCacheError), + /// Leaf 0x6 is missing from CPUID. + #[error("Leaf 0x6 is missing from CPUID.")] + MissingLeaf6, + /// Leaf 0xA is missing from CPUID. + #[error("Leaf 0xA is missing from CPUID.")] + MissingLeafA, + /// Failed to set extended topology leaf. + #[error("Failed to set extended topology leaf: {0}")] + ExtendedTopology(#[from] ExtendedTopologyError), + /// Failed to get brand string. + #[error("Failed to get brand string: {0}")] + GetBrandString(super::DefaultBrandStringError), + /// Failed to set brand string. + #[error("Failed to set brand string: {0}")] + ApplyBrandString(crate::MissingBrandStringLeaves), +} + +/// Error type for setting leaf 4 section of `IntelCpuid::normalize`. +#[derive(Debug, thiserror::Error, Eq, PartialEq)] +pub enum DeterministicCacheError { + /// Failed to set `Maximum number of addressable IDs for logical processors sharing this + /// cache` due to underflow in cpu count. + #[error( + "Failed to set `Maximum number of addressable IDs for logical processors sharing this \ + cache` due to underflow in cpu count." + )] + MaxCpusPerCoreUnderflow, + /// Failed to set `Maximum number of addressable IDs for logical processors sharing this + /// cache`. + #[error( + "Failed to set `Maximum number of addressable IDs for logical processors sharing this \ + cache`: {0}" + )] + MaxCpusPerCore(CheckedAssignError), + /// Failed to set `Maximum number of addressable IDs for processor cores in the physical + /// package` due to underflow in cores + #[error( + "Failed to set `Maximum number of addressable IDs for processor cores in the physical \ + package` due to underflow in cores." + )] + MaxCorePerPackageUnderflow, + /// Failed to set `Maximum number of addressable IDs for processor cores in the physical + /// package`. + #[error( + "Failed to set `Maximum number of addressable IDs for processor cores in the physical \ + package`: {0}" + )] + MaxCorePerPackage(CheckedAssignError), +} + +/// Error type for setting leaf b section of `IntelCpuid::normalize`. +#[derive(Debug, thiserror::Error, Eq, PartialEq)] +pub enum ExtendedTopologyError { + /// Failed to set `Number of bits to shift right on x2APIC ID to get a unique topology ID of + /// the next level type`. + #[error( + "Failed to set `Number of bits to shift right on x2APIC ID to get a unique topology ID of \ + the next level type`: {0}" + )] + ApicId(CheckedAssignError), + /// Failed to set `Number of logical processors at this level type`. + #[error("Failed to set `Number of logical processors at this level type`: {0}")] + LogicalProcessors(CheckedAssignError), + /// Failed to set `Level Type`. + #[error("Failed to set `Level Type`: {0}")] + LevelType(CheckedAssignError), + /// Failed to set `Level Number`. + #[error("Failed to set `Level Number`: {0}")] + LevelNumber(CheckedAssignError), + /// Failed to set all leaves, as more than `u32::MAX` sub-leaves are present. + #[error("Failed to set all leaves, as more than `u32::MAX` sub-leaves are present: {0}")] + Overflow(>::Error), +} + +// We use this 2nd implementation so we can conveniently define functions only used within +// `normalize`. +#[allow(clippy::multiple_inherent_impl)] +impl super::IntelCpuid { + /// Applies required modifications to CPUID respective of a vCPU. + /// + /// # Errors + /// + /// When attempting to access missing leaves or set fields within leaves to values that don't + /// fit. + #[inline] + pub fn normalize( + &mut self, + // The index of the current logical CPU in the range [0..cpu_count]. + cpu_index: u8, + // The total number of logical CPUs. + cpu_count: u8, + // The number of bits needed to enumerate logical CPUs per core. + cpu_bits: u8, + ) -> Result<(), NormalizeCpuidError> { + let cpus_per_core = 1u8 + .checked_shl(u32::from(cpu_bits)) + .ok_or(NormalizeCpuidError::CpuBits(cpu_bits))?; + + self.update_deterministic_cache_entry(cpu_count, cpus_per_core)?; + self.update_power_management_entry()?; + self.update_performance_monitoring_entry()?; + self.update_extended_topology_entry(cpu_index, cpu_count, cpu_bits, cpus_per_core)?; + self.update_brand_string_entry()?; + + Ok(()) + } + + /// Update deterministic cache entry + fn update_deterministic_cache_entry( + &mut self, + cpu_count: u8, + cpus_per_core: u8, + ) -> Result<(), DeterministicCacheError> { + let leaf_4 = self.leaf_mut::<0x4>(); + for subleaf in leaf_4.0 { + match u32::from(&subleaf.eax.cache_level()) { + // L1 & L2 Cache + // The L1 & L2 cache is shared by at most 2 hyperthreads + 1 | 2 => subleaf + .eax + .max_num_addressable_ids_for_logical_processors_sharing_this_cache_mut() + // SAFETY: We know `cpus_per_core > 0` therefore this is always safe. + .checked_assign(u32::from(unsafe { + cpus_per_core.checked_sub(1).unwrap_unchecked() + })) + .map_err(DeterministicCacheError::MaxCpusPerCore)?, + // L3 Cache + // The L3 cache is shared among all the logical threads + 3 => subleaf + .eax + .max_num_addressable_ids_for_logical_processors_sharing_this_cache_mut() + .checked_assign(u32::from( + cpu_count + .checked_sub(1) + .ok_or(DeterministicCacheError::MaxCpusPerCoreUnderflow)?, + )) + .map_err(DeterministicCacheError::MaxCpusPerCore)?, + _ => (), + } + // SAFETY: We know `cpus_per_core !=0` therefore this is always safe. + let cores = unsafe { cpu_count.checked_div(cpus_per_core).unwrap_unchecked() }; + // Put all the cores in the same socket + subleaf + .eax + .max_num_addressable_ids_for_processor_cores_in_physical_package_mut() + .checked_assign( + u32::from(cores) + .checked_sub(1) + .ok_or(DeterministicCacheError::MaxCorePerPackageUnderflow)?, + ) + .map_err(DeterministicCacheError::MaxCorePerPackage)?; + } + Ok(()) + } + + /// Update power management entry + fn update_power_management_entry(&mut self) -> Result<(), NormalizeCpuidError> { + let leaf_6 = self + .leaf_mut::<0x6>() + .ok_or(NormalizeCpuidError::MissingLeaf6)?; + leaf_6.eax.intel_turbo_boost_technology_mut().off(); + // Clear X86 EPB feature. No frequency selection in the hypervisor. + leaf_6.ecx.performance_energy_bias_mut().off(); + Ok(()) + } + + /// Update performance monitoring entry + fn update_performance_monitoring_entry(&mut self) -> Result<(), NormalizeCpuidError> { + let leaf_a = self + .leaf_mut::<0xA>() + .ok_or(NormalizeCpuidError::MissingLeafA)?; + *leaf_a = super::leaves::LeafA::from(( + registers::LeafAEax::from(0), + registers::LeafAEbx::from(0), + registers::LeafAEcx::from(0), + registers::LeafAEdx::from(0), + )); + Ok(()) + } + + /// Update extended topology entry + fn update_extended_topology_entry( + &mut self, + cpu_index: u8, + cpu_count: u8, + cpu_bits: u8, + cpus_per_core: u8, + ) -> Result<(), ExtendedTopologyError> { + /// Level type used for setting thread level processor topology. + const LEVEL_TYPE_THREAD: u32 = 1; + /// Level type used for setting core level processor topology. + const LEVEL_TYPE_CORE: u32 = 2; + /// 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 128 logical + /// processors/package. + const LEAFBH_INDEX1_APICID: u32 = 7; + + let leaf_b = self.leaf_mut::<0xB>(); + for (index, subleaf) in leaf_b.0.into_iter().enumerate() { + // reset eax, ebx, ecx + subleaf.eax.0 = 0; + subleaf.ebx.0 = 0; + subleaf.ecx.0 = 0; + // 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 + subleaf.edx.0 = u32::from(cpu_index); + + // "If SMT is not present in a processor implementation but CPUID leaf 0BH is + // supported, CPUID.EAX=0BH, ECX=0 will return EAX = 0, EBX = 1 and + // level type = 1. Number of logical processors at the core level is + // reported at level type = 2." (IntelĀ® 64 Architecture x2APIC + // Specification, Ch. 2.8) + match 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. + subleaf + .eax + .bit_shifts_right_2x_apic_id_unique_topology_id_mut() + .checked_assign(u32::from(cpu_bits)) + .map_err(ExtendedTopologyError::ApicId)?; + // When cpu_count == 1 or HT is disabled, there is 1 logical core at this + // level Otherwise there are 2 + subleaf + .ebx + .logical_processors_mut() + .checked_assign(u32::from(cpus_per_core)) + .map_err(ExtendedTopologyError::LogicalProcessors)?; + + subleaf + .ecx + .level_type_mut() + .checked_assign(LEVEL_TYPE_THREAD) + .map_err(ExtendedTopologyError::LevelType)?; + } + // Core Level Processor Topology; index = 1 + 1 => { + subleaf + .eax + .bit_shifts_right_2x_apic_id_unique_topology_id_mut() + .checked_assign(LEAFBH_INDEX1_APICID) + .map_err(ExtendedTopologyError::ApicId)?; + subleaf + .ebx + .logical_processors_mut() + .checked_assign(u32::from(cpu_count)) + .map_err(ExtendedTopologyError::LogicalProcessors)?; + // We expect here as this is an extremely rare case that is unlikely to ever + // occur. It would require manual editing of the CPUID structure to push + // more than 2^32 subleaves. + subleaf + .ecx + .level_number_mut() + .checked_assign( + u32::try_from(index).map_err(ExtendedTopologyError::Overflow)?, + ) + .map_err(ExtendedTopologyError::LevelNumber)?; + subleaf + .ecx + .level_type_mut() + .checked_assign(LEVEL_TYPE_CORE) + .map_err(ExtendedTopologyError::LevelType)?; + } + // Core Level Processor Topology; index >=2 + // No other levels available; This should already be set correctly, + // and it is added here as a "re-enforcement" in case we run on + // different hardware + _ => { + // We expect here as this is an extremely rare case that is unlikely to ever + // occur. It would require manual editing of the CPUID structure to push + // more than 2^32 subleaves. + subleaf.ecx.0 = + u32::try_from(index).map_err(ExtendedTopologyError::Overflow)?; + } + } + } + Ok(()) + } + + /// Update brand string entry + fn update_brand_string_entry(&mut self) -> Result<(), NormalizeCpuidError> { + let default_brand_string = + Self::default_brand_string().map_err(NormalizeCpuidError::GetBrandString)?; + + self.apply_brand_string(&default_brand_string) + .map_err(NormalizeCpuidError::ApplyBrandString)?; + Ok(()) + } +} diff --git a/src/cpuid/src/intel/registers.rs b/src/cpuid/src/intel/registers.rs new file mode 100644 index 00000000000..25fb37c3bd9 --- /dev/null +++ b/src/cpuid/src/intel/registers.rs @@ -0,0 +1,1715 @@ +// Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +use bit_fields::bitfield; + +// ------------------------------------------------------------------------------------------------- +// Leaf 3 +// ------------------------------------------------------------------------------------------------- + +bitfield!(Leaf3Eax, u32, { + // Reserved. +}); + +bitfield!(Leaf3Ebx, u32, { + // Reserved. +}); + +bitfield!(Leaf3Ecx, u32, { + /// Bits 00 - 31 of 96 bit processor serial number. (Available in Pentium III processor only; + /// otherwise, the value in this register is reserved.) + bit_processor_serial_number_00_31: 0..32, +}); + +bitfield!(Leaf3Edx, u32, { + /// Bits 32 - 63 of 96 bit processor serial number. (Available in Pentium III processor only; + /// otherwise, the value in this register is reserved.) + bit_processor_serial_number_32_63: 0..32, +}); +// ------------------------------------------------------------------------------------------------- +// Leaf 4 +// ------------------------------------------------------------------------------------------------- + +bitfield!(Leaf4Eax, u32, { + /// Cache Type Field. + /// - 0 = Null - No more caches. + /// - 1 = Data Cache. + /// - 2 = Instruction Cache. + /// - 3 = Unified Cache. + /// - 4-31 = Reserved. + cache_type_field: 0..5, + /// Cache Level (starts at 1). + cache_level: 5..8, + /// Self Initializing cache level (does not need SW initialization). + sicl: 8, + /// Fully Associative cache. + fac: 9, + // Reserved 10..14 + /// Maximum number of addressable IDs for logical processors sharing this cache. + /// - Add one to the return value to get the result. + /// - The nearest power-of-2 integer that is not smaller than (1 + EAX[25:14]) is the number of + /// unique initial APIC IDs reserved for addressing different logical processors sharing this + /// cache. + max_num_addressable_ids_for_logical_processors_sharing_this_cache: 14..26, + /// Maximum number of addressable IDs for processor cores in the physical package. + /// - Add one to the return value to get the result. + /// - The nearest power-of-2 integer that is not smaller than (1 + EAX[31:26]) is the number of + /// unique Core_IDs reserved for addressing different processor cores in a physical package. + /// Core ID is a subset of bits of the initial APIC ID. + /// - The returned value is constant for valid initial values in ECX. Valid ECX values start + /// from 0. + max_num_addressable_ids_for_processor_cores_in_physical_package: 26..32, +}); + +bitfield!(Leaf4Ebx, u32, { + /// L = System Coherency Line Size. + /// + /// Add one to the return value to get the result. + system_coherency_line_size: 0..12, + /// P = Physical Line partitions. + /// + /// Add one to the return value to get the result. + physical_line_partitions: 12..22, + /// W = Ways of associativity. + /// + /// Add one to the return value to get the result. + ways_of_associativity: 22..32 +}); + +bitfield!(Leaf4Ecx, u32, { + /// S = Number of Sets. + /// + /// Add one to the return value to get the result. + number_of_sets: 0..32, +}); + +bitfield!(Leaf4Edx, u32, { + /// Write-Back Invalidate/Invalidate. + /// - 0 = WBINVD/INVD from threads sharing this cache acts upon lower level caches for threads + /// sharing this cache. + /// - 1 = WBINVD/INVD is not guaranteed to act upon lower level caches of non-originating + /// threads sharing this cache. + write_back_invalidate: 0, + /// Cache Inclusiveness. + /// - 0 = Cache is not inclusive of lower cache levels. + /// - 1 = Cache is inclusive of lower cache levels. + cache_inclusiveness: 1, + /// Complex Cache Indexing. + /// - 0 = Direct mapped cache. + /// - 1 = A complex function is used to index the cache, potentially using all address bits. + complex_cache_indexing: 2, +}); +// ------------------------------------------------------------------------------------------------- +// Leaf 5 +// ------------------------------------------------------------------------------------------------- + +bitfield!(Leaf5Eax, u32, { + /// Smallest monitor-line size in bytes (default is processor's monitor granularity). + smallest_monitor_line_size: 0..16, + // Reserved +}); + +bitfield!(Leaf5Ebx, u32, { + /// Largest monitor-line size in bytes (default is processor's monitor granularity). + largest_monitor_line_size: 0..16, + // Reserved +}); + +bitfield!(Leaf5Ecx, u32, { + /// Enumeration of Monitor-Mwait extensions (beyond EAX and EBX registers) supported. + enum_monitor_mwait_ext: 0, + /// Supports treating interrupts as break-event for MWAIT, even when interrupts disabled. + support_treating_interrupts_as_break_events_for_mwait: 1, + // Reserved +}); + +bitfield!(Leaf5Edx, u32, { + /// Number of C0* sub C-states supported using MWAIT. + /// + /// The definition of C0 through C7 states for MWAIT extension are processor-specific C-states, + /// not ACPI Cstates. + c0_states: 0..4, + /// Number of C1* sub C-states supported using MWAIT. + /// + /// The definition of C0 through C7 states for MWAIT extension are processor-specific C-states, + /// not ACPI Cstates. + c1_states: 4..8, + /// Number of C2* sub C-states supported using MWAIT. + /// + /// The definition of C0 through C7 states for MWAIT extension are processor-specific C-states, + /// not ACPI Cstates. + c2_states: 8..12, + /// Number of C3* sub C-states supported using MWAIT. + /// + /// The definition of C0 through C7 states for MWAIT extension are processor-specific C-states, + /// not ACPI Cstates. + c3_states: 12..16, + /// Number of C4* sub C-states supported using MWAIT. + /// + /// The definition of C0 through C7 states for MWAIT extension are processor-specific C-states, + /// not ACPI Cstates. + c4_states: 16..20, + /// Number of C5* sub C-states supported using MWAIT. + /// + /// The definition of C0 through C7 states for MWAIT extension are processor-specific C-states, + /// not ACPI Cstates. + c5_states: 20..24, + /// Number of C6* sub C-states supported using MWAIT. + /// + /// The definition of C0 through C7 states for MWAIT extension are processor-specific C-states, + /// not ACPI Cstates. + c6_states: 24..28, + /// Number of C7* sub C-states supported using MWAIT. + /// + /// The definition of C0 through C7 states for MWAIT extension are processor-specific C-states, + /// not ACPI Cstates. + c7_states: 28..32, +}); +// ------------------------------------------------------------------------------------------------- +// Leaf 6 +// ------------------------------------------------------------------------------------------------- + +bitfield!(Leaf6Eax, u32, { + /// Digital temperature sensor is supported if set. + digital_temperature_sensor: 0, + /// Intel Turbo Boost Technology available (see description of IA32_MISC_ENABLE[38]). + intel_turbo_boost_technology: 1, + /// ARAT. APIC-Timer-always-running feature is supported if set. + arat: 2, + // Reserved + /// PLN. Power limit notification controls are supported if set. + pln: 4, + /// ECMD. Clock modulation duty cycle extension is supported if set. + ecmd: 5, + /// PTM. Package thermal management is supported if set. + ptm: 6, + /// HWP. HWP base registers (IA32_PM_ENABLE[bit 0], IA32_HWP_CAPABILITIES, IA32_HWP_REQUEST, + /// IA32_HWP_STATUS) are supported if set. + hwp: 7, + /// HWP_Notification. IA32_HWP_INTERRUPT MSR is supported if set. + hwp_notification: 8, + /// HWP_Activity_Window. IA32_HWP_REQUEST[bits 41:32] is supported if set. + hwp_activity_window: 9, + /// HWP_Energy_Performance_Preference. IA32_HWP_REQUEST[bits 31:24] is supported if set. + hwp_energy_performance: 10, + /// HWP_Package_Level_Request. IA32_HWP_REQUEST_PKG MSR is supported if set. + hwp_package_level_request: 11, + // Reserved + /// HDC. HDC base registers IA32_PKG_HDC_CTL, IA32_PM_CTL1, IA32_THREAD_STALL MSRs are supported + /// if set. + hdc: 13, + /// IntelĀ® Turbo Boost Max Technology 3.0 available. + intel_turbo_boost_max_technology_3: 14, + /// HWP Capabilities. Highest Performance change is supported if set. + hwp_capabilities: 15, + /// HWP PECI override is supported if set. + hwp_peci_override: 16, + /// Flexible HWP is supported if set. + flexible_hwp: 17, + // Fast access mode for the IA32_HWP_REQUEST MSR is supported if set. + fast_access_mode_for_i32_hwp_request_msr: 18, + /// HW_FEEDBACK. IA32_HW_FEEDBACK_PTR MSR, IA32_HW_FEEDBACK_CONFIG MSR, + /// IA32_PACKAGE_THERM_STATUS MSR bit 26, and IA32_PACKAGE_THERM_INTERRUPT MSR bit 25 are + /// supported if set. + hw_feedback: 19, + // Ignoring Idle Logical Processor HWP request is supported if set. + iilp_hwp_r: 20, + // Reserved 21..=22 + /// IntelĀ® Thread Director supported if set. IA32_HW_FEEDBACK_CHAR and + /// IA32_HW_FEEDBACK_THREAD_CONFIG MSRs are supported if set. + intel_thread_director: 23, + // Reserved 24..=31 + +}); + +bitfield!(Leaf6Ebx, u32, { + /// Number of Interrupt Thresholds in Digital Thermal Sensor. + number_of_interrupt_thresholds_in_digital_thermal_sensor: 0..4, + // Reserved 4..=31 +}); + +bitfield!(Leaf6Ecx, u32, { + /// Hardware Coordination Feedback Capability (Presence of IA32_MPERF and IA32_APERF). The + /// capability to provide a measure of delivered processor performance (since last reset of the + /// counters), as a percentage of the expected processor performance when running at the TSC + /// frequency. + hardware_coordination_feedback_capability: 0, + // Reserved 1..=2 + /// The processor supports performance-energy bias preference if CPUID.06H:ECX.SETBH[bit 3] is + /// set and it also implies the presence of a new architectural MSR called IA32_ENERGY_PERF_BIAS + /// (1B0H). + performance_energy_bias: 3, + /// Reserved 04..=07 + /// Number of IntelĀ® Thread Director classes supported by the processor. Information for that + /// many classes is written into the Intel Thread Director Table by the hardware. + intel_thread_director_classes: 8..16, + // Reserved 16..=31 +}); + +bitfield!(Leaf6Edx, u32, { + /// Bitmap of supported hardware feedback interface capabilities. + /// - 0 = When set to 1, indicates support for performance capability reporting. + /// - 1 = When set to 1, indicates support for energy efficiency capability reporting. + /// - 2-7 = Reserved + bitmap_hardware_feedback_interface_capabilities: 0..8, + /// Enumerates the size of the hardware feedback interface structure in number of 4 KB pages; + /// add one to the return value to get the result. + enum_hardware_feedback_interface_4k: 8..12, + /// Index (starting at 0) of this logical processor's row in the hardware feedback interface + /// structure. Note that on some parts the index may be same for multiple logical processors. On + /// some parts the indices may not be contiguous, i.e., there may be unused rows in the hardware + /// feedback interface structure. + index: 16..32 +}); +// ------------------------------------------------------------------------------------------------- +// Leaf 7 +// ------------------------------------------------------------------------------------------------- + +bitfield!(Leaf7Subleaf0Eax, u32, { + /// Reports the maximum input value for supported leaf 7 sub-leaves. + max_input_value_subleaf: 0..32 +}); + +bitfield!(Leaf7Subleaf0Ebx, u32, { + /// FSGSBASE. Supports RDFSBASE/RDGSBASE/WRFSBASE/WRGSBASE if 1. + fsgsbase: 0, + /// IA32_TSC_ADJUST MSR is supported if 1. + ia32_tsc_adjust_msr: 1, + /// SGX. Supports IntelĀ® Software Guard Extensions (IntelĀ® SGX Extensions) if 1. + sgx: 2, + /// BMI1. + bmi1: 3, + /// HLE. + hle: 4, + /// AVX2. + avx2: 5, + /// FDP_EXCPTN_ONLY. x87 FPU Data Pointer updated only on x87 exceptions if 1. + fdp_excptn_only: 6, + /// SMEP. Supports Supervisor-Mode Execution Prevention if 1. + smep: 7, + /// BMI2. + bmi2: 8, + /// Supports Enhanced REP MOVSB/STOSB if 1. + suports_enhanced_rep_movsb_stosb: 9, + /// INVPCID. If 1, supports INVPCID instruction for system software that manages process-context + /// identifiers. + invpcid: 10, + /// RTM. + rtm: 11, + /// RDT-M. Supports IntelĀ® Resource Director Technology (IntelĀ® RDT) Monitoring capability if 1. + rdt_m: 12, + /// Deprecates FPU CS and FPU DS values if 1. + deprecates_fpu_cs_and_fpu_ds: 13, + /// MPX. Supports IntelĀ® Memory Protection Extensions if 1. + mpx: 14, + /// RDT-A. Supports IntelĀ® Resource Director Technology (IntelĀ® RDT) Allocation capability if 1. + rdt_t: 15, + /// AVX512F. + avx512f: 16, + /// AVX512DQ. + avx512dq: 17, + /// RDSEED. + rdseed: 18, + /// ADX. + adx: 19, + /// SMAP. Supports Supervisor-Mode Access Prevention (and the CLAC/STAC instructions) if 1. + smap: 20, + /// AVX512_IFMA. + avx512_ifma: 21, + // Reserved + /// CLFLUSHOPT. + clfushopt: 23, + /// CLWB. + clwb: 24, + /// Intel Processor Trace. + intel_processor_trace: 25, + /// AVX512PF. (IntelĀ® Xeon Phiā„¢ only.) + avx512pf: 26, + /// AVX512ER. (IntelĀ® Xeon Phiā„¢ only.) + avx512er: 27, + /// AVX512CD. + avx512cd: 28, + /// SHA. supports IntelĀ® Secure Hash Algorithm Extensions (IntelĀ® SHA Extensions) if 1. + sha: 29, + /// AVX512BW. + avx512bw: 30, + /// AVX512VL. + avx512vl: 31 +}); + +bitfield!(Leaf7Subleaf0Ecx, u32, { + /// PREFETCHWT1. (IntelĀ® Xeon Phiā„¢ only.) + prefetchwt1: 0, + /// AVX512_VBMI. + avx512_vbmi: 1, + /// UMIP. Supports user-mode instruction prevention if 1. + umip: 2, + /// PKU. Supports protection keys for user-mode pages if 1. + pku: 3, + /// OSPKE. If 1, OS has set CR4.PKE to enable protection keys (and the RDPKRU/WRPKRU instructions). + ospke: 4, + /// WAITPKG. + waitpkg: 5, + /// AVX512_VBMI2. + avx512_vbmi2: 6, + /// CET_SS. Supports CET shadow stack features if 1. Processors that set this bit define bits + /// 1:0 of the IA32_U_CET and IA32_S_CET MSRs. Enumerates support for the following MSRs: + /// IA32_INTERRUPT_SPP_TABLE_ADDR, IA32_PL3_SSP, IA32_PL2_SSP, IA32_PL1_SSP, and IA32_PL0_SSP. + cet_ss: 7, + /// GFNI. + gfni: 8, + /// VAES. + vaes: 9, + /// VPCLMULQDQ. + vpclmulqdq: 10, + /// AVX512_VNNI. + avx512_vnni: 11, + /// AVX512_BITALG. + avx512_bitalg: 12, + /// TME_EN. If 1, the following MSRs are supported: IA32_TME_CAPABILITY, IA32_TME_ACTIVATE, + /// IA32_TME_EXCLUDE_MASK, and IA32_TME_EXCLUDE_BASE. + tme_en: 13, + /// AVX512_VPOPCNTDQ. + avx512_vpopcntdq: 14, + // Reserved + /// LA57. Supports 57-bit linear addresses and five-level paging if 1. + la57: 16, + /// The value of MAWAU used by the BNDLDX and BNDSTX instructions in 64-bit mode. + value_of_mawau: 17..22, + /// RDPID and IA32_TSC_AUX are available if 1. + rdpid_and_ia32_tsc_aux: 22, + /// KL. Supports Key Locker if 1. + kl: 23, + // Reserved + /// CLDEMOTE. Supports cache line demote if 1. + cldemote: 25, + // Reserved + /// MOVDIRI. Supports MOVDIRI if 1. + movdiri: 27, + /// MOVDIR64B. Supports MOVDIR64B if 1. + movdiri64b: 28, + // Reserved + /// SGX_LC. Supports SGX Launch Configuration if 1. + sgx_lc: 30, + /// PKS. Supports protection keys for supervisor-mode pages if 1. + pks: 31 +}); + +bitfield!(Leaf7Subleaf0Edx, u32, { + // Reserved + /// AVX512_4VNNIW. (IntelĀ® Xeon Phiā„¢ only.) + avx512_4vnniw: 2, + /// AVX512_4FMAPS. (IntelĀ® Xeon Phiā„¢ only.) + avx512_4fmaps: 3, + /// Fast Short REP MOV. + fast_short_rep_mov: 4, + // Reserved 5..=7 + /// AVX512_VP2INTERSECT. + avx512_vp2intersect: 8, + // Reserved + /// MD_CLEAR supported. + md_clear: 10, + // Reserved + /// SERIALIZE. + serialize: 11..14, + /// Hybrid. If 1, the processor is identified as a hybrid part. + hydrid: 15, + // Reserved 16..=17 + /// PCONFIG. Supports PCONFIG if 1. + pconfig: 18, + // Reserved + /// CET_IBT. Supports CET indirect branch tracking features if 1. Processors that set this bit + /// define bits 5:2 and bits 63:10 of the IA32_U_CET and IA32_S_CET MSRs. + cet_ibt: 19, + // Reserved 21..=25 + /// Enumerates support for indirect branch restricted speculation (IBRS) and the indirect branch + /// predictorn barrier (IBPB). Processors that set this bit support the IA32_SPEC_CTRL MSR and + /// the A32_PRED_CMD MSR. They allow software to set IA32_SPEC_CTRL[0] (IBRS) and + /// IA32_PRED_CMD[0] (IBPB). + ibrs_ibpb_enum: 26, + /// Enumerates support for single thread indirect branch predictors (STIBP). Processors that set + /// this bit support the IA32_SPEC_CTRL MSR. They allow software to set IA32_SPEC_CTRL[1] + /// (STIBP). + stibp_enum: 27, + /// Enumerates support for L1D_FLUSH. Processors that set this bit support the IA32_FLUSH_CMD + /// MSR. They allow software to set IA32_FLUSH_CMD[0] (L1D_FLUSH). + l1d_flush_enum: 28, + /// Enumerates support for the IA32_ARCH_CAPABILITIES MSR. + ia32_arch_capabilities_msr_enum: 29, + /// Enumerates support for the IA32_CORE_CAPABILITIES MSR. + ia32_core_capabilities_msr_enum: 30, + /// Enumerates support for Speculative Store Bypass Disable (SSBD). Processors that set this bit + /// support the IA32_SPEC_CTRL MSR. They allow software to set IA32_SPEC_CTRL[2] (SSBD). + ssbd_enum: 31, +}); + +bitfield!(Leaf7Subleaf1Eax, u32, { + // Reserved 0..=3 + /// AVX-VNNI. AVX (VEX-encoded) versions of the Vector Neural Network Instructions. + avx_vnni: 4, + /// AVX512_BF16. Vector Neural Network Instructions supporting BFLOAT16 inputs and conversion + /// instructions from IEEE single precision. + avx512_bf16: 5, + // Reserved 6..=9 + /// If 1, supports fast zero-length REP MOVSB. + fast_zero_length_rep_movsh: 10, + /// If 1, supports fast short REP STOSB. + fast_short_rep_stosb: 11, + /// If 1, supports fast short REP CMPSB, REP SCASB. + fast_short_rep_cmpsb_rep_scasb: 12, + // Reserved 13..=21 + /// HRESET. If 1, supports history reset via the HRESET instruction and the IA32_HRESET_ENABLE + /// MSR. When set, indicates that the Processor History Reset Leaf (EAX = 20H) is valid. + hreset: 22, + // Reserved 23..=31 +}); + +bitfield!(Leaf7Subleaf1Ebx, u32, { + /// Enumerates the presence of the IA32_PPIN and IA32_PPIN_CTL MSRs. If 1, these MSRs are + /// supported. + ia32_ppin_and_ia32_ppin_ctl_msrs_enum: 0 + // Reserved 1..=31 +}); + +bitfield!(Leaf7Subleaf1Ecx, u32, { + // Reserved +}); + +bitfield!(Leaf7Subleaf1Edx, u32, { + // Reserved +}); +// ------------------------------------------------------------------------------------------------- +// Leaf 9 +// ------------------------------------------------------------------------------------------------- + +bitfield!(Leaf9Eax, u32, { + /// Value of bits [31:0] of IA32_PLATFORM_DCA_CAP MSR (address 1F8H). + ia32_platform_dca_cap_msr: 0..32 +}); + +bitfield!(Leaf9Ebx, u32, { + // Reserved +}); + +bitfield!(Leaf9Ecx, u32, { + // Reserved +}); + +bitfield!(Leaf9Edx, u32, { + // Reserved +}); +// ------------------------------------------------------------------------------------------------- +// Leaf A +// ------------------------------------------------------------------------------------------------- + +bitfield!(LeafAEax, u32, { + /// Version ID of architectural performance monitoring. + version_id_of_architectural_performance_monitoring: 0..8, + /// Number of general-purpose performance monitoring counter per logical processor. + num_perf_monitor_counter_per_logical_processor: 8..16, + /// Bit width of general-purpose, performance monitoring counter. + bot_width_perf_monitor_counter: 16..24, + /// Length of EBX bit vector to enumerate architectural performance monitoring events. + /// Architectural event x is supported if EBX[x]=0 && EAX[31:24]>x. + len_ebx_bit_vec: 24..32 +}); + +bitfield!(LeafAEbx, u32, { + /// Core cycle event not available if 1 or if EAX[31:24]<1. + core_cycle_event: 0, + /// Instruction retired event not available if 1 or if EAX[31:24]<2. + instruction_retired_event: 1, + /// Reference cycles event not available if 1 or if EAX[31:24]<3. + reference_cycles_event: 2, + /// Last-level cache reference event not available if 1 or if EAX[31:24]<4. + last_level_cache_reference_event: 3, + /// Last-level cache misses event not available if 1 or if EAX[31:24]<5. + last_level_cache_misses_event: 4, + /// Branch instruction retired event not available if 1 or if EAX[31:24]<6. + branch_instruction_retired_event: 5, + /// Branch mispredict retired event not available if 1 or if EAX[31:24]<7. + branch_mispredict_retired_event: 6, + /// Top-down slots event not available if 1 or if EAX[31:24]<8. + top_down_slots_event: 7, + // Reserved 8..=31 +}); + +bitfield!(LeafAEcx, u32, { + /// Supported fixed counters bit mask. Fixed-function performance counter 'i' is supported if + /// bit ā€˜iā€™ is 1 (first counter index starts at zero). It is recommended to use the following + /// logic to determine if a Fixed Counter is supported: + /// FxCtr[i]_is_supported := ECX[i] || (EDX[4:0] > i); + supported_fixed_counters_bit_mask: 0..32 +}); + +bitfield!(LeafAEdx, u32, { + /// Number of contiguous fixed-function performance counters starting from 0 (if Version ID >1). + contigous_fixed_function_performance_counter: 0..5, + /// Bit width of fixed-function performance counters (if Version ID > 1). + bit_width_of_fixed_function_performnace_counter: 5..13, + // Reserved 13..=14 + /// AnyThread deprecation. + anythread_deprecation: 15 + // Reserved 16..=31 +}); +// ------------------------------------------------------------------------------------------------- +// Leaf B +// ------------------------------------------------------------------------------------------------- + +bitfield!(LeafBEax, u32, { + /// Number of bits to shift right on x2APIC ID to get a unique topology ID of the next level + /// type*. All logical processors with the same next level ID share current level. + /// + /// *Software should use this field (EAX[4:0]) to enumerate processor topology of the system. + bit_shifts_right_2x_apic_id_unique_topology_id: 0..5 +}); + +bitfield!(LeafBEbx, u32, { + /// Number of logical processors at this level type. The number reflects configuration as shipped + /// by Intel**. + /// + /// **Software must not use EBX[15:0] to enumerate processor topology of the system. This value + /// in this field (EBX[15:0]) is only intended for display/diagnostic purposes. The actual + /// number of logical processors available to BIOS/OS/Applications may be different from the + /// value of EBX[15:0], depending on software and platform hardware configurations. + logical_processors: 0..16 +}); + +bitfield!(LeafBEcx, u32, { + /// Level number. Same value in ECX input. + level_number: 0..8, + /// Level type*** + /// + /// If an input value n in ECX returns the invalid level-type of 0 in ECX[15:8], other input + /// values with ECX>n also return 0 in ECX[15:8]. + /// + /// ***The value of the ā€œlevel typeā€ field is not related to level numbers in any way, higher + /// ā€œlevel typeā€ values do not mean higher levels. Level type field has the following encoding: + /// - 0: Invalid. + /// - 1: SMT. + /// - 2: Core. + /// - 3-255: Reserved. + level_type: 8..16 + // Reserved 16..=31 +}); + +bitfield!(LeafBEdx, u32, { + /// x2APIC ID the current logical processor. + x2_apic_id_current_logical_processor: 0..32 +}); +// ------------------------------------------------------------------------------------------------- +// Leaf D +// ------------------------------------------------------------------------------------------------- +// Leaf 0 + +bitfield!(LeafDSubleaf0Eax, u32, { + // Bits 31 - 00: Reports the supported bits of the lower 32 bits of XCR0. XCR0[n] can be set to + // 1 only if EAX[n] is 1. + /// x87 state. + x86_state: 0, + /// SSE state. + sse_state: 1, + /// AVX state. + avx_state: 2, + /// MPX state. + mpx_state: 3..5, + /// AVX-512 state. + avx512_state: 5..8, + /// Used for IA32_XSS. + used_for_ia32_xss: 8, + /// PKRU state. + pkru_state: 9, + // Reserved 10..=12 + /// Used for IA32_XSS. + used_for_ia32_xss_1: 13, + // Reserved 14..=15 + /// Used for IA32_XSS. + used_for_ia32_xss_2: 16, + // Reserved 17..=31 +}); + +bitfield!(LeafDSubleaf0Ebx, u32, { + /// Maximum size (bytes, from the beginning of the XSAVE/XRSTOR save area) required by enabled + /// features in XCR0. May be different than ECX if some features at the end of the XSAVE save + /// area are not enabled. + maximum_size: 0..32 +}); + +bitfield!(LeafDSubleaf0Ecx, u32, { + /// Maximum size (bytes, from the beginning of the XSAVE/XRSTOR save area) of the XSAVE/XRSTOR + /// save area required by all supported features in the processor, i.e., all the valid bit + /// fields in XCR0. + /// + // `LeafDSubleaf0Ecx::maximum_size() >= LeafDSubleaf0Ebx::maximum_size()` + maximum_size: 0..32 +}); + +bitfield!(LeafDSubleaf0Edx, u32, { + // Reports the supported bits of the upper 32 bits of XCR0. XCR0[n+32] can be set to 1 only if + // EDX[n] is 1. + // Reserved +}); +// Leaf 1 + +bitfield!(LeafDSubleaf1Eax, u32, { + /// XSAVEOPT is available. + xsaveopt_available: 0, + /// Supports XSAVEC and the compacted form of XRSTOR if set. + xsavec_compacted_xrstor: 1, + /// Supports XGETBV with ECX = 1 if set. + xgetbv: 2, + /// Supports XSAVES/XRSTORS and IA32_XSS if set. + xsaves_xrstors_ia32_xss: 3, + // Reserved 0..32 +}); + +bitfield!(LeafDSubleaf1Ebx, u32, { + /// The size in bytes of the XSAVE area containing all states enabled by XCRO | IA32_XSS. + xsave_size: 0..32, +}); + +bitfield!(LeafDSubleaf1Ecx, u32, { + // Reports the supported bits of the lower 32 bits of the IA32_XSS MSR. IA32_XSS[n] can be set + // to 1 only if ECX[n] is 1. + /// Used for XCR0. + xcr0_1: 0..8, + /// PT state. + pt_state: 8, + /// Used for XCR0. + xcr0_2: 9, + // Reserved + /// CET user state. + cet_user_state: 11, + /// CET supervisor state. + cet_supervisor_state: 12, + /// HDC state. + hdc_state: 13, + // Reserved + /// LBR state (architectural). + lbr_state: 15, + /// HWP state. + hwp_state: 16, + // Reserved 17..=31 +}); + +bitfield!(LeafDSubleaf1Edx, u32, { + // Reports the supported bits of the upper 32 bits of the IA32_XSS MSR. IA32_XSS[n+32] can be + // set to 1 only if EDX[n] is 1. + // Reserved +}); +// Leaf >1 + +bitfield!(LeafDSubleafGt1Eax, u32, { + /// The size in bytes (from the offset specified in EBX) of the save area for an extended state + /// feature associated with a valid sub-leaf index, n. + save_area_size: 0..32, +}); + +bitfield!(LeafDSubleafGt1Ebx, u32, { + /// The offset in bytes of this extended state componentā€™s save area from the beginning of the + /// XSAVE/XRSTOR area. + /// + /// This field reports 0 if the sub-leaf index, n, does not map to a valid bit in the XCR0 + /// register*. + /// + /// *If ECX contains an invalid sub-leaf index, EAX/EBX/ECX/EDX return 0. Sub-leaf n + /// (0 ā‰¤ n ā‰¤ 31) is invalid if sub-leaf 0 returns 0 in EAX[n] and sub-leaf 1 returns 0 in + /// ECX[n]. Sub-leaf n (32 ā‰¤ n ā‰¤ 63) is invalid if sub-leaf 0 returns 0 in EDX[n-32] and + /// sub-leaf 1 returns 0 in EDX[n-32]. + save_area_offset: 0..32 +}); + +bitfield!(LeafDSubleafGt1Ecx, u32, { + /// Is set if the bit n (corresponding to the sub-leaf index) is supported in the IA32_XSS MSR; + /// it is clear if bit n is instead supported in XCR0. + /// + /// This field reports 0 if the sub-leaf index, n, is invalid*. + /// + /// *If ECX contains an invalid sub-leaf index, EAX/EBX/ECX/EDX return 0. Sub-leaf n + /// (0 ā‰¤ n ā‰¤ 31) is invalid if sub-leaf 0 returns 0 in EAX[n] and sub-leaf 1 returns 0 in + /// ECX[n]. Sub-leaf n (32 ā‰¤ n ā‰¤ 63) is invalid if sub-leaf 0 returns 0 in EDX[n-32] and + /// sub-leaf 1 returns 0 in EDX[n-32]. + supported_ia32_xss_msr: 0, + /// Is set if, when the compacted format of an XSAVE area is used, this extended state component + /// located on the next 64-byte boundary following the preceding state component (otherwise, it + /// is located immediately following the preceding state component). + /// + /// This field reports 0 if the sub-leaf index, n, is invalid*. + /// + /// *If ECX contains an invalid sub-leaf index, EAX/EBX/ECX/EDX return 0. Sub-leaf n + /// (0 ā‰¤ n ā‰¤ 31) is invalid if sub-leaf 0 returns 0 in EAX[n] and sub-leaf 1 returns 0 in + /// ECX[n]. Sub-leaf n (32 ā‰¤ n ā‰¤ 63) is invalid if sub-leaf 0 returns 0 in EDX[n-32] and + /// sub-leaf 1 returns 0 in EDX[n-32]. + compacted_xsave_used: 1, + // 0..=31 reserved +}); + +bitfield!(LeafDSubleafGt1Edx, u32, { + // This field reports 0 if the sub-leaf index, n, is invalid*; otherwise it is reserved. + // 0..=31 reserved +}); +// ------------------------------------------------------------------------------------------------- +// Leaf F +// ------------------------------------------------------------------------------------------------- +// Leaf 0 + +bitfield!(LeafFSubleaf0Eax, u32, { + // Reserved +}); + +bitfield!(LeafFSubleaf0Ebx, u32, { + /// Maximum range (zero-based) of RMID within this physical processor of all types. + max_rmid_range: 0..32, +}); + +bitfield!(LeafFSubleaf0Ecx, u32, { + // Reserved +}); + +bitfield!(LeafFSubleaf0Edx, u32, { + // Reserved + /// Supports L3 Cache Intel RDT Monitoring if 1. + l3_rdt_monitor: 1, + // 2..=32 reserved +}); +// Leaf 1 + +bitfield!(LeafFSubleaf1Eax, u32, { + // Reserved +}); + +bitfield!(LeafFSubleaf1Ebx, u32, { + /// Conversion factor from reported IA32_QM_CTR value to occupancy metric (bytes) and Memory + /// Bandwidth Monitoring (MBM) metrics. + ia32_qm_ctr_conv_factor: 0..32, +}); + +bitfield!(LeafFSubleaf1Ecx, u32, { + /// Maximum range (zero-based) of RMID of this resource type. + rmid_max: 0..32, +}); + +bitfield!(LeafFSubleaf1Edx, u32, { + /// Supports L3 occupancy monitoring if 1. + l3_occupancy_monitor: 0, + /// Supports L3 Total Bandwidth monitoring if 1. + l3_total_band_monitor: 1, + /// Supports L3 Local Bandwidth monitoring if 1. + l3_local_band_monitor: 2, + // 0..=31 reserved +}); +// ------------------------------------------------------------------------------------------------- +// Leaf 10 +// ------------------------------------------------------------------------------------------------- +// Leaf 0 + +bitfield!(Leaf10Subleaf0Eax, u32, { + // Reserved +}); + +bitfield!(Leaf10Subleaf0Ebx, u32, { + // Reserved + /// Supports L3 Cache Allocation Technology if 1. + l3_alloc: 1, + /// Supports L2 Cache Allocation Technology if 1. + l2_alloc: 2, + /// Supports Memory Bandwidth Allocation if 1. + mem_band_alloc: 3, + // 04..=31 reserved +}); + +bitfield!(Leaf10Subleaf0Ecx, u32, { + // Reserved +}); + +bitfield!(Leaf10Subleaf0Edx, u32, { + // Reserved +}); +// Leaf 1 + +bitfield!(Leaf10Subleaf1Eax, u32, { + /// Length of the capacity bit mask for the corresponding ResID. Add one to the return value to + /// get the result. + len_cap_resid_mask: 0..5, + // 5..=31 reserved +}); + +bitfield!(Leaf10Subleaf1Ebx, u32, { + /// Bit-granular map of isolation/contention of allocation units. + granular_iso_cont_map: 0..32, +}); + +bitfield!(Leaf10Subleaf1Ecx, u32, { + // 0..=1 reserved + /// Code and Data Prioritization Technology supported if 1. + cd_prior: 2, + // 3..=31 reserved +}); + +bitfield!(Leaf10Subleaf1Edx, u32, { + /// Highest COS number supported for this ResID. + highest_cos_resid: 0..16, + // 0..=31 reserved +}); +// Leaf 2 + +bitfield!(Leaf10Subleaf2Eax, u32, { + /// Length of the capacity bit mask for the corresponding ResID. Add one to the return value to + /// get the result. + len_cap_resid_mask: 0..5, + // 5..=31 reserved +}); + +bitfield!(Leaf10Subleaf2Ebx, u32, { + /// Bit-granular map of isolation/contention of allocation units. + granular_iso_cont_map: 0..32, +}); + +bitfield!(Leaf10Subleaf2Ecx, u32, { + // Reserved. +}); + +bitfield!(Leaf10Subleaf2Edx, u32, { + /// Highest COS number supported for this ResID. + highest_cos_resid: 0..16, + // 0..=31 reserved +}); +// Leaf 3 + +bitfield!(Leaf10Subleaf3Eax, u32, { + /// Reports the maximum MBA throttling value supported for the corresponding ResID. Add one to + /// the return value to get the result. + max_mba_throt_resid: 0..12, + // reserved 12..=31 +}); + +bitfield!(Leaf10Subleaf3Ebx, u32, { + // Reserved +}); + +bitfield!(Leaf10Subleaf3Ecx, u32, { + // 0..=1 reserved + /// Reports whether the response of the delay values is linear. + linear_response_delay_values: 2, + // 3..=31 reserved +}); + +bitfield!(Leaf10Subleaf3Edx, u32, { + /// Highest COS number supported for this ResID. + highest_cos_resid: 0..16, + // 16..=31 reserved +}); +// ------------------------------------------------------------------------------------------------- +// Leaf 12 +// ------------------------------------------------------------------------------------------------- +// Leaf 0 + +bitfield!(Leaf12Subleaf0Eax, u32, { + /// SGX1. If 1, Indicates Intel SGX supports the collection of SGX1 leaf functions. + sgx1: 0, + /// SGX2. If 1, Indicates Intel SGX supports the collection of SGX2 leaf functions. + sgx2: 1, + // 2..=4 reserved + /// If 1, indicates Intel SGX supports ENCLV instruction leaves EINCVIRTCHILD, EDECVIRTCHILD, + /// and ESETCONTEXT. + enclv: 5, + /// If 1, indicates Intel SGX supports ENCLS instruction leaves ETRACKC, ERDINFO, ELDBC, and + /// ELDUC. + encls: 6, + // 7..=31 reserved +}); + +bitfield!(Leaf12Subleaf0Ebx, u32, { + /// MISCSELECT. Bit vector of supported extended SGX features. + miscselect: 0..32, +}); + +bitfield!(Leaf12Subleaf0Ecx, u32, { + // Reserved +}); + +bitfield!(Leaf12Subleaf0Edx, u32, { + /// MaxEnclaveSize_Not64. The maximum supported enclave size in non-64-bit mode is 2^(EDX[7:0]). + max_enclave_size_not_64: 0..8, + /// MaxEnclaveSize_64. The maximum supported enclave size in 64-bit mode is 2^(EDX[15:8]). + max_enclave_size_64: 8..16, + // 16..=31 reserved +}); +// Leaf 1 + +bitfield!(Leaf12Subleaf1Eax, u32, { + /// Reports the valid bits of SECS.ATTRIBUTES[31:0] that software can set with ECREATE. + ecreate_attrs_0_31: 0..32, +}); + +bitfield!(Leaf12Subleaf1Ebx, u32, { + /// Reports the valid bits of SECS.ATTRIBUTES[63:32] that software can set with ECREATE. + ecreate_attrs_32_63: 0..32, +}); + +bitfield!(Leaf12Subleaf1Ecx, u32, { + /// Reports the valid bits of SECS.ATTRIBUTES[95:64] that software can set with ECREATE. + ecreate_attrs_64_95: 0..32, +}); + +bitfield!(Leaf12Subleaf1Edx, u32, { + /// Reports the valid bits of SECS.ATTRIBUTES[127:96] that software can set with ECREATE. + ecreate_attrs_96_127: 0..32, +}); +// Leaf >1 + +bitfield!(Leaf12SubleafGt1Eax, u32, { + /// Sub-leaf Type + /// - 0000b: Indicates this sub-leaf is invalid. + /// - 0001b: This sub-leaf enumerates an EPC section. EBX:EAX and EDX:ECX provide information on the + /// Enclave Page Cache (EPC) section. + /// All other type encodings are reserved. + subleaf_type: 0..4, + /// Bits 31:12 of the physical address of the base of the EPC section. + /// + /// When EAX[03:00] = 0001b (otherwise 0) + epc_base_31_12: 12..32, +}); + +bitfield!(Leaf12SubleafGt1Ebx, u32, { + /// Bits 51:32 of the physical address of the base of the EPC section. + /// + /// When EAX[03:00] = 0001b (otherwise 0) + epc_base_51_32: 0..20, + // 20..=31 reserved +}); + +bitfield!(Leaf12SubleafGt1Ecx, u32, { + /// EPC section property encoding defined as follows: + /// - If ECX[3:0] = 0000b, then all bits of the EDX:ECX pair are enumerated as 0. + /// - If ECX[3:0] = 0001b, then this section has confidentiality and integrity protection. + /// - If ECX[3:0] = 0010b, then this section has confidentiality protection only. + /// All other encodings are reserved. + /// + /// When EAX[03:00] = 0001b (otherwise 0) + epc_section: 0..4, + // 4..=11 reserved + /// Bits 31:12 of the size of the corresponding EPC section within the Processor Reserved + /// Memory. + /// + /// When EAX[03:00] = 0001b (otherwise 0) + epc_reserved_31_12: 12..32, + +}); + +bitfield!(Leaf12SubleafGt1Edx, u32, { + /// Bits 51:32 of the size of the corresponding EPC section within the Processor Reserved + /// Memory. + /// + /// When EAX[03:00] = 0001b (otherwise 0) + epc_reserved_51_32: 0..20, + // 20..=31 reserved +}); +// ------------------------------------------------------------------------------------------------- +// Leaf 14 +// ------------------------------------------------------------------------------------------------- +// Leaf 0 + +bitfield!(Leaf14Subleaf0Eax, u32, { + /// Reports the maximum sub-leaf supported in leaf 14H. + /// + /// **At the moment of writing the Intel specification only notes the format of ECX=1, therefore + /// this field should only be 0 or 1** + max_subleaf: 0..32, +}); + +bitfield!(Leaf14Subleaf0Ebx, u32, { + /// If 1, indicates that IA32_RTIT_CTL.CR3Filter can be set to 1, and that IA32_RTIT_CR3_MATCH + /// MSR can be accessed. + ia32_rtit: 0, + /// If 1, indicates support of Configurable PSB and Cycle-Accurate Mode. + psb_config_cam: 1, + /// If 1, indicates support of IP Filtering, TraceStop filtering, and preservation of Intel PT + /// MSRs across warm reset. + ip_filtering_and_ts_filtering_and_pt_msr_preservation: 2, + /// If 1, indicates support of MTC timing packet and suppression of COFI-based packets. + mtc_timing_and_cofi_suppression: 3, + /// If 1, indicates support of PTWRITE. Writes can set IA32_RTIT_CTL[12] (PTWEn) and + /// IA32_RTIT_CTL[5] (FUPonPTW), and PTWRITE can generate packets. + ptwrite: 4, + /// If 1, indicates support of Power Event Trace. Writes can set IA32_RTIT_CTL[4] (PwrEvtEn), + /// enabling Power Event Trace packet generation. + power_event_trace: 5, + /// If 1, indicates support for PSB and PMI preservation. Writes can set IA32_RTIT_CTL[56] + /// (InjectPsbPmiOnEnable), enabling the processor to set IA32_RTIT_STATUS[7] (PendTopaPMI) + /// and/or IA32_RTIT_STATUS[6] (PendPSB) in order to preserve ToPA PMIs and/or PSBs otherwise + /// lost due to Intel PT disable. Writes can also set PendToPAPMI and PendPSB. + psb_and_pmi_preservation: 6, + /// If 1, writes can set IA32_RTIT_CTL[31] (EventEn), enabling Event Trace packet generation. + ia32_rtit_ctl_31: 7, + /// If 1, writes can set IA32_RTIT_CTL[55] (DisTNT), disabling TNT packet generation. + ia32_rtit_ctl_55: 8, + // 9..=31 reserved +}); + +bitfield!(Leaf14Subleaf0Ecx, u32, { + /// If 1, Tracing can be enabled with IA32_RTIT_CTL.ToPA = 1, hence utilizing the ToPA output + /// scheme; IA32_RTIT_OUTPUT_BASE and IA32_RTIT_OUTPUT_MASK_PTRS MSRs can be accessed. + ia32_rtit_ctl_topa: 0, + /// If 1, ToPA tables can hold any number of output entries, up to the maximum allowed by the + /// MaskOrTableOffset field of IA32_RTIT_OUTPUT_MASK_PTRS. + topa_ext: 1, + /// If 1, indicates support of Single-Range Output scheme. + sros: 2, + /// If 1, indicates support of output to Trace Transport subsystem. + otts: 3, + // 4..=30 reserved + /// If 1, generated packets which contain IP payloads have LIP values, which include the CS base component. + lip_cs_base: 31, +}); + +bitfield!(Leaf14Subleaf0Edx, u32, { + /// Reserved +}); +// Leaf 1 + +bitfield!(Leaf14Subleaf1Eax, u32, { + /// Number of configurable Address Ranges for filtering. + configurable_filterig_addr_ranges: 0..3, + // 3..=15 reserved + /// Bitmap of supported MTC period encodings. + mtc_period_encodings: 16..32, +}); + +bitfield!(Leaf14Subleaf1Ebx, u32, { + /// Bitmap of supported Cycle Threshold value encodings. + cycle_threshold_value_encodings: 0..16, + /// Bitmap of supported Configurable PSB frequency encodings. + configurable_psb_freq_encodings: 16..32, +}); + +bitfield!(Leaf14Subleaf1Ecx, u32, { + // Reserved +}); + +bitfield!(Leaf14Subleaf1Edx, u32, { + // Reserved +}); +// ------------------------------------------------------------------------------------------------- +// Leaf 15 +// ------------------------------------------------------------------------------------------------- + +bitfield!(Leaf15Eax, u32, { + /// An unsigned integer which is the denominator of the TSC/ā€core crystal clockā€ ratio. + /// + /// If EBX[31:0] is 0, the TSC/ā€core crystal clockā€ ratio is not enumerated. EBX[31:0]/EAX[31:0] + /// indicates the ratio of the TSC frequency and the core crystal clock frequency. If ECX is 0, + /// the nominal core crystal clock frequency is not enumerated. ā€œTSC frequencyā€ = ā€œcore crystal + /// clock frequencyā€ * EBX/EAX. The core crystal clock may differ from the reference clock, bus + /// clock, or core clock frequencies. + tsc_denominator: 0..32, + +}); + +bitfield!(Leaf15Ebx, u32, { + /// An unsigned integer which is the numerator of the TSC/ā€core crystal clockā€ ratio. + /// + /// If EBX[31:0] is 0, the TSC/ā€core crystal clockā€ ratio is not enumerated. EBX[31:0]/EAX[31:0] + /// indicates the ratio of the TSC frequency and the core crystal clock frequency. If ECX is 0, + /// the nominal core crystal clock frequency is not enumerated. ā€œTSC frequencyā€ = ā€œcore crystal + /// clock frequencyā€ * EBX/EAX. The core crystal clock may differ from the reference clock, bus + /// clock, or core clock frequencies. + tsc_numerator: 0..32, +}); + +bitfield!(Leaf15Ecx, u32, { + /// An unsigned integer which is the nominal frequency of the core crystal clock in Hz. + /// + /// If EBX[31:0] is 0, the TSC/ā€core crystal clockā€ ratio is not enumerated. EBX[31:0]/EAX[31:0] + /// indicates the ratio of the TSC frequency and the core crystal clock frequency. If ECX is 0, + /// the nominal core crystal clock frequency is not enumerated. ā€œTSC frequencyā€ = ā€œcore crystal + /// clock frequencyā€ * EBX/EAX. The core crystal clock may differ from the reference clock, bus + /// clock, or core clock frequencies. + nominal_frequency: 0..32 +}); + +bitfield!(Leaf15Edx, u32, { + /// Reserved +}); +// ------------------------------------------------------------------------------------------------- +// Leaf 16 +// ------------------------------------------------------------------------------------------------- + +bitfield!(Leaf16Eax, u32, { + /// Processor Base Frequency (in MHz). + base_frequency: 0..15, + // 15..=31 reserved +}); + +bitfield!(Leaf16Ebx, u32, { + /// Maximum Frequency (in MHz). + max_frequency: 0..15, + // 15..=31 reserved +}); + +bitfield!(Leaf16Ecx, u32, { + /// Bus (Reference) Frequency (in MHz). + ref_frequency: 0..15, + // 15..=31 reserved +}); + +bitfield!(Leaf16Edx, u32, { + /// Reserved +}); +// ------------------------------------------------------------------------------------------------- +// Leaf 17 +// ------------------------------------------------------------------------------------------------- +// leaf 0 + +bitfield!(Leaf17Subleaf0Eax, u32, { + /// MaxSOCID_Index. Reports the maximum input value of supported sub-leaf in leaf 17H. + max_socid_index: 0..32, +}); + +bitfield!(Leaf17Subleaf0Ebx, u32, { + /// SOC Vendor ID. + soc_vendor_id: 0..16, + /// IsVendorScheme. If 1, the SOC Vendor ID field is assigned via an industry standard + /// enumeration scheme. Otherwise, the SOC Vendor ID field is assigned by Intel. + is_vendor_scheme: 16, + // 17..=31 reserved +}); + +bitfield!(Leaf17Subleaf0Ecx, u32, { + /// Project ID. A unique number an SOC vendor assigns to its SOC projects. + project_id: 0..32, +}); + +bitfield!(Leaf17Subleaf0Edx, u32, { + /// Stepping ID. A unique number within an SOC project that an SOC vendor assigns. + stepping_id: 0..32, +}); +// leaf 1 + +bitfield!(Leaf17Subleaf1Eax, u32, { + /// SOC Vendor Brand String. UTF-8 encoded string. + /// + /// Leaf 17H output depends on the initial value in ECX. SOC Vendor Brand String is a UTF-8 + /// encoded string padded with trailing bytes of 00H. The complete SOC Vendor Brand String is + /// constructed by concatenating in ascending order of EAX:EBX:ECX:EDX and from the sub-leaf 1 + /// fragment towards sub-leaf 3. + soc_string: 0..32, +}); + +bitfield!(Leaf17Subleaf1Ebx, u32, { + /// SOC Vendor Brand String. UTF-8 encoded string. + /// + /// Leaf 17H output depends on the initial value in ECX. SOC Vendor Brand String is a UTF-8 + /// encoded string padded with trailing bytes of 00H. The complete SOC Vendor Brand String is + /// constructed by concatenating in ascending order of EAX:EBX:ECX:EDX and from the sub-leaf 1 + /// fragment towards sub-leaf 3. + soc_string: 0..32, +}); + +bitfield!(Leaf17Subleaf1Ecx, u32, { + /// SOC Vendor Brand String. UTF-8 encoded string. + /// + /// Leaf 17H output depends on the initial value in ECX. SOC Vendor Brand String is a UTF-8 + /// encoded string padded with trailing bytes of 00H. The complete SOC Vendor Brand String is + /// constructed by concatenating in ascending order of EAX:EBX:ECX:EDX and from the sub-leaf 1 + /// fragment towards sub-leaf 3. + soc_string: 0..32, +}); + +bitfield!(Leaf17Subleaf1Edx, u32, { + /// SOC Vendor Brand String. UTF-8 encoded string. + /// + /// Leaf 17H output depends on the initial value in ECX. SOC Vendor Brand String is a UTF-8 + /// encoded string padded with trailing bytes of 00H. The complete SOC Vendor Brand String is + /// constructed by concatenating in ascending order of EAX:EBX:ECX:EDX and from the sub-leaf 1 + /// fragment towards sub-leaf 3. + soc_string: 0..32, +}); +// leaf >3 + +bitfield!(Leaf17SubleafGt3Eax, u32, { + // Reserved +}); + +bitfield!(Leaf17SubleafGt3Ebx, u32, { + // Reserved +}); + +bitfield!(Leaf17SubleafGt3Ecx, u32, { + // Reserved +}); + +bitfield!(Leaf17SubleafGt3Edx, u32, { + // Reserved +}); +// ------------------------------------------------------------------------------------------------- +// Leaf 18 +// ------------------------------------------------------------------------------------------------- +// leaf 0 + +bitfield!(Leaf18Subleaf0Eax, u32, { + /// Reports the maximum input value of supported sub-leaf in leaf 18H. + max_subleaf: 0..32, +}); + +bitfield!(Leaf18Subleaf0Ebx, u32, { + /// 4K page size entries supported by this structure. + page_4k: 0, + /// 2MB page size entries supported by this structure. + page_2m: 1, + /// 4MB page size entries supported by this structure. + page_4m: 2, + /// 1 GB page size entries supported by this structure. + page_3g: 3, + // 4..=7 reserved + /// Partitioning (0: Soft partitioning between the logical processors sharing this structure). + partitioning: 8..16, + // 11..=15 reserved + /// W = Ways of associativity. + ways_of_associativity: 16..32, +}); + +bitfield!(Leaf18Subleaf0Ecx, u32, { + /// S = Number of Sets. + number_of_sets: 0..32, +}); + +bitfield!(Leaf18Subleaf0Edx, u32, { + /// Translation cache type field. + /// - 00000b: Null (indicates this sub-leaf is not valid). + /// - 00001b: Data TLB. + /// - 00010b: Instruction TLB. + /// - 00011b: Unified TLB*. + /// - 00100b: Load Only TLB. Hit on loads; fills on both loads and stores. + /// - 00101b: Store Only TLB. Hit on stores; fill on stores. + /// All other encodings are reserved. + /// + /// * Some unified TLBs will allow a single TLB entry to satisfy data read/write and instruction + /// fetches. Others will require separate entries (e.g., one loaded on data read/write and + /// another loaded on an instruction fetch) . Please see the IntelĀ® 64 and IA-32 Architectures + /// Optimization Reference Manual for details of a particular product. + translation_cache_type: 0..5, + /// Translation cache level (starts at 1). + translation_cache_level: 5..8, + /// Fully associative structure. + fully_associative_structure: 8, + // 9..=13 reserved + /// Maximum number of addressable IDs for logical processors sharing this translation cache** + /// + /// **Add one to the return value to get the result. + max_addressable_ids: 14..26, + // 26..=31 reserved +}); +// leaf >0 + +bitfield!(Leaf18SubleafGt0Eax, u32, { + // reserved +}); + +bitfield!(Leaf18SubleafGt0Ebx, u32, { + /// 4K page size entries supported by this structure. + page_4k: 0, + /// 2MB page size entries supported by this structure. + page_2m: 1, + /// 4MB page size entries supported by this structure. + page_4m: 2, + /// 1 GB page size entries supported by this structure. + page_3g: 3, + // 4..=7 reserved + /// Partitioning (0: Soft partitioning between the logical processors sharing this structure). + partitioning: 8..16, + // 11..=15 reserved + /// W = Ways of associativity. + ways_of_associativity: 16..32, +}); + +bitfield!(Leaf18SubleafGt0Ecx, u32, { + /// S = Number of Sets. + number_of_sets: 0..32, +}); + +bitfield!(Leaf18SubleafGt0Edx, u32, { + /// Translation cache type field. + /// - 00000b: Null (indicates this sub-leaf is not valid). + /// - 00001b: Data TLB. + /// - 00010b: Instruction TLB. + /// - 00011b: Unified TLB*. + /// - 00100b: Load Only TLB. Hit on loads; fills on both loads and stores. + /// - 00101b: Store Only TLB. Hit on stores; fill on stores. + /// All other encodings are reserved. + /// + /// * Some unified TLBs will allow a single TLB entry to satisfy data read/write and instruction + /// fetches. Others will require separate entries (e.g., one loaded on data read/write and + /// another loaded on an instruction fetch) . Please see the IntelĀ® 64 and IA-32 Architectures + /// Optimization Reference Manual for details of a particular product. + translation_cache_type: 0..5, + /// Translation cache level (starts at 1). + translation_cache_level: 5..8, + /// Fully associative structure. + fully_associative_structure: 8, + // 9..=13 reserved + /// Maximum number of addressable IDs for logical processors sharing this translation cache** + /// + /// **Add one to the return value to get the result. + max_addressable_ids: 14..26, + // 26..=31 reserved +}); +// ------------------------------------------------------------------------------------------------- +// Leaf 19 +// ------------------------------------------------------------------------------------------------- + +bitfield!(Leaf19Eax, u32, { + /// Key Locker restriction of CPL0-only supported. + cpl0_only: 0, + /// Key Locker restriction of no-encrypt supported. + no_encrypt: 1, + /// Key Locker restriction of no-decrypt supported. + no_decrypt: 2, + // 3..=31 reserved + +}); + +bitfield!(Leaf19Ebx, u32, { + /// AESKLE. If 1, the AES Key Locker instructions are fully enabled. + aeskle: 0, + // Reserved. + /// If 1, the AES wide Key Locker instructions are supported. + aes_wide_key_locker: 2, + // Reserved + /// If 1, the platform supports the Key Locker MSRs (IA32_COPY_LOCAL_TO_PLATFORM, + /// IA23_COPY_PLATFORM_TO_LOCAL, IA32_COPY_STATUS, and IA32_IWKEYBACKUP_STATUS) and backing up + /// the internal wrapping key. + key_locker_msrs: 4, + // 5..=31 reserved +}); + +bitfield!(Leaf19Ecx, u32, { + /// If 1, the NoBackup parameter to LOADIWKEY is supported. + loadikey_no_backup: 0, + key_source_encoding_one: 1, + // 2..=31 reserved +}); + +bitfield!(Leaf19Edx, u32, { + // reserved. +}); +// ------------------------------------------------------------------------------------------------- +// Leaf 1A +// ------------------------------------------------------------------------------------------------- + +bitfield!(Leaf1AEax, u32, { + /// Native model ID of the core. The core-type and native model ID can be used to uniquely + /// identify the microarchitecture of the core. This native model ID is not unique across core + /// types, and not related to the model ID reported in CPUID leaf 01H, and does not identify the + /// SOC. + native_id: 0..24, + /// Core type + /// - 10H: Reserved + /// - 20H: Intel AtomĀ® + /// - 30H: Reserved + /// - 40H: IntelĀ® Coreā„¢ + core_type: 24..32, +}); + +bitfield!(Leaf1AEbx, u32, { + // Reserved. +}); + +bitfield!(Leaf1AEcx, u32, { + // Reserved. +}); + +bitfield!(Leaf1AEdx, u32, { + // Reserved. +}); +// ------------------------------------------------------------------------------------------------- +// Leaf 1C +// ------------------------------------------------------------------------------------------------- + +bitfield!(Leaf1CEax, u32, { + /// Supported LBR Depth Values. For each bit n set in this field, the IA32_LBR_DEPTH.DEPTH value + /// 8*(n+1) is supported. + lbr_depth_values: 0..8, + // 9..=28 reserved + /// Deep C-state Reset. If set, indicates that LBRs may be cleared on an MWAIT that requests a + /// C-state numerically greater than C1. + deep_c_state: 30, + /// IP Values Contain LIP. If set, LBR IP values contain LIP. If clear, IP values contain Effective IP. + ip_values_contain_lip: 31, +}); + +bitfield!(Leaf1CEbx, u32, { + /// CPL Filtering Supported. If set, the processor supports setting IA32_LBR_CTL[2:1] to + /// non-zero value. + cpl_filtering: 0, + /// Branch Filtering Supported. If set, the processor supports setting IA32_LBR_CTL[22:16] to + /// nonzero value. + branch_filtering: 1, + /// Call-stack Mode Supported. If set, the processor supports setting IA32_LBR_CTL[3] to 1. + call_stack_mode: 2, + // 3..=31 reserved +}); + +bitfield!(Leaf1CEcx, u32, { + /// Mispredict Bit Supported. IA32_LBR_x_INFO[63] holds indication of branch misprediction + /// (MISPRED). + mispredict_bit: 0, + /// Timed LBRs Supported. IA32_LBR_x_INFO[15:0] holds CPU cycles since last LBR entry (CYC_CNT), + /// and IA32_LBR_x_INFO[60] holds an indication of whether the value held there is valid + /// (CYC_CNT_VALID). + timed_lbrs: 1, + /// Branch Type Field Supported. IA32_LBR_INFO_x[59:56] holds indication of the recorded + /// operation's branch type (BR_TYPE). + branch_type_field: 2, + // 3..=31 reserved +}); + +bitfield!(Leaf1CEdx, u32, { + // 3..=31 reserved +}); +// ------------------------------------------------------------------------------------------------- +// Leaf 1F +// ------------------------------------------------------------------------------------------------- + +bitfield!(Leaf1FEax, u32, { + /// Number of bits to shift right on x2APIC ID to get a unique topology ID of the next level + /// type*. All logical processors with the same next level ID share current level. + right_shift_2xapic_id_unique_top_id: 0..5, + // 5..=31 reserved +}); + +bitfield!(Leaf1FEbx, u32, { + /// Number of logical processors at this level type. The number reflects configuration as + /// shipped by Intel**. + /// + /// ** Software must not use EBX[15:0] to enumerate processor topology of the system. This value + /// in this field (EBX[15:0]) is only intended for display/diagnostic purposes. The actual + /// number of logical processors available to BIOS/OS/Applications may be different from the + /// value of EBX[15:0], depending on software and platform hardware configurations. + logical_processors: 0..16, + // 16..=31 reserved +}); + +bitfield!(Leaf1FEcx, u32, { + /// Level number. Same value in ECX input. + level_number: 0..8, + /// Level type***. + /// + /// *** The value of the ā€œlevel typeā€ field is not related to level numbers in any way, higher + /// ā€œlevel typeā€ values do not mean higher levels. Level type field has the following encoding: + /// + /// - 0: Invalid. + /// - 1: SMT. + /// - 2: Core. + /// - 3: Module. + /// - 4: Tile. + /// - 5: Die. + /// - 6-255: Reserved. + level_type: 8..16, + // 16..=31 reserved +}); + +bitfield!(Leaf1FEdx, u32, { + /// x2APIC ID the current logical processor. + x2apic_id: 0..32, +}); +// ------------------------------------------------------------------------------------------------- +// Leaf 20 +// ------------------------------------------------------------------------------------------------- + +bitfield!(Leaf20Eax, u32, { + /// Reports the maximum number of sub-leaves that are supported in leaf 20H. + max_subleaves: 0..32, +}); + +bitfield!(Leaf20Ebx, u32, { + // Indicates which bits may be set in the IA32_HRESET_ENABLE MSR to enable reset of different + // components of hardware-maintained history. + + /// Indicates support for both HRESETā€™s EAX[0] parameter, and IA32_HRESET_ENABLE[0] set by the + /// OS to enable reset of IntelĀ® Thread Director history. + hreset: 0, + // 1..=31 reserved +}); + +bitfield!(Leaf20Ecx, u32, { + // Reserved. +}); + +bitfield!(Leaf20Edx, u32, { + // Reserved. +}); +// ------------------------------------------------------------------------------------------------- +// Leaf 80000000 +// ------------------------------------------------------------------------------------------------- + +bitfield!(Leaf80000000Eax, u32, { + /// Maximum Input Value for Extended Function CPUID Information. + max_extend_function_input: 0..32, +}); + +bitfield!(Leaf80000000Ebx, u32, { + // Reserved. +}); + +bitfield!(Leaf80000000Ecx, u32, { + // Reserved. +}); + +bitfield!(Leaf80000000Edx, u32, { + // Reserved. +}); +// ------------------------------------------------------------------------------------------------- +// Leaf 80000001 +// ------------------------------------------------------------------------------------------------- + +bitfield!(Leaf80000001Eax, u32, { + /// Extended Processor Signature and Feature Bits. + extended_processor_signature_and_feature_bits: 0..32, +}); + +bitfield!(Leaf80000001Ebx, u32, { + // Reserved. +}); + +bitfield!(Leaf80000001Ecx, u32, { + /// LAHF/SAHF available in 64-bit mode.* + /// + /// * LAHF and SAHF are always available in other modes, regardless of the enumeration of this + /// feature flag. + lahf_sahf: 0, + // 1..=4 reserved + /// LZCNT. + lzcnt: 5, + // 6..=7 reserved + /// PREFETCHW. + prefetchcw: 8, + // 9..=31 reserved +}); + +bitfield!(Leaf80000001Edx, u32, { + // 0..=10 reserved + /// SYSCALL/SYSRET.** + /// + /// ** Intel processors support SYSCALL and SYSRET only in 64-bit mode. This feature flag is + /// always enumerated as 0 outside 64-bit mode. + syscall_sysret: 11, + // 12..=19 reserved + /// Execute Disable Bit available. + execute_disable_bit: 20, + // 21..=25 reserved + /// 1-GByte pages are available if 1. + pages_1g: 26, + /// RDTSCP and IA32_TSC_AUX are available if 1. + rdtscp_and_ia32_tsc_aux: 27, + // Reserved. + /// IntelĀ® 64 Architecture available if 1. + arch64: 29, + // 30..=31 reserved +}); +// ------------------------------------------------------------------------------------------------- +// Leaf 80000005 +// ------------------------------------------------------------------------------------------------- + +bitfield!(Leaf80000005Eax, u32, { + // Reserved. +}); + +bitfield!(Leaf80000005Ebx, u32, { + // Reserved. +}); + +bitfield!(Leaf80000005Ecx, u32, { + // Reserved. +}); + +bitfield!(Leaf80000005Edx, u32, { + // Reserved. +}); +// ------------------------------------------------------------------------------------------------- +// Leaf 80000006 +// ------------------------------------------------------------------------------------------------- + +bitfield!(Leaf80000006Eax, u32, { + // Reserved. +}); + +bitfield!(Leaf80000006Ebx, u32, { + // Reserved. +}); + +bitfield!(Leaf80000006Ecx, u32, { + /// Cache Line size in bytes. + cache_line_size: 0..8, + // 8..=11 reserved + /// L2 Associativity field *. + /// + /// * L2 associativity field encodings: + /// - 00H - Disabled 08H - 16 ways + /// - 01H - 1 way (direct mapped) 09H - Reserved + /// - 02H - 2 ways 0AH - 32 ways + /// - 03H - Reserved 0BH - 48 ways + /// - 04H - 4 ways 0CH - 64 ways + /// - 05H - Reserved 0DH - 96 ways + /// - 06H - 8 ways 0EH - 128 ways + /// - 07H - See CPUID leaf 04H, sub-leaf 2** 0FH - Fully associative + /// + /// ** CPUID leaf 04H provides details of deterministic cache parameters, including the L2 cache + /// in sub-leaf 2 + l2_associativity: 12..16, + /// Cache size in 1K units. + cache_size_1k_units: 16..32, +}); + +bitfield!(Leaf80000006Edx, u32, { + // Reserved. +}); +// ------------------------------------------------------------------------------------------------- +// Leaf 80000007 +// ------------------------------------------------------------------------------------------------- + +bitfield!(Leaf80000007Eax, u32, { + // Reserved. +}); + +bitfield!(Leaf80000007Ebx, u32, { + // Reserved. +}); + +bitfield!(Leaf80000007Ecx, u32, { + // Reserved. +}); + +bitfield!(Leaf80000007Edx, u32, { + // 0..=7 reserved + invariant_tsc: 8, + // 9..=31 reserved +}); +// ------------------------------------------------------------------------------------------------- +// Leaf 80000008 +// ------------------------------------------------------------------------------------------------- + +bitfield!(Leaf80000008Eax, u32, { + /// #Physical Address Bits*. + /// + /// * If CPUID.80000008H:EAX[7:0] is supported, the maximum physical address number supported + /// should come from this field. + physical_address_bits: 0..8, + /// #Linear Address Bits. + linear_address_bits: 8..16, + // 8..=31 reserved +}); + +bitfield!(Leaf80000008Ebx, u32, { + // 0..=8 reserved + wbnoinvd: 9, + // 10..=31 reserved + +}); + +bitfield!(Leaf80000008Ecx, u32, { + // Reserved. +}); + +bitfield!(Leaf80000008Edx, u32, { + // Reserved. +}); diff --git a/src/cpuid/src/leaves.rs b/src/cpuid/src/leaves.rs new file mode 100644 index 00000000000..74394cdf623 --- /dev/null +++ b/src/cpuid/src/leaves.rs @@ -0,0 +1,86 @@ +// Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +use super::cpuid_ffi::RawKvmCpuidEntry; +#[allow(clippy::wildcard_imports)] +use super::registers::*; + +/// A generic leaf formed of 4 members `eax`, `ebx`, `ecx` and `edx`. +#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +#[repr(C)] +pub struct Leaf { + /// EAX register. + pub eax: A, + /// EBX register. + pub ebx: B, + /// ECX register. + pub ecx: C, + /// EDX register. + pub edx: D, +} + +impl From<(A, B, C, D)> for Leaf { + #[inline] + fn from((a, b, c, d): (A, B, C, D)) -> Self { + Leaf { + eax: a, + ebx: b, + ecx: c, + edx: d, + } + } +} + +#[cfg(cpuid)] +impl, B: From, C: From, D: From> From + for Leaf +{ + #[inline] + fn from( + std::arch::x86_64::CpuidResult { eax, ebx, ecx, edx }: std::arch::x86_64::CpuidResult, + ) -> Self { + Leaf { + eax: A::from(eax), + ebx: B::from(ebx), + ecx: C::from(ecx), + edx: D::from(edx), + } + } +} + +impl, B: From, C: From, D: From> From<&RawKvmCpuidEntry> + for Leaf +{ + #[inline] + fn from( + &RawKvmCpuidEntry { + eax, ebx, ecx, edx, .. + }: &RawKvmCpuidEntry, + ) -> Self { + Leaf { + eax: A::from(eax), + ebx: B::from(ebx), + ecx: C::from(ecx), + edx: D::from(edx), + } + } +} + +// ------------------------------------------------------------------------------------------------- +// Shared leaf types +// ------------------------------------------------------------------------------------------------- + +/// Leaf 00H +pub type Leaf0 = Leaf; + +/// Leaf 01H +pub type Leaf1 = Leaf; + +/// Leaf 80000002H +pub type Leaf80000002 = Leaf; + +/// Leaf 80000003H +pub type Leaf80000003 = Leaf80000002; + +/// Leaf 80000004H +pub type Leaf80000004 = Leaf80000002; diff --git a/src/cpuid/src/lib.rs b/src/cpuid/src/lib.rs index 8a2a2ed2021..349e8e6b0eb 100644 --- a/src/cpuid/src/lib.rs +++ b/src/cpuid/src/lib.rs @@ -5,68 +5,624 @@ // Use of this source code is governed by a BSD-style license that can be // found in the THIRD-PARTY file. -#![deny(missing_docs)] -#![warn(clippy::ptr_as_ptr)] -#![warn(clippy::undocumented_unsafe_blocks)] -#![warn(clippy::cast_lossless)] +#![warn(clippy::pedantic, clippy::restriction)] +#![allow( + clippy::blanket_clippy_restriction_lints, + clippy::implicit_return, + clippy::pattern_type_mismatch, + clippy::std_instead_of_alloc, + clippy::std_instead_of_core, + clippy::pub_use, + clippy::non_ascii_literal, + clippy::single_char_lifetime_names, + clippy::exhaustive_enums, + clippy::exhaustive_structs, + clippy::unseparated_literal_suffix, + clippy::mod_module_files +)] +// Apply CPUID specific lint adjustments. +#![allow( + clippy::unsafe_derive_deserialize, + clippy::unreadable_literal, + clippy::similar_names, + clippy::same_name_method, + clippy::doc_markdown +)] + //! Utility for configuring the CPUID (CPU identification) for the guest microVM. -#![cfg(target_arch = "x86_64")] -use kvm_bindings::CpuId; +use std::convert::TryFrom; +#[cfg(cpuid)] +use std::mem::{size_of, transmute}; /// cpuid utility functions. pub mod common; -use crate::common::*; -/// Contains helper methods for bit operations. -pub mod bit_helper; +/// Raw CPUID specification handling. +mod cpuid_ffi; +pub use cpuid_ffi::*; -mod template; -pub use crate::template::intel::{c3, t2, t2s}; -pub use crate::template::msrs_to_save_by_cpuid; +/// AMD CPUID specification handling. +pub mod amd; +pub use amd::AmdCpuid; -mod cpu_leaf; +/// Intel CPUID specification handling. +pub mod intel; +pub use intel::IntelCpuid; -mod transformer; -use crate::transformer::*; -pub use crate::transformer::{Error, VmSpec}; +/// Indexing implementations (shared between AMD and Intel). +mod indexing; +pub(crate) use indexing::{index_leaf, transmute_vec}; +pub use indexing::{IndexLeaf, IndexLeafMut}; -mod brand_string; +/// Leaf structs (shared between AMD and Intel). +mod leaves; +pub use leaves::Leaf; -/// Sets up the CPUID entries for the given vcpu. -/// -/// # Arguments -/// -/// * `kvm_cpuid` - KVM related structure holding the relevant CPUID info. -/// * `vm_spec` - The specifications of the VM. -/// -/// # Example -/// ``` -/// use cpuid::{filter_cpuid, VmSpec}; -/// use kvm_bindings::{CpuId, KVM_MAX_CPUID_ENTRIES}; -/// use kvm_ioctls::Kvm; -/// -/// let kvm = Kvm::new().unwrap(); -/// let mut kvm_cpuid: CpuId = kvm.get_supported_cpuid(KVM_MAX_CPUID_ENTRIES).unwrap(); -/// -/// let vm_spec = VmSpec::new(0, 1, true).unwrap(); -/// -/// filter_cpuid(&mut kvm_cpuid, &vm_spec).unwrap(); +/// CPUID normalize implementation. +#[cfg(cpuid)] +mod normalize; +#[cfg(cpuid)] +pub use normalize::{FeatureInformationError, GetMaxCpusPerPackageError, NormalizeCpuidError}; + +/// Register bit fields (shared between AMD and Intel). +mod registers; + +/// Intel brand string. +pub const VENDOR_ID_INTEL: &[u8; 12] = b"GenuineIntel"; + +/// AMD brand string. +pub const VENDOR_ID_AMD: &[u8; 12] = b"AuthenticAMD"; + +/// Intel brand string. +#[allow(clippy::undocumented_unsafe_blocks)] +pub const VENDOR_ID_INTEL_STR: &str = unsafe { std::str::from_utf8_unchecked(VENDOR_ID_INTEL) }; + +/// AMD brand string. +#[allow(clippy::undocumented_unsafe_blocks)] +pub const VENDOR_ID_AMD_STR: &str = unsafe { std::str::from_utf8_unchecked(VENDOR_ID_AMD) }; + +/// To store the brand string we have 3 leaves, each with 4 registers, each with 4 bytes. +pub const BRAND_STRING_LENGTH: usize = 3 * 4 * 4; + +/// Gets the Intel default brand. +// As we pass through host frequency, we require CPUID and thus `cfg(cpuid)`. +/// Gets host brand string. /// -/// // Get expected `kvm_cpuid` entries. -/// let entries = kvm_cpuid.as_mut_slice(); +/// Its stored in-order with bytes flipped in each register e.g.: +/// ```text +/// "etnI" | ")4(l" | "oeX " | ")R(n" | +/// "orP " | "ssec" | "@ ro" | "0.3 " | +/// "zHG0" | null | null | null +/// ------------------------------------ +/// Intel(4) Xeon(R) Processor @ 3.00Ghz /// ``` -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -pub fn filter_cpuid(kvm_cpuid: &mut CpuId, vm_spec: &VmSpec) -> Result<(), Error> { - let maybe_cpuid_transformer: Option<&dyn CpuidTransformer> = match vm_spec.cpu_vendor_id() { - VENDOR_ID_INTEL => Some(&intel::IntelCpuidTransformer {}), - VENDOR_ID_AMD => Some(&amd::AmdCpuidTransformer {}), - _ => None, - }; +#[cfg(cpuid)] +#[inline] +#[must_use] +pub fn host_brand_string() -> [u8; BRAND_STRING_LENGTH] { + // SAFETY: Safe we check `CPUID` availability with `cfg(cpuid)`. + let leaf_a = unsafe { core::arch::x86_64::__cpuid(0x80000002) }; + + // SAFETY: Safe we check `CPUID` availability with `cfg(cpuid)`. + let leaf_b = unsafe { core::arch::x86_64::__cpuid(0x80000003) }; + + // SAFETY: Safe we check `CPUID` availability with `cfg(cpuid)`. + let leaf_c = unsafe { core::arch::x86_64::__cpuid(0x80000004) }; + + let arr = [ + leaf_a.eax, leaf_a.ebx, leaf_a.ecx, leaf_a.edx, leaf_b.eax, leaf_b.ebx, leaf_b.ecx, + leaf_b.edx, leaf_c.eax, leaf_c.ebx, leaf_c.ecx, leaf_c.edx, + ]; + + // SAFETY: Transmuting `[u32;12]` to `[u8;BRAND_STRING_LENGTH]` (`[u8;48]`) is always safe. + unsafe { std::mem::transmute(arr) } +} + +/// Trait defining shared behaviour between CPUID structures. +pub trait CpuidTrait { + /// Returns the CPUID manufacturers ID (e.g. `GenuineIntel` or `AuthenticAMD`) or `None` if it + /// cannot be found in CPUID (e.g. leaf 0x0 is missing). + #[inline] + #[must_use] + fn manufacturer_id(&self) -> Option<[u8; 12]> { + let leaf_0 = self.get(&CpuidKey::leaf(0x0))?; + + // The ordering of the manufacturer string is ebx,edx,ecx this is not a mistake. + let (ebx, edx, ecx) = ( + leaf_0.result.ebx.to_ne_bytes(), + leaf_0.result.edx.to_ne_bytes(), + leaf_0.result.ecx.to_ne_bytes(), + ); + let arr: [u8; 12] = [ + ebx[0], ebx[1], ebx[2], ebx[3], edx[0], edx[1], edx[2], edx[3], ecx[0], ecx[1], ecx[2], + ecx[3], + ]; + Some(arr) + } + + /// Get immutable reference to leaf. + #[inline] + #[must_use] + fn leaf(&self) -> >::Output<'_> + where + Self: IndexLeaf, + { + >::index_leaf(self) + } + + /// Get mutable reference to leaf. + #[inline] + #[must_use] + fn leaf_mut(&mut self) -> >::Output<'_> + where + Self: IndexLeafMut, + { + >::index_leaf_mut(self) + } + + /// Gets a given sub-leaf. + fn get(&self, key: &CpuidKey) -> Option<&CpuidEntry>; + + /// Gets a given sub-leaf. + fn get_mut(&mut self, key: &CpuidKey) -> Option<&mut CpuidEntry>; + + /// Applies a given brand string to CPUID. + /// + /// # Errors + /// + /// When any of the leaves 0x80000002, 0x80000003 or 0x80000004 are not present. + #[inline] + fn apply_brand_string( + &mut self, + brand_string: &[u8; BRAND_STRING_LENGTH], + ) -> Result<(), MissingBrandStringLeaves> { + // 0x80000002 + { + let leaf: &mut CpuidEntry = self + .get_mut(&CpuidKey::leaf(0x80000002)) + .ok_or(MissingBrandStringLeaves)?; + leaf.result.eax = u32::from_ne_bytes([ + brand_string[0], + brand_string[1], + brand_string[2], + brand_string[3], + ]); + leaf.result.ebx = u32::from_ne_bytes([ + brand_string[4], + brand_string[5], + brand_string[6], + brand_string[7], + ]); + leaf.result.ecx = u32::from_ne_bytes([ + brand_string[8], + brand_string[9], + brand_string[10], + brand_string[11], + ]); + leaf.result.edx = u32::from_ne_bytes([ + brand_string[12], + brand_string[13], + brand_string[14], + brand_string[15], + ]); + } + + // 0x80000003 + { + let leaf: &mut CpuidEntry = self + .get_mut(&CpuidKey::leaf(0x80000003)) + .ok_or(MissingBrandStringLeaves)?; + leaf.result.eax = u32::from_ne_bytes([ + brand_string[16], + brand_string[17], + brand_string[18], + brand_string[19], + ]); + leaf.result.ebx = u32::from_ne_bytes([ + brand_string[20], + brand_string[21], + brand_string[22], + brand_string[23], + ]); + leaf.result.ecx = u32::from_ne_bytes([ + brand_string[24], + brand_string[25], + brand_string[26], + brand_string[27], + ]); + leaf.result.edx = u32::from_ne_bytes([ + brand_string[28], + brand_string[29], + brand_string[30], + brand_string[31], + ]); + } + + // 0x80000004 + { + let leaf: &mut CpuidEntry = self + .get_mut(&CpuidKey::leaf(0x80000004)) + .ok_or(MissingBrandStringLeaves)?; + leaf.result.eax = u32::from_ne_bytes([ + brand_string[32], + brand_string[33], + brand_string[34], + brand_string[35], + ]); + leaf.result.ebx = u32::from_ne_bytes([ + brand_string[36], + brand_string[37], + brand_string[38], + brand_string[39], + ]); + leaf.result.ecx = u32::from_ne_bytes([ + brand_string[40], + brand_string[41], + brand_string[42], + brand_string[43], + ]); + leaf.result.edx = u32::from_ne_bytes([ + brand_string[44], + brand_string[45], + brand_string[46], + brand_string[47], + ]); + } + + Ok(()) + } +} + +#[cfg(cpuid)] +impl CpuidTrait for kvm_bindings::CpuId { + /// Gets a given sub-leaf. + #[allow(clippy::transmute_ptr_to_ptr)] + #[inline] + fn get(&self, CpuidKey { leaf, subleaf }: &CpuidKey) -> Option<&CpuidEntry> { + let entry_opt = self + .as_slice() + .iter() + .find(|entry| entry.function == *leaf && entry.index == *subleaf); + + entry_opt.map(|entry| { + // SAFETY: The `kvm_cpuid_entry2` and `CpuidEntry` are `repr(C)` with known sizes. + unsafe { + let arr: &[u8; size_of::()] = transmute(entry); + let arr2: &[u8; size_of::()] = + arr.get_unchecked(8..28).try_into().unwrap_unchecked(); + transmute::<_, &CpuidEntry>(arr2) + } + }) + } + + /// Gets a given sub-leaf. + #[allow(clippy::transmute_ptr_to_ptr)] + #[inline] + fn get_mut(&mut self, CpuidKey { leaf, subleaf }: &CpuidKey) -> Option<&mut CpuidEntry> { + let entry_opt = self + .as_mut_slice() + .iter_mut() + .find(|entry| entry.function == *leaf && entry.index == *subleaf); + entry_opt.map(|entry| { + // SAFETY: The `kvm_cpuid_entry2` and `CpuidEntry` are `repr(C)` with known sizes. + unsafe { + let arr: &mut [u8; size_of::()] = transmute(entry); + let arr2: &mut [u8; size_of::()] = + arr.get_unchecked_mut(8..28).try_into().unwrap_unchecked(); + transmute::<_, &mut CpuidEntry>(arr2) + } + }) + } +} + +/// Error type for [`apply_brand_string`]. +#[derive(Debug, thiserror::Error, Eq, PartialEq)] +#[error("Missing brand string leaves 0x80000002, 0x80000003 and 0x80000004.")] +pub struct MissingBrandStringLeaves; + +/// Error type for [`Cpuid::kvm_get_supported_cpuid`]. +#[cfg(cpuid)] +#[derive(Debug, thiserror::Error, Eq, PartialEq)] +pub enum KvmGetSupportedCpuidError { + /// Could not access KVM. + #[error("Could not access KVM: {0}")] + KvmAccess(#[from] utils::errno::Error), + /// Failed to create CPUID structure. + #[error("Failed to create CPUID structure: {0}")] + CpuidFromRaw(CpuidTryFromRawCpuid), +} + +/// Error type for [`>::try_from`]. +#[derive(Debug, thiserror::Error, Eq, PartialEq)] +pub enum CpuidTryFromRawCpuid { + /// Leaf 0 not found in the given `RawCpuid`.. + #[error("Leaf 0 not found in the given `RawCpuid`.")] + MissingLeaf0, + /// Unsupported CPUID manufacturer id. + #[error( + "Unsupported CPUID manufacturer id: \"{0:?}\" (only 'GenuineIntel' and 'AuthenticAMD' are \ + supported)." + )] + UnsupportedManufacturer([u8; 12]), +} + +/// CPUID information +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Cpuid { + /// Intel CPUID specific information. + Intel(IntelCpuid), + /// AMD CPUID specific information. + Amd(AmdCpuid), +} - if let Some(cpuid_transformer) = maybe_cpuid_transformer { - cpuid_transformer.process_cpuid(kvm_cpuid, vm_spec)?; +impl Cpuid { + /// Returns `Some(&mut IntelCpuid)` if `Self == Self::Intel(_)` else returns `None`. + #[inline] + #[must_use] + pub fn intel_mut(&mut self) -> Option<&mut IntelCpuid> { + match self { + Self::Intel(intel) => Some(intel), + Self::Amd(_) => None, + } } - Ok(()) + /// Returns `Some(&IntelCpuid)` if `Self == Self::Intel(_)` else returns `None`. + #[inline] + #[must_use] + pub fn intel(&self) -> Option<&IntelCpuid> { + match self { + Self::Intel(intel) => Some(intel), + Self::Amd(_) => None, + } + } + + /// Returns `Some(&AmdCpuid)` if `Self == Self::Amd(_)` else returns `None`. + #[inline] + #[must_use] + pub fn amd(&self) -> Option<&AmdCpuid> { + match self { + Self::Intel(_) => None, + Self::Amd(amd) => Some(amd), + } + } + + /// Returns `Some(&mut AmdCpuid)` if `Self == Self::Amd(_)` else returns `None`. + #[inline] + #[must_use] + pub fn amd_mut(&mut self) -> Option<&mut AmdCpuid> { + match self { + Self::Intel(_) => None, + Self::Amd(amd) => Some(amd), + } + } +} + +impl CpuidTrait for Cpuid { + /// Gets a given sub-leaf. + #[inline] + fn get(&self, key: &CpuidKey) -> Option<&CpuidEntry> { + match self { + Self::Intel(intel_cpuid) => intel_cpuid.get(key), + Self::Amd(amd_cpuid) => amd_cpuid.get(key), + } + } + + /// Gets a given sub-leaf. + #[inline] + fn get_mut(&mut self, key: &CpuidKey) -> Option<&mut CpuidEntry> { + match self { + Self::Intel(intel_cpuid) => intel_cpuid.get_mut(key), + Self::Amd(amd_cpuid) => amd_cpuid.get_mut(key), + } + } +} + +impl TryFrom for Cpuid { + type Error = CpuidTryFromRawCpuid; + + #[inline] + fn try_from(raw_cpuid: RawCpuid) -> Result { + let manufacturer_id = raw_cpuid + .manufacturer_id() + .ok_or(CpuidTryFromRawCpuid::MissingLeaf0)?; + + match std::str::from_utf8(&manufacturer_id) { + Ok(VENDOR_ID_INTEL_STR) => Ok(Cpuid::Intel(IntelCpuid::from(raw_cpuid))), + Ok(VENDOR_ID_AMD_STR) => Ok(Cpuid::Amd(AmdCpuid::from(raw_cpuid))), + _ => Err(CpuidTryFromRawCpuid::UnsupportedManufacturer( + manufacturer_id, + )), + } + } +} + +impl From for RawCpuid { + #[inline] + fn from(cpuid: Cpuid) -> Self { + match cpuid { + Cpuid::Intel(intel_cpuid) => RawCpuid::from(intel_cpuid), + Cpuid::Amd(amd_cpuid) => RawCpuid::from(amd_cpuid), + } + } +} + +#[cfg(cpuid)] +impl From for kvm_bindings::CpuId { + #[inline] + fn from(cpuid: Cpuid) -> Self { + let raw_cpuid = RawCpuid::from(cpuid); + Self::from(raw_cpuid) + } +} + +/// CPUID index values `leaf` and `subleaf`. +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct CpuidKey { + /// CPUID leaf. + pub leaf: u32, + /// CPUID subleaf. + pub subleaf: u32, +} + +impl CpuidKey { + /// `CpuidKey { leaf, subleaf: 0 }` + #[inline] + #[must_use] + pub fn leaf(leaf: u32) -> Self { + Self { leaf, subleaf: 0 } + } + + /// `CpuidKey { leaf, subleaf }` + #[inline] + #[must_use] + pub fn subleaf(leaf: u32, subleaf: u32) -> Self { + Self { leaf, subleaf } + } +} + +impl std::cmp::PartialOrd for CpuidKey { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + Some( + self.leaf + .cmp(&other.leaf) + .then(self.subleaf.cmp(&other.subleaf)), + ) + } +} + +impl std::cmp::Ord for CpuidKey { + #[inline] + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.leaf + .cmp(&other.leaf) + .then(self.subleaf.cmp(&other.subleaf)) + } +} + +/// CPUID entry information stored for each leaf of [`IntelCpuid`]. +#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct CpuidEntry { + /// The KVM requires a `flags` parameter which indicates if a given CPUID leaf has sub-leaves. + /// This does not change at runtime so we can save memory by not storing this under every + /// sub-leaf and instead fetching from a map when converting back to the KVM CPUID + /// structure. But for robustness we currently do store we do not use this approach. + /// + /// A map on flags would look like: + /// ```ignore + /// use cpuid::KvmCpuidFlags; + /// #[allow(clippy::non_ascii_literal)] + /// pub static KVM_CPUID_LEAF_FLAGS: phf::Map = phf::phf_map! { + /// 0x00u32 => KvmCpuidFlags::empty(), + /// 0x01u32 => KvmCpuidFlags::empty(), + /// 0x02u32 => KvmCpuidFlags::empty(), + /// 0x03u32 => KvmCpuidFlags::empty(), + /// 0x04u32 => KvmCpuidFlags::SIGNIFICANT_INDEX, + /// 0x05u32 => KvmCpuidFlags::empty(), + /// 0x06u32 => KvmCpuidFlags::empty(), + /// 0x07u32 => KvmCpuidFlags::SIGNIFICANT_INDEX, + /// 0x09u32 => KvmCpuidFlags::empty(), + /// 0x0Au32 => KvmCpuidFlags::empty(), + /// 0x0Bu32 => KvmCpuidFlags::SIGNIFICANT_INDEX, + /// 0x0Fu32 => KvmCpuidFlags::SIGNIFICANT_INDEX, + /// 0x10u32 => KvmCpuidFlags::SIGNIFICANT_INDEX, + /// 0x12u32 => KvmCpuidFlags::SIGNIFICANT_INDEX, + /// 0x14u32 => KvmCpuidFlags::SIGNIFICANT_INDEX, + /// 0x15u32 => KvmCpuidFlags::empty(), + /// 0x16u32 => KvmCpuidFlags::empty(), + /// 0x17u32 => KvmCpuidFlags::SIGNIFICANT_INDEX, + /// 0x18u32 => KvmCpuidFlags::SIGNIFICANT_INDEX, + /// 0x19u32 => KvmCpuidFlags::empty(), + /// 0x1Au32 => KvmCpuidFlags::empty(), + /// 0x1Bu32 => KvmCpuidFlags::empty(), + /// 0x1Cu32 => KvmCpuidFlags::empty(), + /// 0x1Fu32 => KvmCpuidFlags::SIGNIFICANT_INDEX, + /// 0x20u32 => KvmCpuidFlags::empty(), + /// 0x80000000u32 => KvmCpuidFlags::empty(), + /// 0x80000001u32 => KvmCpuidFlags::empty(), + /// 0x80000002u32 => KvmCpuidFlags::empty(), + /// 0x80000003u32 => KvmCpuidFlags::empty(), + /// 0x80000004u32 => KvmCpuidFlags::empty(), + /// 0x80000005u32 => KvmCpuidFlags::empty(), + /// 0x80000006u32 => KvmCpuidFlags::empty(), + /// 0x80000007u32 => KvmCpuidFlags::empty(), + /// 0x80000008u32 => KvmCpuidFlags::empty(), + /// }; + /// ``` + pub flags: crate::cpuid_ffi::KvmCpuidFlags, + /// Register values. + pub result: CpuidRegisters, +} + +/// To transmute this into leaves such that we can return mutable reference to it with leaf specific +/// accessors, requires this to have a consistent member ordering. [`core::arch::x86::CpuidResult`] +/// is not `repr(C)`. +#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +#[repr(C)] +pub struct CpuidRegisters { + /// EAX + pub eax: u32, + /// EBX + pub ebx: u32, + /// ECX + pub ecx: u32, + /// EDX + pub edx: u32, +} + +#[cfg(cpuid)] +impl From for CpuidRegisters { + #[inline] + fn from( + core::arch::x86_64::CpuidResult { eax, ebx, ecx, edx }: core::arch::x86_64::CpuidResult, + ) -> Self { + Self { eax, ebx, ecx, edx } + } +} + +impl From<(CpuidKey, CpuidEntry)> for RawKvmCpuidEntry { + #[inline] + fn from( + (CpuidKey { leaf, subleaf }, CpuidEntry { flags, result }): (CpuidKey, CpuidEntry), + ) -> Self { + let CpuidRegisters { eax, ebx, ecx, edx } = result; + Self { + function: leaf, + index: subleaf, + flags, + eax, + ebx, + ecx, + edx, + padding: Padding::default(), + } + } +} + +impl From for (CpuidKey, CpuidEntry) { + #[inline] + fn from( + RawKvmCpuidEntry { + function, + index, + flags, + eax, + ebx, + ecx, + edx, + .. + }: RawKvmCpuidEntry, + ) -> Self { + ( + CpuidKey { + leaf: function, + subleaf: index, + }, + CpuidEntry { + flags, + result: CpuidRegisters { eax, ebx, ecx, edx }, + }, + ) + } } diff --git a/src/cpuid/src/normalize.rs b/src/cpuid/src/normalize.rs new file mode 100644 index 00000000000..3276644ecc2 --- /dev/null +++ b/src/cpuid/src/normalize.rs @@ -0,0 +1,151 @@ +// Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +use bit_fields::CheckedAssignError; + +use crate::CpuidTrait; + +/// Error type for [`Cpuid::normalize`]. +#[allow(clippy::module_name_repetitions)] +#[derive(Debug, thiserror::Error, Eq, PartialEq)] +pub enum NormalizeCpuidError { + /// Failed to apply modifications to Intel CPUID. + #[error("Failed to apply modifications to Intel CPUID: {0}")] + Intel(#[from] crate::intel::NormalizeCpuidError), + /// Failed to apply modifications to AMD CPUID. + #[error("Failed to apply modifications to AMD CPUID: {0}")] + Amd(#[from] crate::amd::NormalizeCpuidError), + /// Failed to set feature information leaf. + #[error("Failed to set feature information leaf: {0}")] + FeatureInformation(#[from] FeatureInformationError), +} + +/// Error type for setting leaf 1 section of `IntelCpuid::normalize`. +#[derive(Debug, thiserror::Error, Eq, PartialEq)] +pub enum FeatureInformationError { + /// Leaf 0x1 is missing from CPUID. + #[error("Leaf 0x1 is missing from CPUID.")] + MissingLeaf1, + /// Failed to set `Initial APIC ID`. + #[error("Failed to set `Initial APIC ID`: {0}")] + InitialApicId(CheckedAssignError), + /// Failed to set `CLFLUSH line size`. + #[error("Failed to set `CLFLUSH line size`: {0}")] + Clflush(CheckedAssignError), + /// Failed to get max CPUs per package. + #[error("Failed to get max CPUs per package: {0}")] + GetMaxCpusPerPackage(GetMaxCpusPerPackageError), + /// Failed to set max CPUs per package. + #[error("Failed to set max CPUs per package: {0}")] + SetMaxCpusPerPackage(CheckedAssignError), +} + +/// Error type for `get_max_cpus_per_package`. +#[derive(Debug, thiserror::Error, Eq, PartialEq)] +pub enum GetMaxCpusPerPackageError { + /// Failed to get max CPUs per package as `cpu_count == 0`. + #[error("Failed to get max CPUs per package as `cpu_count == 0`")] + Underflow, + /// Failed to get max CPUs per package as `cpu_count > 128`. + #[error("Failed to get max CPUs per package as `cpu_count > 128`")] + Overflow, +} + +// We use this 2nd implementation so we can conveniently define functions only used within +// `normalize`. +#[allow(clippy::multiple_inherent_impl)] +impl super::Cpuid { + /// Applies required modifications to CPUID respective of a vCPU. + /// + /// # Errors + /// + /// When: + /// - [`IntelCpuid::normalize`] errors. + /// - [`AmdCpuid::normalize`] errors. + // As we pass through host frequency, we require CPUID and thus `cfg(cpuid)`. + #[inline] + pub fn normalize( + &mut self, + // The index of the current logical CPU in the range [0..cpu_count]. + cpu_index: u8, + // The total number of logical CPUs. + cpu_count: u8, + // The number of bits needed to enumerate logical CPUs per core. + cpu_bits: u8, + ) -> Result<(), NormalizeCpuidError> { + // Update feature information entry + { + /// Flush a cache line size. + const EBX_CLFLUSH_CACHELINE: u32 = 8; + + /// CPU is running on a hypervisor. + pub const HYPERVISOR_BITINDEX: u8 = 31; + + /// 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. + const fn get_max_cpus_per_package( + cpu_count: u8, + ) -> Result { + // This match is better than but approximately equivalent to + // `2.pow((cpu_count as f32).log2().ceil() as u8)` (`2^ceil(log_2(c))`). + match cpu_count { + 0 => Err(GetMaxCpusPerPackageError::Underflow), + 1 => Ok(1), + 2 => Ok(2), + 3..=4 => Ok(4), + 5..=8 => Ok(8), + 9..=16 => Ok(16), + 17..=32 => Ok(32), + 33..=64 => Ok(64), + 65..=128 => Ok(128), + 129..=u8::MAX => Err(GetMaxCpusPerPackageError::Overflow), + } + } + + let leaf_1 = self + .leaf_mut::<0x1>() + .ok_or(FeatureInformationError::MissingLeaf1)?; + + // X86 hypervisor feature + leaf_1.ecx.tsc_deadline_mut().on(); + // Hypervisor bit + leaf_1.ecx.bit_mut::().on(); + + leaf_1 + .ebx + .initial_apic_id_mut() + .checked_assign(u32::from(cpu_index)) + .map_err(FeatureInformationError::InitialApicId)?; + leaf_1 + .ebx + .clflush_mut() + .checked_assign(EBX_CLFLUSH_CACHELINE) + .map_err(FeatureInformationError::Clflush)?; + let max_cpus_per_package = u32::from( + get_max_cpus_per_package(cpu_count) + .map_err(FeatureInformationError::GetMaxCpusPerPackage)?, + ); + leaf_1 + .ebx + .max_addressable_logical_processor_ids_mut() + .checked_assign(max_cpus_per_package) + .map_err(FeatureInformationError::SetMaxCpusPerPackage)?; + + // 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 + leaf_1.edx.htt_mut().set(cpu_count > 1); + } + + // Apply manufacturer specific modifications. + match self { + // Apply Intel specific modifications. + Self::Intel(intel_cpuid) => intel_cpuid + .normalize(cpu_index, cpu_count, cpu_bits) + .map_err(NormalizeCpuidError::Intel), + // Apply AMD specific modifications. + Self::Amd(amd_cpuid) => amd_cpuid + .normalize(cpu_index, cpu_count, cpu_bits) + .map_err(NormalizeCpuidError::Amd), + } + } +} diff --git a/src/cpuid/src/registers.rs b/src/cpuid/src/registers.rs new file mode 100644 index 00000000000..e913fa19a4e --- /dev/null +++ b/src/cpuid/src/registers.rs @@ -0,0 +1,258 @@ +// Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +use bit_fields::bitfield; + +// ------------------------------------------------------------------------------------------------- +// Leaf 1 +// ------------------------------------------------------------------------------------------------- + +bitfield!(Leaf1Eax, u32, { + /// Stepping ID. + stepping_id: 0..4, + /// Model. + model: 4..8, + /// Family ID. + family_id: 8..12, + /// Processor Type. + processor_type: 12..14, + /// Extended Model ID. + extended_model_id: 16..20, + /// Extended Family ID. + extended_family_id: 20..28, +}); + +bitfield!(Leaf1Ebx, u32, { + /// Brand Index. + brand_index: 0..8, + /// CLFLUSH line size (Value āˆ— 8 = cache line size in bytes; used also by CLFLUSHOPT). + clflush: 8..16, + /// Maximum number of addressable IDs for logical processors in this physical package. + /// + /// The nearest power-of-2 integer that is not smaller than EBX[23:16] is the number of unique + /// initial APIC IDs reserved for addressing different logical processors in a physical package. + /// This field is only valid if CPUID.1.EDX.HTT[bit 28]= 1. + max_addressable_logical_processor_ids: 16..24, + /// Initial APIC ID. + /// + /// The 8-bit initial APIC ID in EBX[31:24] is replaced by the 32-bit x2APIC ID, available in + /// Leaf 0BH and Leaf 1FH. + initial_apic_id: 24..32, +}); + +bitfield!(Leaf1Ecx, u32, { + /// Streaming SIMD Extensions 3 (SSE3). A value of 1 indicates the processor supports this + /// technology. + sse3: 0, + /// PCLMULQDQ. A value of 1 indicates the processor supports the PCLMULQDQ instruction. + pclmulqdq: 1, + /// 64-bit DS Area. A value of 1 indicates the processor supports DS area using 64-bit layout. + dtes64: 2, + /// MONITOR/MWAIT. A value of 1 indicates the processor supports this feature. + monitor: 3, + /// CPL Qualified Debug Store. A value of 1 indicates the processor supports the extensions to + /// the Debug Store feature to allow for branch message storage qualified by CPL. + ds_cpl: 4, + /// Virtual Machine Extensions. A value of 1 indicates that the processor supports this + /// technology. + vmx: 5, + /// Safer Mode Extensions. A value of 1 indicates that the processor supports this technology. + /// See Chapter 6, ā€œSafer Mode Extensions Referenceā€. + smx: 6, + /// Enhanced Intel SpeedStepĀ® technology. A value of 1 indicates that the processor supports + /// this technology. + eist: 7, + /// Thermal Monitor 2. A value of 1 indicates whether the processor supports this technology. + tm2: 8, + /// A value of 1 indicates the presence of the Supplemental Streaming SIMD Extensions 3 (SSSE3). + /// A value of 0 indicates the instruction extensions are not present in the processor. + ssse3: 9, + /// L1 Context ID. A value of 1 indicates the L1 data cache mode can be set to either adaptive + /// mode or shared mode. A value of 0 indicates this feature is not supported. See definition of + /// the IA32_MISC_ENABLE MSR Bit 24 (L1 Data Cache Context Mode) for details. + cnxt_id: 10, + /// A value of 1 indicates the processor supports IA32_DEBUG_INTERFACE MSR for silicon debug. + sdbg: 11, + /// A value of 1 indicates the processor supports FMA extensions using YMM state. + fma: 12, + /// CMPXCHG16B Available. A value of 1 indicates that the feature is available. See the + /// ā€œCMPXCHG8B/CMPXCHG16Bā€”Compare and Exchange Bytesā€ section in this chapter for a description. + cmpxchg16b: 13, + /// xTPR Update Control. A value of 1 indicates that the processor supports changing + /// IA32_MISC_ENABLE[bit 23]. + xtpr_update_control: 14, + /// Perfmon and Debug Capability: A value of 1 indicates the processor supports the performance + /// and debug feature indication MSR IA32_PERF_CAPABILITIES. + pdcm: 15, + // Reserved + /// Process-context identifiers. A value of 1 indicates that the processor supports PCIDs and + /// that software may set CR4.PCIDE to 1. + pcid: 17, + /// A value of 1 indicates the processor supports the ability to prefetch data from a memory + /// mapped device. + dca: 18, + /// A value of 1 indicates that the processor supports SSE4.1. + sse4_1: 19, + /// A value of 1 indicates that the processor supports SSE4.2. + sse4_2: 20, + /// A value of 1 indicates that the processor supports x2APIC feature. + x2apic: 21, + /// A value of 1 indicates that the processor supports MOVBE instruction. + movbe: 22, + /// A value of 1 indicates that the processor supports the POPCNT instruction. + popcnt: 23, + /// A value of 1 indicates that the processorā€™s local APIC timer supports one-shot operation + /// using a TSC deadline value. + tsc_deadline: 24, + /// A value of 1 indicates that the processor supports the AESNI instruction extensions. + aesni: 25, + /// A value of 1 indicates that the processor supports the XSAVE/XRSTOR processor extended + /// states feature, the XSETBV/XGETBV instructions, and XCR0. + xsave: 26, + /// A value of 1 indicates that the OS has set CR4.OSXSAVE[bit 18] to enable XSETBV/XGETBV + /// instructions to access XCR0 and to support processor extended state management using + /// XSAVE/XRSTOR. + osxsave: 27, + /// A value of 1 indicates the processor supports the AVX instruction extensions. + avx: 28, + /// A value of 1 indicates that processor supports 16-bit floating-point conversion instructions. + f16c: 29, + /// A value of 1 indicates that processor supports RDRAND instruction. + rdrand: 30, + // Not used +}); + +bitfield!(Leaf1Edx, u32, { + /// Floating Point Unit On-Chip. The processor contains an x87 FPU. + fpu: 0, + /// Virtual 8086 Mode Enhancements. Virtual 8086 mode enhancements, including CR4.VME for + /// controlling the feature, CR4.PVI for protected mode virtual interrupts, software interrupt + /// indirection, expansion of the TSS with the software indirection bitmap, and EFLAGS.VIF and + /// EFLAGS.VIP flags. + vme: 1, + /// Debugging Extensions. Support for I/O breakpoints, including CR4.DE for controlling the + /// feature, and optional trapping of accesses to DR4 and DR5. + de: 2, + /// Page Size Extension. Large pages of size 4 MByte are supported, including CR4.PSE for + /// controlling the feature, the defined dirty bit in PDE (Page Directory Entries), optional + /// reserved bit trapping in CR3, PDEs, and PTEs. + pse: 3, + /// Time Stamp Counter. The RDTSC instruction is supported, including CR4.TSD for controlling + /// privilege. + tsc: 4, + /// Model Specific Registers RDMSR and WRMSR Instructions. The RDMSR and WRMSR instructions are + /// supported. Some of the MSRs are implementation dependent. + msr: 5, + /// Physical Address Extension. Physical addresses greater than 32 bits are supported: extended + /// page table entry formats, an extra level in the page translation tables is defined, 2-MByte + /// pages are supported instead of 4 Mbyte pages if PAE bit is 1. + pae: 6, + /// Machine Check Exception. Exception 18 is defined for Machine Checks, including CR4.MCE for + /// controlling the feature. This feature does not define the model-specific implementations of + /// machine-check error logging, reporting, and processor shutdowns. Machine Check exception + /// handlers may have to depend on processor version to do model specific processing of the + /// exception, or test for the presence of the Machine Check feature. + mce: 7, + /// CMPXCHG8B Instruction. The compare-and-exchange 8 bytes (64 bits) instruction is supported + /// (implicitly locked and atomic). + cx8: 8, + /// APIC On-Chip. The processor contains an Advanced Programmable Interrupt Controller (APIC), + /// responding to memory mapped commands in the physical address range FFFE0000H to FFFE0FFFH + /// (by default - some processors permit the APIC to be relocated). + apic: 9, + // Reserved + /// SYSENTER and SYSEXIT Instructions. The SYSENTER and SYSEXIT and associated MSRs are + /// supported. + sep: 11, + /// Memory Type Range Registers. MTRRs are supported. The MTRRcap MSR contains feature bits that + /// describe what memory types are supported, how many variable MTRRs are supported, and whether + /// fixed MTRRs are supported. + mtrr: 12, + /// Page Global Bit. The global bit is supported in paging-structure entries that map a page, + /// indicating TLB entries that are common to different processes and need not be flushed. The + /// CR4.PGE bit controls this feature. + pge: 13, + /// Machine Check Architecture. A value of 1 indicates the Machine Check Architecture of + /// reporting machine errors is supported. The MCG_CAP MSR contains feature bits describing how + /// many banks of error reporting MSRs are supported. + mca: 14, + /// Conditional Move Instructions. The conditional move instruction CMOV is supported. In + /// addition, if x87 FPU is present as indicated by the CPUID.FPU feature bit, then the FCOMI + /// and FCMOV instructions are supported + cmov: 15, + /// Page Attribute Table. Page Attribute Table is supported. This feature augments the Memory + /// Type Range Registers (MTRRs), allowing an operating system to specify attributes of memory + /// accessed through a linear address on a 4KB granularity. + pat: 16, + /// 36-Bit Page Size Extension. 4-MByte pages addressing physical memory beyond 4 GBytes are + /// supported with 32-bit paging. This feature indicates that upper bits of the physical address + /// of a 4-MByte page are encoded in bits 20:13 of the page-directory entry. Such physical + /// addresses are limited by MAXPHYADDR and may be up to 40 bits in size. + pse3_36: 17, + /// Processor Serial Number. The processor supports the 96-bit processor identification number + /// feature and the feature is enabled. + psn: 18, + /// CLFLUSH Instruction. CLFLUSH Instruction is supported. + clfsh: 19, + // Reserved + /// Debug Store. The processor supports the ability to write debug information into a memory + /// resident buffer. This feature is used by the branch trace store (BTS) and processor + /// event-based sampling (PEBS) facilities (see Chapter 23, ā€œIntroduction to Virtual-Machine + /// Extensions,ā€ in the IntelĀ® 64 and IA-32 Architectures Software Developerā€™s Manual, Volume + /// 3C). + ds: 21, + /// Thermal Monitor and Software Controlled Clock Facilities. The processor implements internal + /// MSRs that allow processor temperature to be monitored and processor performance to be + /// modulated in predefined duty cycles under software control. + acpi: 22, + /// Intel MMX Technology. The processor supports the Intel MMX technology. + mmx: 23, + /// FXSAVE and FXRSTOR Instructions. The FXSAVE and FXRSTOR instructions are supported for fast + /// save and restore of the floating point context. Presence of this bit also indicates that + /// CR4.OSFXSR is available for an operating system to indicate that it supports the FXSAVE and + /// FXRSTOR instructions. + fxsr: 24, + /// SSE. The processor supports the SSE extensions. + sse: 25, + /// SSE2. The processor supports the SSE2 extensions. + sse2: 26, + /// Self Snoop. The processor supports the management of conflicting memory types by performing + /// a snoop of its own cache structure for transactions issued to the bus. + ss: 27, + /// Max APIC IDs reserved field is Valid. A value of 0 for HTT indicates there is only a single + /// logical processor in the package and software should assume only a single APIC ID is + /// reserved. 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. + htt: 28, + /// Thermal Monitor. The processor implements the thermal monitor automatic thermal control circuitry (TCC). + tm: 29, + // Reserved + /// Pending Break Enable. The processor supports the use of the FERR#/PBE# pin when the + /// processor is in the stop-clock state (STPCLK# is asserted) to signal the processor that an + /// interrupt is pending and that the processor should return to normal operation to handle the + /// interrupt. + pbe: 31, +}); + +// ------------------------------------------------------------------------------------------------- +// Leaf 80000002 +// ------------------------------------------------------------------------------------------------- + +bitfield!(Leaf80000002Eax, u32, { + /// Processor Brand String. + processor_brand_string: 0..32, +}); + +bitfield!(Leaf80000002Ebx, u32, { + /// Processor Brand String Continued. + processor_brand_string: 0..32, +}); + +bitfield!(Leaf80000002Ecx, u32, { + /// Processor Brand String Continued. + processor_brand_string: 0..32, +}); + +bitfield!(Leaf80000002Edx, u32, { + /// Processor Brand String Continued. + processor_brand_string: 0..32, +}); diff --git a/src/cpuid/src/template/intel/c3.rs b/src/cpuid/src/template/intel/c3.rs deleted file mode 100644 index 670aa1e4cac..00000000000 --- a/src/cpuid/src/template/intel/c3.rs +++ /dev/null @@ -1,185 +0,0 @@ -// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -use kvm_bindings::{kvm_cpuid_entry2, CpuId}; - -use crate::bit_helper::BitHelper; -use crate::cpu_leaf::*; -use crate::template::intel::validate_vendor_id; -use crate::transformer::*; - -fn update_feature_info_entry(entry: &mut kvm_cpuid_entry2, _vm_spec: &VmSpec) -> Result<(), Error> { - use crate::cpu_leaf::leaf_0x1::*; - - entry - .eax - // Extended Family ID = 0 - .write_bits_in_range(&eax::EXTENDED_FAMILY_ID_BITRANGE, 0) - // Extended Processor Model ID = 3 (Haswell) - .write_bits_in_range(&eax::EXTENDED_PROCESSOR_MODEL_BITRANGE, 3) - // Processor Type = 0 (Primary processor) - .write_bits_in_range(&eax::PROCESSOR_TYPE_BITRANGE, 0) - // Processor Family = 6 - .write_bits_in_range(&eax::PROCESSOR_FAMILY_BITRANGE, 6) - // Processor Model = 14 - .write_bits_in_range(&eax::PROCESSOR_MODEL_BITRANGE, 14) - // Stepping = 4 - .write_bits_in_range(&eax::STEPPING_BITRANGE, 4); - - // Enable/disable Features - entry - .ecx - .write_bit(ecx::DTES64_BITINDEX, false) - .write_bit(ecx::MONITOR_BITINDEX, false) - .write_bit(ecx::DS_CPL_SHIFT, false) - .write_bit(ecx::VMX_BITINDEX, false) - .write_bit(ecx::TM2_BITINDEX, false) - .write_bit(ecx::CNXT_ID_BITINDEX, false) - .write_bit(ecx::SDBG_BITINDEX, false) - .write_bit(ecx::FMA_BITINDEX, false) - .write_bit(ecx::XTPR_UPDATE_BITINDEX, false) - .write_bit(ecx::PDCM_BITINDEX, false) - .write_bit(ecx::MOVBE_BITINDEX, false) - .write_bit(ecx::OSXSAVE_BITINDEX, false); - - entry - .edx - .write_bit(edx::MCE_BITINDEX, true) - .write_bit(edx::MTRR_BITINDEX, true) - .write_bit(edx::PSN_BITINDEX, false) - .write_bit(edx::DS_BITINDEX, false) - .write_bit(edx::ACPI_BITINDEX, false) - .write_bit(edx::SS_BITINDEX, false) - .write_bit(edx::TM_BITINDEX, false) - .write_bit(edx::PBE_BITINDEX, false); - - Ok(()) -} - -fn update_structured_extended_entry( - entry: &mut kvm_cpuid_entry2, - _vm_spec: &VmSpec, -) -> Result<(), Error> { - use crate::cpu_leaf::leaf_0x7::index0::*; - - if entry.index == 0 { - entry - .ebx - .write_bit(ebx::SGX_BITINDEX, false) - .write_bit(ebx::BMI1_BITINDEX, false) - .write_bit(ebx::HLE_BITINDEX, false) - .write_bit(ebx::AVX2_BITINDEX, false) - .write_bit(ebx::FPDP_BITINDEX, false) - .write_bit(ebx::BMI2_BITINDEX, false) - .write_bit(ebx::INVPCID_BITINDEX, false) - .write_bit(ebx::RTM_BITINDEX, false) - .write_bit(ebx::RDT_M_BITINDEX, false) - .write_bit(ebx::RDT_A_BITINDEX, false) - .write_bit(ebx::MPX_BITINDEX, false) - .write_bit(ebx::AVX512F_BITINDEX, false) - .write_bit(ebx::AVX512DQ_BITINDEX, false) - .write_bit(ebx::RDSEED_BITINDEX, false) - .write_bit(ebx::ADX_BITINDEX, false) - .write_bit(ebx::AVX512IFMA_BITINDEX, false) - .write_bit(ebx::CLFLUSHOPT_BITINDEX, false) - .write_bit(ebx::CLWB_BITINDEX, false) - .write_bit(ebx::PT_BITINDEX, false) - .write_bit(ebx::AVX512PF_BITINDEX, false) - .write_bit(ebx::AVX512ER_BITINDEX, false) - .write_bit(ebx::AVX512CD_BITINDEX, false) - .write_bit(ebx::SHA_BITINDEX, false) - .write_bit(ebx::AVX512BW_BITINDEX, false) - .write_bit(ebx::AVX512VL_BITINDEX, false); - - entry - .ecx - .write_bit(ecx::AVX512_VBMI_BITINDEX, false) - .write_bit(ecx::UMIP_BITINDEX, false) - .write_bit(ecx::PKU_BITINDEX, false) - .write_bit(ecx::OSPKE_BITINDEX, false) - .write_bit(ecx::AVX512_VNNI_BITINDEX, false) - .write_bit(ecx::AVX512_VPOPCNTDQ_BITINDEX, false) - .write_bit(ecx::LA57, false) - .write_bit(ecx::RDPID_BITINDEX, false) - .write_bit(ecx::SGX_LC_BITINDEX, false); - - entry - .edx - .write_bit(edx::AVX512_4VNNIW_BITINDEX, false) - .write_bit(edx::AVX512_4FMAPS_BITINDEX, false); - } - - Ok(()) -} - -fn update_xsave_features_entry( - entry: &mut kvm_cpuid_entry2, - _vm_spec: &VmSpec, -) -> Result<(), Error> { - use crate::cpu_leaf::leaf_0xd::*; - - if entry.index == 0 { - // MPX is masked out with the current template so the size in bytes of the save - // area should be 0 (or invalid). - entry - .eax - .write_bits_in_range(&index0::eax::MPX_STATE_BITRANGE, 0); - - // AVX-512 instructions are masked out with the current template so the size in bytes - // of the save area should be 0 (or invalid). - entry - .eax - .write_bits_in_range(&index0::eax::AVX512_STATE_BITRANGE, 0); - - // OSPKE is masked in leaf_0x7 index 0 - RDPKRU/WRPKRU not exposed. - // Here we mask the XSAVE PKRU capabilities. - entry.eax.write_bit(index0::eax::PKRU_BITINDEX, false); - } - - if entry.index == 1 { - entry - .eax - .write_bit(index1::eax::XSAVEC_SHIFT, false) - .write_bit(index1::eax::XGETBV_SHIFT, false) - .write_bit(index1::eax::XSAVES_SHIFT, false); - } - - Ok(()) -} - -fn update_extended_feature_info_entry( - entry: &mut kvm_cpuid_entry2, - _vm_spec: &VmSpec, -) -> Result<(), Error> { - use crate::cpu_leaf::leaf_0x80000001::*; - - entry - .ecx - .write_bit(ecx::PREFETCH_BITINDEX, false) - .write_bit(ecx::LZCNT_BITINDEX, false); - - entry.edx.write_bit(edx::PDPE1GB_BITINDEX, false); - - Ok(()) -} - -/// Sets up the cpuid entries for a given VCPU following a C3 template. -struct C3CpuidTransformer {} - -impl CpuidTransformer for C3CpuidTransformer { - fn entry_transformer_fn(&self, entry: &mut kvm_cpuid_entry2) -> Option { - match entry.function { - leaf_0x1::LEAF_NUM => Some(update_feature_info_entry), - leaf_0x7::LEAF_NUM => Some(update_structured_extended_entry), - leaf_0xd::LEAF_NUM => Some(update_xsave_features_entry), - leaf_0x80000001::LEAF_NUM => Some(update_extended_feature_info_entry), - _ => None, - } - } -} - -/// Sets up the cpuid entries for a given VCPU following a C3 template. -pub fn set_cpuid_entries(kvm_cpuid: &mut CpuId, vm_spec: &VmSpec) -> Result<(), Error> { - validate_vendor_id()?; - C3CpuidTransformer {}.process_cpuid(kvm_cpuid, vm_spec) -} diff --git a/src/cpuid/src/template/intel/mod.rs b/src/cpuid/src/template/intel/mod.rs deleted file mode 100644 index dfb2290c60a..00000000000 --- a/src/cpuid/src/template/intel/mod.rs +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright 2020 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; -/// Follows a T2 template for setting up the CPUID with additional MSRs -/// that are speciffic to an Intel Skylake CPU. -pub mod t2s; - -use std::collections::HashSet; - -use arch_gen::x86::msr_index::*; -use kvm_bindings::CpuId; - -use crate::common::{get_vendor_id_from_host, VENDOR_ID_INTEL}; -use crate::cpuid_is_feature_set; -use crate::transformer::Error; - -pub fn validate_vendor_id() -> Result<(), Error> { - let vendor_id = get_vendor_id_from_host()?; - if &vendor_id != VENDOR_ID_INTEL { - return Err(Error::InvalidVendor); - } - - Ok(()) -} - -/// Returns MSRs to be saved based on the Intel CPUID features that are enabled. -pub(crate) fn msrs_to_save_by_cpuid(cpuid: &CpuId) -> HashSet { - use crate::cpu_leaf::*; - - let mut msrs = HashSet::new(); - - // Macro used for easy definition of CPUID-MSR dependencies. - macro_rules! cpuid_msr_dep { - ($leaf:expr, $index:expr, $reg:tt, $feature_bit:expr, $msr:expr) => { - if cpuid_is_feature_set!(cpuid, $leaf, $index, $reg, $feature_bit) { - msrs.extend($msr) - } - }; - } - - // TODO: Add more dependencies. - cpuid_msr_dep!( - 0x7, - 0, - ebx, - leaf_0x7::index0::ebx::MPX_BITINDEX, - [MSR_IA32_BNDCFGS] - ); - - // IA32_MTRR_PHYSBASEn, IA32_MTRR_PHYSMASKn - cpuid_msr_dep!(0x1, 0, edx, leaf_0x1::edx::MTRR_BITINDEX, 0x200..0x210); - - // Other MTRR MSRs - cpuid_msr_dep!( - 0x1, - 0, - edx, - leaf_0x1::edx::MTRR_BITINDEX, - [ - 0x250, // IA32_MTRR_FIX64K_00000 - 0x258, // IA32_MTRR_FIX16K_80000 - 0x259, // IA32_MTRR_FIX16K_A0000 - 0x268, // IA32_MTRR_FIX4K_C0000 - 0x269, // IA32_MTRR_FIX4K_C8000 - 0x26a, // IA32_MTRR_FIX4K_D0000 - 0x26b, // IA32_MTRR_FIX4K_D8000 - 0x26c, // IA32_MTRR_FIX4K_E0000 - 0x26d, // IA32_MTRR_FIX4K_E8000 - 0x26e, // IA32_MTRR_FIX4K_F0000 - 0x26f, // IA32_MTRR_FIX4K_F8000 - 0x277, // IA32_PAT - 0x2ff // IA32_MTRR_DEF_TYPE - ] - ); - - // MCE MSRs - // We are saving 32 MCE banks here as this is the maximum number supported by KVM - // and configured by default. - // The physical number of the MCE banks depends on the CPU. - // The number of emulated MCE banks can be configured via KVM_X86_SETUP_MCE. - cpuid_msr_dep!(0x1, 0, edx, leaf_0x1::edx::MCE_BITINDEX, 0x400..0x480); - - msrs -} - -#[cfg(test)] -pub mod tests { - use super::*; - - #[test] - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - fn test_msrs_to_save_by_cpuid_empty() { - // No CPUID entries are provided. - let entrs = []; - let cpuid = CpuId::from_entries(&entrs).unwrap(); - - let msrs = msrs_to_save_by_cpuid(&cpuid); - - // No MSRs are expected to match the CPUID features. - assert!(msrs.is_empty()); - } - - #[test] - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - fn test_msrs_to_save_by_cpuid_one() { - use kvm_bindings::kvm_cpuid_entry2; - - use crate::cpu_leaf::leaf_0x7; - - // One CPUID entry with MPX feature flag is provided - // that causes MSR_IA32_BNDCFGS to be pulled in. - let entrs = [kvm_cpuid_entry2 { - function: 0x7, - index: 0x0, - ebx: 1 << leaf_0x7::index0::ebx::MPX_BITINDEX, - ..Default::default() - }]; - let cpuid = CpuId::from_entries(&entrs).unwrap(); - - let msrs = msrs_to_save_by_cpuid(&cpuid); - - // One MSR is expected to be pulled in by the CPUID feature provided. - assert_eq!(msrs.len(), 1); - - // The expected MSR is MSR_IA32_BNDCFGS. - assert!(msrs.contains(&MSR_IA32_BNDCFGS)); - } -} diff --git a/src/cpuid/src/template/intel/t2.rs b/src/cpuid/src/template/intel/t2.rs deleted file mode 100644 index acec2266532..00000000000 --- a/src/cpuid/src/template/intel/t2.rs +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -use kvm_bindings::{kvm_cpuid_entry2, CpuId}; - -use crate::bit_helper::BitHelper; -use crate::cpu_leaf::*; -use crate::template::intel::validate_vendor_id; -use crate::transformer::*; - -pub(crate) fn update_feature_info_entry( - entry: &mut kvm_cpuid_entry2, - _vm_spec: &VmSpec, -) -> Result<(), Error> { - use crate::cpu_leaf::leaf_0x1::*; - - entry - .eax - // Extended Family ID = 0 - .write_bits_in_range(&eax::EXTENDED_FAMILY_ID_BITRANGE, 0) - // Extended Processor Model ID = 3 (Haswell) - .write_bits_in_range(&eax::EXTENDED_PROCESSOR_MODEL_BITRANGE, 3) - // Processor Type = 0 (Primary processor) - .write_bits_in_range(&eax::PROCESSOR_TYPE_BITRANGE, 0) - // Processor Family = 6 - .write_bits_in_range(&eax::PROCESSOR_FAMILY_BITRANGE, 6) - // Processor Model = 15 - .write_bits_in_range(&eax::PROCESSOR_MODEL_BITRANGE, 15) - // Stepping = 2 - .write_bits_in_range(&eax::STEPPING_BITRANGE, 2); - - // Enable/disable Features - entry - .ecx - .write_bit(ecx::DTES64_BITINDEX, false) - .write_bit(ecx::MONITOR_BITINDEX, false) - .write_bit(ecx::DS_CPL_SHIFT, false) - .write_bit(ecx::VMX_BITINDEX, false) - .write_bit(ecx::SMX_BITINDEX, false) - .write_bit(ecx::EIST_BITINDEX, false) - .write_bit(ecx::TM2_BITINDEX, false) - .write_bit(ecx::CNXT_ID_BITINDEX, false) - .write_bit(ecx::SDBG_BITINDEX, false) - .write_bit(ecx::XTPR_UPDATE_BITINDEX, false) - .write_bit(ecx::PDCM_BITINDEX, false) - .write_bit(ecx::DCA_BITINDEX, false) - .write_bit(ecx::OSXSAVE_BITINDEX, false); - - entry - .edx - .write_bit(edx::MCE_BITINDEX, true) - .write_bit(edx::MTRR_BITINDEX, true) - .write_bit(edx::PSN_BITINDEX, false) - .write_bit(edx::SSE42_BITINDEX, false) - .write_bit(edx::DS_BITINDEX, false) - .write_bit(edx::ACPI_BITINDEX, false) - .write_bit(edx::SS_BITINDEX, false) - .write_bit(edx::TM_BITINDEX, false) - .write_bit(edx::IA64_BITINDEX, false) - .write_bit(edx::PBE_BITINDEX, false); - - Ok(()) -} - -pub(crate) fn update_structured_extended_entry( - entry: &mut kvm_cpuid_entry2, - _vm_spec: &VmSpec, -) -> Result<(), Error> { - use crate::cpu_leaf::leaf_0x7::index0::*; - - if entry.index == 0 { - entry - .ebx - .write_bit(ebx::SGX_BITINDEX, false) - .write_bit(ebx::HLE_BITINDEX, false) - .write_bit(ebx::FPDP_BITINDEX, false) - .write_bit(ebx::RTM_BITINDEX, false) - .write_bit(ebx::RDT_M_BITINDEX, false) - .write_bit(ebx::FPU_CS_DS_DEPRECATE_BITINDEX, false) - .write_bit(ebx::RDT_A_BITINDEX, false) - .write_bit(ebx::MPX_BITINDEX, false) - .write_bit(ebx::AVX512F_BITINDEX, false) - .write_bit(ebx::AVX512DQ_BITINDEX, false) - .write_bit(ebx::RDSEED_BITINDEX, false) - .write_bit(ebx::ADX_BITINDEX, false) - .write_bit(ebx::AVX512IFMA_BITINDEX, false) - .write_bit(ebx::PCOMMIT_BITINDEX, false) - .write_bit(ebx::CLFLUSHOPT_BITINDEX, false) - .write_bit(ebx::CLWB_BITINDEX, false) - .write_bit(ebx::PT_BITINDEX, false) - .write_bit(ebx::AVX512PF_BITINDEX, false) - .write_bit(ebx::AVX512ER_BITINDEX, false) - .write_bit(ebx::AVX512CD_BITINDEX, false) - .write_bit(ebx::SHA_BITINDEX, false) - .write_bit(ebx::AVX512BW_BITINDEX, false) - .write_bit(ebx::AVX512VL_BITINDEX, false); - - entry - .ecx - .write_bit(ecx::AVX512_VBMI_BITINDEX, false) - .write_bit(ecx::UMIP_BITINDEX, false) - .write_bit(ecx::PKU_BITINDEX, false) - .write_bit(ecx::OSPKE_BITINDEX, false) - .write_bit(ecx::AVX512_VNNI_BITINDEX, false) - .write_bit(ecx::AVX512_VPOPCNTDQ_BITINDEX, false) - .write_bit(ecx::LA57, false) - .write_bit(ecx::RDPID_BITINDEX, false) - .write_bit(ecx::SGX_LC_BITINDEX, false); - - entry - .edx - .write_bit(edx::AVX512_4VNNIW_BITINDEX, false) - .write_bit(edx::AVX512_4FMAPS_BITINDEX, false); - } - - Ok(()) -} - -pub(crate) fn update_xsave_features_entry( - entry: &mut kvm_cpuid_entry2, - _vm_spec: &VmSpec, -) -> Result<(), Error> { - use crate::cpu_leaf::leaf_0xd::*; - - if entry.index == 0 { - // MPX is masked out with the current template so the size in bytes of the save - // area should be 0 (or invalid). - entry - .eax - .write_bits_in_range(&index0::eax::MPX_STATE_BITRANGE, 0); - - // AVX-512 instructions are masked out with the current template so the size in bytes - // of the save area should be 0 (or invalid). - entry - .eax - .write_bits_in_range(&index0::eax::AVX512_STATE_BITRANGE, 0); - - // OSPKE is masked in leaf_0x7 index 0 - RDPKRU/WRPKRU not exposed. - // Here we mask the XSAVE PKRU capabilities. - entry.eax.write_bit(index0::eax::PKRU_BITINDEX, false); - } - - if entry.index == 1 { - entry - .eax - .write_bit(index1::eax::XSAVEC_SHIFT, false) - .write_bit(index1::eax::XGETBV_SHIFT, false) - .write_bit(index1::eax::XSAVES_SHIFT, false); - } - - Ok(()) -} - -pub(crate) fn update_extended_feature_info_entry( - entry: &mut kvm_cpuid_entry2, - _vm_spec: &VmSpec, -) -> Result<(), Error> { - use crate::cpu_leaf::leaf_0x80000001::*; - - entry.ecx.write_bit(ecx::PREFETCH_BITINDEX, false); - - entry.edx.write_bit(edx::PDPE1GB_BITINDEX, false); - - Ok(()) -} - -/// Sets up the cpuid entries for a given VCPU following a T2 template. -struct T2CpuidTransformer {} - -impl CpuidTransformer for T2CpuidTransformer { - fn entry_transformer_fn(&self, entry: &mut kvm_cpuid_entry2) -> Option { - match entry.function { - leaf_0x1::LEAF_NUM => Some(update_feature_info_entry), - leaf_0x7::LEAF_NUM => Some(update_structured_extended_entry), - leaf_0xd::LEAF_NUM => Some(update_xsave_features_entry), - leaf_0x80000001::LEAF_NUM => Some(update_extended_feature_info_entry), - _ => None, - } - } -} - -/// Sets up the cpuid entries for a given VCPU following a T2 template. -pub fn set_cpuid_entries(kvm_cpuid: &mut CpuId, vm_spec: &VmSpec) -> Result<(), Error> { - validate_vendor_id()?; - T2CpuidTransformer {}.process_cpuid(kvm_cpuid, vm_spec) -} diff --git a/src/cpuid/src/template/intel/t2s.rs b/src/cpuid/src/template/intel/t2s.rs deleted file mode 100644 index d6a4ed43a26..00000000000 --- a/src/cpuid/src/template/intel/t2s.rs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -use arch::x86_64::msr::{ArchCapaMSRFlags, MSR_IA32_ARCH_CAPABILITIES}; -use kvm_bindings::{kvm_cpuid_entry2, kvm_msr_entry, CpuId}; - -use crate::cpu_leaf::*; -use crate::template::intel::validate_vendor_id; -use crate::transformer::*; - -/// Sets up the cpuid entries for a given VCPU following a T2S template. -struct T2SCpuidTransformer {} - -impl CpuidTransformer for T2SCpuidTransformer { - fn entry_transformer_fn(&self, entry: &mut kvm_cpuid_entry2) -> Option { - match entry.function { - leaf_0x1::LEAF_NUM => Some(crate::t2::update_feature_info_entry), - leaf_0x7::LEAF_NUM => Some(crate::t2::update_structured_extended_entry), - leaf_0xd::LEAF_NUM => Some(crate::t2::update_xsave_features_entry), - leaf_0x80000001::LEAF_NUM => Some(crate::t2::update_extended_feature_info_entry), - _ => None, - } - } -} - -/// Sets up the cpuid entries for a given VCPU following a T2S template. -pub fn set_cpuid_entries(kvm_cpuid: &mut CpuId, vm_spec: &VmSpec) -> Result<(), Error> { - validate_vendor_id()?; - T2SCpuidTransformer {}.process_cpuid(kvm_cpuid, vm_spec) -} - -/// Add the MSR entries speciffic to this T2S template. -pub fn update_msr_entries(msr_entries: &mut Vec) { - let capabilities = ArchCapaMSRFlags::RSBA - | ArchCapaMSRFlags::SKIP_L1DFL_VMENTRY - | ArchCapaMSRFlags::IF_PSCHANGE_MC_NO - | ArchCapaMSRFlags::MISC_PACKAGE_CTRLS - | ArchCapaMSRFlags::ENERGY_FILTERING_CTL; - msr_entries.push(kvm_msr_entry { - index: MSR_IA32_ARCH_CAPABILITIES, - data: capabilities.bits(), - ..Default::default() - }); -} - -static EXTRA_MSR_ENTRIES: &[u32] = &[MSR_IA32_ARCH_CAPABILITIES]; - -/// Return a list of MSRs speciffic to this T2S template. -pub fn msr_entries_to_save() -> &'static [u32] { - EXTRA_MSR_ENTRIES -} diff --git a/src/cpuid/src/template/mod.rs b/src/cpuid/src/template/mod.rs deleted file mode 100644 index fd0957a7df9..00000000000 --- a/src/cpuid/src/template/mod.rs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -// Contains Intel specific templates. -pub mod intel; - -use std::collections::HashSet; - -use kvm_bindings::CpuId; - -use crate::common::{get_vendor_id_from_cpuid, VENDOR_ID_INTEL}; -use crate::transformer::Error; - -/// Returns MSRs to be saved based on CPUID features that are enabled. -pub fn msrs_to_save_by_cpuid(cpuid: &CpuId) -> Result, Error> { - let vendor_id = get_vendor_id_from_cpuid(cpuid).map_err(|_| Error::InvalidVendor)?; - match &vendor_id { - VENDOR_ID_INTEL => Ok(intel::msrs_to_save_by_cpuid(cpuid)), - _ => { - // We don't have MSR-CPUID dependencies set for other vendors yet. - Ok(HashSet::new()) - } - } -} diff --git a/src/cpuid/src/transformer/amd.rs b/src/cpuid/src/transformer/amd.rs deleted file mode 100644 index 5c90f5ebfb7..00000000000 --- a/src/cpuid/src/transformer/amd.rs +++ /dev/null @@ -1,382 +0,0 @@ -// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -use kvm_bindings::{CpuId, KVM_CPUID_FLAG_SIGNIFCANT_INDEX}; - -use super::*; -use crate::bit_helper::BitHelper; -use crate::cpu_leaf::*; -use crate::transformer::common::use_host_cpuid_function; - -// Largest extended function. It has to be larger then 0x8000001d (Extended Cache Topology). -const LARGEST_EXTENDED_FN: u32 = 0x8000_001f; -// This value allows at most 64 logical threads within a package. -// See also the documentation for leaf_0x80000008::ecx::THREAD_ID_SIZE_BITRANGE -const THREAD_ID_MAX_SIZE: u32 = 7; -// This value means there is 1 node per processor. -// See also the documentation for leaf_0x8000001e::ecx::NODES_PER_PROCESSOR_BITRANGE. -const NODES_PER_PROCESSOR: u32 = 0; - -pub fn update_structured_extended_entry( - entry: &mut kvm_cpuid_entry2, - _vm_spec: &VmSpec, -) -> Result<(), Error> { - use crate::cpu_leaf::leaf_0x7::index0::*; - - // according to the EPYC PPR, only the leaf 0x7 with index 0 contains the - // structured extended feature identifiers - if entry.index == 0 { - // KVM sets this bit no matter what but this feature is not supported by hardware - entry.edx.write_bit(edx::ARCH_CAPABILITIES_BITINDEX, false); - } - - Ok(()) -} - -pub fn update_largest_extended_fn_entry( - entry: &mut kvm_cpuid_entry2, - _vm_spec: &VmSpec, -) -> Result<(), Error> { - use crate::cpu_leaf::leaf_0x80000000::*; - - // KVM sets the largest extended function to 0x80000000. Change it to 0x8000001f - // Since we also use the leaf 0x8000001d (Extended Cache Topology). - entry - .eax - .write_bits_in_range(&eax::LARGEST_EXTENDED_FN_BITRANGE, LARGEST_EXTENDED_FN); - - Ok(()) -} - -pub fn update_extended_feature_info_entry( - entry: &mut kvm_cpuid_entry2, - _vm_spec: &VmSpec, -) -> Result<(), Error> { - use crate::cpu_leaf::leaf_0x80000001::*; - - // set the Topology Extension bit since we use the Extended Cache Topology leaf - entry.ecx.write_bit(ecx::TOPOEXT_INDEX, true); - - Ok(()) -} - -pub fn update_amd_features_entry( - entry: &mut kvm_cpuid_entry2, - vm_spec: &VmSpec, -) -> Result<(), Error> { - use crate::cpu_leaf::leaf_0x80000008::*; - - // We don't support more then 128 threads right now. - // It's safe to put them all on the same processor. - entry - .ecx - .write_bits_in_range(&ecx::THREAD_ID_SIZE_BITRANGE, THREAD_ID_MAX_SIZE) - .write_bits_in_range(&ecx::NUM_THREADS_BITRANGE, u32::from(vm_spec.cpu_count - 1)); - - Ok(()) -} - -pub fn update_extended_cache_topology_entry( - entry: &mut kvm_cpuid_entry2, - vm_spec: &VmSpec, -) -> Result<(), Error> { - entry.flags |= KVM_CPUID_FLAG_SIGNIFCANT_INDEX; - - common::update_cache_parameters_entry(entry, vm_spec) -} - -pub fn update_extended_apic_id_entry( - entry: &mut kvm_cpuid_entry2, - vm_spec: &VmSpec, -) -> Result<(), Error> { - use crate::cpu_leaf::leaf_0x8000001e::*; - - // When hyper-threading is enabled each pair of 2 consecutive logical CPUs - // will have the same core id since they represent 2 threads in the same core. - // For Example: - // logical CPU 0 -> core id: 0 - // logical CPU 1 -> core id: 0 - // logical CPU 2 -> core id: 1 - // logical CPU 3 -> core id: 1 - let core_id = u32::from(vm_spec.cpu_index / vm_spec.cpus_per_core()); - - entry - .eax - // the Extended APIC ID is the id of the current logical CPU - .write_bits_in_range( - &eax::EXTENDED_APIC_ID_BITRANGE, - u32::from(vm_spec.cpu_index), - ); - - entry - .ebx - .write_bits_in_range(&ebx::CORE_ID_BITRANGE, core_id) - .write_bits_in_range( - &ebx::THREADS_PER_CORE_BITRANGE, - u32::from(vm_spec.cpus_per_core() - 1), - ); - - entry - .ecx - .write_bits_in_range(&ecx::NODES_PER_PROCESSOR_BITRANGE, NODES_PER_PROCESSOR) - // Put all the cpus in the same node. - .write_bits_in_range(&ecx::NODE_ID_BITRANGE, 0); - - Ok(()) -} - -pub struct AmdCpuidTransformer {} - -impl CpuidTransformer for AmdCpuidTransformer { - fn process_cpuid(&self, cpuid: &mut CpuId, vm_spec: &VmSpec) -> Result<(), Error> { - // Some versions of kernel may return the 0xB leaf for AMD even if this is an - // Intel-specific leaf. Remove it. - cpuid.retain(|entry| entry.function != leaf_0xb::LEAF_NUM); - use_host_cpuid_function(cpuid, leaf_0x8000001e::LEAF_NUM, false)?; - use_host_cpuid_function(cpuid, leaf_0x8000001d::LEAF_NUM, true)?; - self.process_entries(cpuid, vm_spec) - } - - fn entry_transformer_fn(&self, entry: &mut kvm_cpuid_entry2) -> Option { - match entry.function { - leaf_0x1::LEAF_NUM => Some(common::update_feature_info_entry), - leaf_0x7::LEAF_NUM => Some(amd::update_structured_extended_entry), - leaf_0x80000000::LEAF_NUM => Some(amd::update_largest_extended_fn_entry), - leaf_0x80000001::LEAF_NUM => Some(amd::update_extended_feature_info_entry), - leaf_0x80000008::LEAF_NUM => Some(amd::update_amd_features_entry), - leaf_0x8000001d::LEAF_NUM => Some(amd::update_extended_cache_topology_entry), - leaf_0x8000001e::LEAF_NUM => Some(amd::update_extended_apic_id_entry), - 0x8000_0002..=0x8000_0004 => Some(common::update_brand_string_entry), - _ => None, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_process_cpuid() { - let vm_spec = VmSpec::new(0, 1, false).expect("Error creating vm_spec"); - let mut cpuid = CpuId::new(0).unwrap(); - let entry = kvm_cpuid_entry2 { - function: leaf_0xb::LEAF_NUM, - index: 0, - flags: 0, - eax: 0, - ebx: 0, - ecx: 0, - edx: 0, - padding: [0, 0, 0], - }; - cpuid.push(entry).unwrap(); - - assert!(AmdCpuidTransformer {} - .process_cpuid(&mut cpuid, &vm_spec) - .is_ok()); - assert!(!cpuid.as_slice().contains(&entry)); - } - - #[test] - fn test_update_structured_extended_entry() { - use crate::cpu_leaf::leaf_0x7::index0::*; - - // Check that if index == 0 the entry is processed - let vm_spec = VmSpec::new(0, 1, false).expect("Error creating vm_spec"); - let mut entry = &mut kvm_cpuid_entry2 { - function: leaf_0x7::LEAF_NUM, - index: 0, - flags: 0, - eax: 0, - ebx: 0, - ecx: 0, - edx: *(0_u32).write_bit(edx::ARCH_CAPABILITIES_BITINDEX, true), - padding: [0, 0, 0], - }; - assert!(update_structured_extended_entry(entry, &vm_spec).is_ok()); - assert!(!entry.edx.read_bit(edx::ARCH_CAPABILITIES_BITINDEX)); - - // Check that if index != 0 the entry is not processed - entry.index = 1; - entry.edx.write_bit(edx::ARCH_CAPABILITIES_BITINDEX, true); - assert!(update_structured_extended_entry(entry, &vm_spec).is_ok()); - assert!(entry.edx.read_bit(edx::ARCH_CAPABILITIES_BITINDEX)); - } - - #[test] - fn test_update_largest_extended_fn_entry() { - use crate::cpu_leaf::leaf_0x80000000::*; - - let vm_spec = VmSpec::new(0, 1, false).expect("Error creating vm_spec"); - let entry = &mut kvm_cpuid_entry2 { - function: LEAF_NUM, - index: 0, - flags: 0, - eax: 0, - ebx: 0, - ecx: 0, - edx: 0, - padding: [0, 0, 0], - }; - - assert!(update_largest_extended_fn_entry(entry, &vm_spec).is_ok()); - - assert_eq!( - entry - .eax - .read_bits_in_range(&eax::LARGEST_EXTENDED_FN_BITRANGE), - LARGEST_EXTENDED_FN - ); - } - - #[test] - fn test_update_extended_feature_info_entry() { - use crate::cpu_leaf::leaf_0x80000001::*; - - let vm_spec = VmSpec::new(0, 1, false).expect("Error creating vm_spec"); - let entry = &mut kvm_cpuid_entry2 { - function: LEAF_NUM, - index: 0, - flags: 0, - eax: 0, - ebx: 0, - ecx: 0, - edx: 0, - padding: [0, 0, 0], - }; - - assert!(matches!( - update_extended_feature_info_entry(entry, &vm_spec), - Ok(()) - )); - - assert!(entry.ecx.read_bit(ecx::TOPOEXT_INDEX)); - } - - fn check_update_amd_features_entry(cpu_count: u8, smt: bool) { - use crate::cpu_leaf::leaf_0x80000008::*; - - let vm_spec = VmSpec::new(0, cpu_count, smt).expect("Error creating vm_spec"); - let entry = &mut kvm_cpuid_entry2 { - function: LEAF_NUM, - index: 0, - flags: 0, - eax: 0, - ebx: 0, - ecx: 0, - edx: 0, - padding: [0, 0, 0], - }; - - assert!(update_amd_features_entry(entry, &vm_spec).is_ok()); - - assert_eq!( - entry.ecx.read_bits_in_range(&ecx::NUM_THREADS_BITRANGE), - u32::from(cpu_count - 1) - ); - assert_eq!( - entry.ecx.read_bits_in_range(&ecx::THREAD_ID_SIZE_BITRANGE), - THREAD_ID_MAX_SIZE - ); - } - - fn check_update_extended_apic_id_entry( - cpu_id: u8, - cpu_count: u8, - smt: bool, - expected_core_id: u32, - expected_threads_per_core: u32, - ) { - use crate::cpu_leaf::leaf_0x8000001e::*; - - let vm_spec = VmSpec::new(cpu_id, cpu_count, smt).expect("Error creating vm_spec"); - let entry = &mut kvm_cpuid_entry2 { - function: LEAF_NUM, - index: 0, - flags: 0, - eax: 0, - ebx: 0, - ecx: 0, - edx: 0, - padding: [0, 0, 0], - }; - - assert!(update_extended_apic_id_entry(entry, &vm_spec).is_ok()); - - assert_eq!( - entry - .eax - .read_bits_in_range(&eax::EXTENDED_APIC_ID_BITRANGE), - u32::from(cpu_id) - ); - - assert_eq!( - entry.ebx.read_bits_in_range(&ebx::CORE_ID_BITRANGE), - expected_core_id - ); - assert_eq!( - entry - .ebx - .read_bits_in_range(&ebx::THREADS_PER_CORE_BITRANGE), - expected_threads_per_core - ); - - assert_eq!( - entry - .ecx - .read_bits_in_range(&ecx::NODES_PER_PROCESSOR_BITRANGE), - NODES_PER_PROCESSOR - ); - assert_eq!(entry.ecx.read_bits_in_range(&ecx::NODE_ID_BITRANGE), 0); - } - - #[test] - fn test_update_extended_cache_topology_entry() { - let vm_spec = VmSpec::new(0, 1, false).expect("Error creating vm_spec"); - let entry = &mut kvm_cpuid_entry2 { - function: leaf_0x8000001d::LEAF_NUM, - index: 0, - flags: 0, - eax: 0, - ebx: 0, - ecx: 0, - edx: 0, - padding: [0, 0, 0], - }; - - assert!(update_extended_cache_topology_entry(entry, &vm_spec).is_ok()); - - assert_eq!(entry.flags & KVM_CPUID_FLAG_SIGNIFCANT_INDEX, 1); - } - - #[test] - fn test_1vcpu_ht_off() { - check_update_amd_features_entry(1, false); - - check_update_extended_apic_id_entry(0, 1, false, 0, 0); - } - - #[test] - fn test_1vcpu_ht_on() { - check_update_amd_features_entry(1, true); - - check_update_extended_apic_id_entry(0, 1, true, 0, 0); - } - - #[test] - fn test_2vcpu_ht_off() { - check_update_amd_features_entry(2, false); - - check_update_extended_apic_id_entry(0, 2, false, 0, 0); - check_update_extended_apic_id_entry(1, 2, false, 1, 0); - } - - #[test] - fn test_2vcpu_ht_on() { - check_update_amd_features_entry(2, true); - - check_update_extended_apic_id_entry(0, 2, true, 0, 1); - check_update_extended_apic_id_entry(1, 2, true, 0, 1); - } -} diff --git a/src/cpuid/src/transformer/common.rs b/src/cpuid/src/transformer/common.rs deleted file mode 100644 index a3ed9577ac9..00000000000 --- a/src/cpuid/src/transformer/common.rs +++ /dev/null @@ -1,307 +0,0 @@ -// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -use kvm_bindings::{kvm_cpuid_entry2, CpuId}; - -use super::*; -use crate::bit_helper::BitHelper; -use crate::common::get_cpuid; - -// 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 crate::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_index)) - .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_BITINDEX, 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(()) -} - -pub fn update_cache_parameters_entry( - entry: &mut kvm_cpuid_entry2, - vm_spec: &VmSpec, -) -> Result<(), Error> { - use crate::cpu_leaf::leaf_cache_parameters::*; - - 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, - u32::from(vm_spec.cpus_per_core() - 1), - ); - } - // 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), - ); - } - _ => (), - } - - Ok(()) -} - -/// Replaces the `cpuid` entries corresponding to `function` with the entries from the host's cpuid. -pub fn use_host_cpuid_function( - cpuid: &mut CpuId, - function: u32, - use_count: bool, -) -> Result<(), Error> { - // copy all the CpuId entries, except for the ones with the provided function - cpuid.retain(|entry| entry.function != function); - - // add all the host leaves with the provided function - let mut count: u32 = 0; - while let Ok(entry) = get_cpuid(function, count) { - if count > 0 && !use_count { - break; - } - - cpuid - .push(kvm_cpuid_entry2 { - function, - index: count, - flags: 0, - eax: entry.eax, - ebx: entry.ebx, - ecx: entry.ecx, - edx: entry.edx, - padding: [0, 0, 0], - }) - .map_err(Error::Fam)?; - - count += 1; - } - - Ok(()) -} - -#[cfg(test)] -mod tests { - use kvm_bindings::kvm_cpuid_entry2; - - use super::*; - use crate::common::tests::get_topoext_fn; - use crate::transformer::VmSpec; - - #[test] - fn test_get_max_cpus_per_package() { - 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 crate::cpu_leaf::leaf_0x1::*; - - let vm_spec = VmSpec::new(0, cpu_count, false).expect("Error creating vm_spec"); - let entry = &mut kvm_cpuid_entry2 { - function: 0x0, - index: 0, - flags: 0, - eax: 0, - ebx: 0, - ecx: 0, - edx: 0, - padding: [0, 0, 0], - }; - - assert!(matches!(update_feature_info_entry(entry, &vm_spec), Ok(()))); - - assert_eq!(entry.edx.read_bit(edx::HTT_BITINDEX), expected_htt); - assert!(entry.ecx.read_bit(ecx::TSC_DEADLINE_TIMER_BITINDEX)); - } - - fn check_update_cache_parameters_entry( - cpu_count: u8, - smt: bool, - cache_level: u32, - expected_max_cpus_per_core: u32, - ) { - use crate::cpu_leaf::leaf_cache_parameters::*; - - let vm_spec = VmSpec::new(0, cpu_count, smt).expect("Error creating vm_spec"); - let entry = &mut kvm_cpuid_entry2 { - function: 0x0, - index: 0, - flags: 0, - eax: *(0_u32).write_bits_in_range(&eax::CACHE_LEVEL_BITRANGE, cache_level), - ebx: 0, - ecx: 0, - edx: 0, - padding: [0, 0, 0], - }; - - assert!(update_cache_parameters_entry(entry, &vm_spec).is_ok()); - - assert!( - entry - .eax - .read_bits_in_range(&eax::MAX_CPUS_PER_CORE_BITRANGE) - == expected_max_cpus_per_core - ); - } - - #[test] - fn test_1vcpu_ht_off() { - check_update_feature_info_entry(1, false); - - // test update_deterministic_cache_entry - // test L1 - check_update_cache_parameters_entry(1, false, 1, 0); - // test L2 - check_update_cache_parameters_entry(1, false, 2, 0); - // test L3 - check_update_cache_parameters_entry(1, false, 3, 0); - } - - #[test] - fn test_1vcpu_ht_on() { - check_update_feature_info_entry(1, false); - - // test update_deterministic_cache_entry - // test L1 - check_update_cache_parameters_entry(1, true, 1, 0); - // test L2 - check_update_cache_parameters_entry(1, true, 2, 0); - // test L3 - check_update_cache_parameters_entry(1, true, 3, 0); - } - - #[test] - fn test_2vcpu_ht_off() { - check_update_feature_info_entry(2, true); - - // test update_deterministic_cache_entry - // test L1 - check_update_cache_parameters_entry(2, false, 1, 0); - // test L2 - check_update_cache_parameters_entry(2, false, 2, 0); - // test L3 - check_update_cache_parameters_entry(2, false, 3, 1); - } - - #[test] - fn test_2vcpu_ht_on() { - check_update_feature_info_entry(2, true); - - // test update_deterministic_cache_entry - // test L1 - check_update_cache_parameters_entry(2, true, 1, 1); - // test L2 - check_update_cache_parameters_entry(2, true, 2, 1); - // test L3 - check_update_cache_parameters_entry(2, true, 3, 1); - } - - #[test] - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - fn test_use_host_cpuid_function_with_count() { - // try to emulate the extended cache topology leaves - let topoext_fn = get_topoext_fn(); - - // check that it behaves correctly for TOPOEXT function - let mut cpuid = CpuId::new(1).unwrap(); - cpuid.as_mut_slice()[0].function = topoext_fn; - assert!(use_host_cpuid_function(&mut cpuid, topoext_fn, true).is_ok()); - let entries = cpuid.as_mut_slice(); - assert!(entries.len() > 1); - for (count, entry) in entries.iter_mut().enumerate() { - assert!(entry.function == topoext_fn); - assert!(entry.index == count as u32); - assert!(entry.eax != 0); - } - } - - #[test] - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - fn test_use_host_cpuid_function_without_count() { - use crate::cpu_leaf::leaf_0x1::*; - // try to emulate the extended cache topology leaves - let feature_info_fn = LEAF_NUM; - - // check that it behaves correctly for TOPOEXT function - let mut cpuid = CpuId::new(1).unwrap(); - cpuid.as_mut_slice()[0].function = feature_info_fn; - assert!(use_host_cpuid_function(&mut cpuid, feature_info_fn, false).is_ok()); - let entries = cpuid.as_mut_slice(); - assert!(entries.len() == 1); - let entry = entries[0]; - - assert!(entry.function == feature_info_fn); - assert!(entry.index == 0); - assert!(entry.eax != 0); - } - - #[test] - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - fn test_use_host_cpuid_function_err() { - let topoext_fn = get_topoext_fn(); - // check that it returns Err when there are too many entriesentry.function == topoext_fn - let mut cpuid = CpuId::new(kvm_bindings::KVM_MAX_CPUID_ENTRIES).unwrap(); - match use_host_cpuid_function(&mut cpuid, topoext_fn, true) { - Err(Error::Fam(utils::fam::Error::SizeLimitExceeded)) => {} - _ => panic!("Wrong behavior"), - } - } -} diff --git a/src/cpuid/src/transformer/intel.rs b/src/cpuid/src/transformer/intel.rs deleted file mode 100644 index f9fbf8ef9b8..00000000000 --- a/src/cpuid/src/transformer/intel.rs +++ /dev/null @@ -1,296 +0,0 @@ -// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -use super::*; -use crate::bit_helper::BitHelper; -use crate::cpu_leaf::*; - -// 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 128 logical processors/package. -const LEAFBH_INDEX1_APICID: u32 = 7; - -fn update_deterministic_cache_entry( - entry: &mut kvm_cpuid_entry2, - vm_spec: &VmSpec, -) -> Result<(), Error> { - use crate::cpu_leaf::leaf_0x4::*; - - common::update_cache_parameters_entry(entry, vm_spec)?; - - // 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 / vm_spec.cpus_per_core()) - 1, - ); - - Ok(()) -} - -fn update_power_management_entry( - entry: &mut kvm_cpuid_entry2, - _vm_spec: &VmSpec, -) -> Result<(), Error> { - use crate::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_topology_entry( - entry: &mut kvm_cpuid_entry2, - vm_spec: &VmSpec, -) -> Result<(), Error> { - use crate::cpu_leaf::leaf_0xb::*; - - // reset eax, ebx, ecx - entry.eax = 0_u32; - entry.ebx = 0_u32; - entry.ecx = 0_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_index); - - // "If SMT is not present in a processor implementation but CPUID leaf 0BH is supported, - // CPUID.EAX=0BH, ECX=0 will return EAX = 0, EBX = 1 and level type = 1. - // Number of logical processors at the core level is reported at level type = 2." - // (IntelĀ® 64 Architecture x2APIC Specification, Ch. 2.8) - 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, u32::from(vm_spec.cpu_bits)); - // 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, - u32::from(vm_spec.cpus_per_core()), - ); - - entry - .ecx - .write_bits_in_range(&ecx::LEVEL_TYPE_BITRANGE, LEVEL_TYPE_THREAD); - } - // Core Level Processor Topology; index = 1 - 1 => { - entry - .eax - .write_bits_in_range(&eax::APICID_BITRANGE, LEAFBH_INDEX1_APICID); - 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_NUMBER_BITRANGE, entry.index as u32); - 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 correctly, - // and it is added here as a "re-enforcement" in case we run on - // different hardware - level => { - entry.ecx = level; - } - } - - Ok(()) -} - -pub struct IntelCpuidTransformer {} - -impl CpuidTransformer for IntelCpuidTransformer { - fn entry_transformer_fn(&self, entry: &mut kvm_cpuid_entry2) -> Option { - match entry.function { - leaf_0x1::LEAF_NUM => Some(common::update_feature_info_entry), - leaf_0x4::LEAF_NUM => Some(intel::update_deterministic_cache_entry), - leaf_0x6::LEAF_NUM => Some(intel::update_power_management_entry), - leaf_0xa::LEAF_NUM => Some(intel::update_perf_mon_entry), - leaf_0xb::LEAF_NUM => Some(intel::update_extended_topology_entry), - 0x8000_0002..=0x8000_0004 => Some(common::update_brand_string_entry), - _ => None, - } - } -} - -#[cfg(test)] -mod tests { - use kvm_bindings::kvm_cpuid_entry2; - - use super::*; - use crate::cpu_leaf::leaf_0xb::{LEVEL_TYPE_CORE, LEVEL_TYPE_THREAD}; - use crate::transformer::VmSpec; - - #[test] - fn test_update_perf_mon_entry() { - let vm_spec = VmSpec::new(0, 1, false).expect("Error creating vm_spec"); - let entry = &mut kvm_cpuid_entry2 { - function: leaf_0xa::LEAF_NUM, - index: 0, - flags: 0, - eax: 1, - ebx: 1, - ecx: 1, - edx: 1, - padding: [0, 0, 0], - }; - - assert!(update_perf_mon_entry(entry, &vm_spec).is_ok()); - - assert_eq!(entry.eax, 0); - assert_eq!(entry.ebx, 0); - assert_eq!(entry.ecx, 0); - assert_eq!(entry.edx, 0); - } - - fn check_update_deterministic_cache_entry( - cpu_count: u8, - smt: bool, - cache_level: u32, - expected_max_cores_per_package: u32, - ) { - use crate::cpu_leaf::leaf_0x4::*; - - let vm_spec = VmSpec::new(0, cpu_count, smt).expect("Error creating vm_spec"); - let entry = &mut kvm_cpuid_entry2 { - function: 0x0, - index: 0, - flags: 0, - eax: *(0_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(entry, &vm_spec).is_ok()); - - assert!( - entry - .eax - .read_bits_in_range(&eax::MAX_CORES_PER_PACKAGE_BITRANGE) - == expected_max_cores_per_package - ); - } - - fn check_update_extended_topology_entry( - cpu_count: u8, - smt: bool, - index: u32, - expected_apicid: u32, - expected_num_logical_processors: u32, - expected_level_type: u32, - ) { - use crate::cpu_leaf::leaf_0xb::*; - - let vm_spec = VmSpec::new(0, cpu_count, smt).expect("Error creating vm_spec"); - let 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_topology_entry(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); - // test L2 - check_update_deterministic_cache_entry(1, false, 2, 0); - // test L3 - check_update_deterministic_cache_entry(1, false, 3, 0); - - // test update_extended_topology_entry - // index 0 - check_update_extended_topology_entry(1, false, 0, 0, 1, LEVEL_TYPE_THREAD); - // index 1 - check_update_extended_topology_entry(1, false, 1, LEAFBH_INDEX1_APICID, 1, LEVEL_TYPE_CORE); - } - - #[test] - fn test_1vcpu_ht_on() { - // test update_deterministic_cache_entry - // test L1 - check_update_deterministic_cache_entry(1, true, 1, 0); - // test L2 - check_update_deterministic_cache_entry(1, true, 2, 0); - // test L3 - check_update_deterministic_cache_entry(1, true, 3, 0); - - // test update_extended_topology_entry - // index 0 - check_update_extended_topology_entry(1, true, 0, 0, 1, LEVEL_TYPE_THREAD); - // index 1 - check_update_extended_topology_entry(1, true, 1, LEAFBH_INDEX1_APICID, 1, LEVEL_TYPE_CORE); - } - - #[test] - fn test_2vcpu_ht_off() { - // test update_deterministic_cache_entry - // test L1 - check_update_deterministic_cache_entry(2, false, 1, 1); - // test L2 - check_update_deterministic_cache_entry(2, false, 2, 1); - // test L3 - check_update_deterministic_cache_entry(2, false, 3, 1); - - // test update_extended_topology_entry - // index 0 - check_update_extended_topology_entry(2, false, 0, 0, 1, LEVEL_TYPE_THREAD); - // index 1 - check_update_extended_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, 0); - // test L2 - check_update_deterministic_cache_entry(2, true, 2, 0); - // test L3 - check_update_deterministic_cache_entry(2, true, 3, 0); - - // test update_extended_topology_entry - // index 0 - check_update_extended_topology_entry(2, true, 0, 1, 2, LEVEL_TYPE_THREAD); - // index 1 - check_update_extended_topology_entry(2, true, 1, LEAFBH_INDEX1_APICID, 2, LEVEL_TYPE_CORE); - } -} diff --git a/src/cpuid/src/transformer/mod.rs b/src/cpuid/src/transformer/mod.rs deleted file mode 100644 index aea30d14719..00000000000 --- a/src/cpuid/src/transformer/mod.rs +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -pub mod amd; -pub mod common; -pub mod intel; - -pub use kvm_bindings::{kvm_cpuid_entry2, CpuId}; - -use crate::brand_string::{BrandString, Reg as BsReg}; -use crate::common::get_vendor_id_from_host; - -/// Structure containing the specifications of the VM -pub struct VmSpec { - /// The vendor id of the CPU - cpu_vendor_id: [u8; 12], - /// The desired brand string for the guest. - brand_string: BrandString, - - /// The index of the current logical cpu in the range [0..cpu_count]. - cpu_index: u8, - /// The total number of logical cpus. - cpu_count: u8, - - /// The number of bits needed to enumerate logical CPUs per core. - cpu_bits: u8, -} - -impl VmSpec { - /// Creates a new instance of VmSpec with the specified parameters - /// The brand string is deduced from the vendor_id - pub fn new(cpu_index: u8, cpu_count: u8, smt: bool) -> Result { - let cpu_vendor_id = get_vendor_id_from_host()?; - - Ok(VmSpec { - cpu_vendor_id, - cpu_index, - cpu_count, - cpu_bits: u8::from(cpu_count > 1 && smt), - brand_string: BrandString::from_vendor_id(&cpu_vendor_id), - }) - } - - /// Returns an immutable reference to cpu_vendor_id - pub fn cpu_vendor_id(&self) -> &[u8; 12] { - &self.cpu_vendor_id - } - - /// Returns the number of cpus per core - pub fn cpus_per_core(&self) -> u8 { - 1 << self.cpu_bits - } -} - -/// Errors associated with processing the CPUID leaves. -#[derive(Debug, Clone, thiserror::Error)] -pub enum Error { - /// A FamStructWrapper operation has failed - #[error("A FamStructWrapper operation has failed.")] - Fam(utils::fam::Error), - /// A call to an internal helper method failed - #[error("A call to an internal helper method failed: {0}")] - InternalError(#[from] super::common::Error), - /// The operation is not permitted for the current vendor - #[error("The operation is not permitted for the current vendor.")] - InvalidVendor, - /// The maximum number of addressable logical CPUs cannot be stored in an `u8`. - #[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>; - -/// Generic trait that provides methods for transforming the cpuid -pub trait CpuidTransformer { - /// Trait main function. It processes the cpuid and makes the desired transformations. - /// The default logic can be overwritten if needed. For example see `AmdCpuidTransformer`. - fn process_cpuid(&self, cpuid: &mut CpuId, vm_spec: &VmSpec) -> Result<(), Error> { - self.process_entries(cpuid, vm_spec) - } - - /// Iterates through all the cpuid entries and calls the associated transformer for each one. - fn process_entries(&self, cpuid: &mut CpuId, vm_spec: &VmSpec) -> Result<(), Error> { - for entry in cpuid.as_mut_slice().iter_mut() { - let maybe_transformer_fn = self.entry_transformer_fn(entry); - - if let Some(transformer_fn) = maybe_transformer_fn { - transformer_fn(entry, vm_spec)?; - } - } - - Ok(()) - } - - /// Gets the associated transformer for a cpuid entry - fn entry_transformer_fn(&self, _entry: &mut kvm_cpuid_entry2) -> Option { - None - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_vmspec() { - let vm_spec = VmSpec::new(0, 1, true).unwrap(); - assert_eq!(vm_spec.cpu_bits, 0); - assert_eq!(vm_spec.cpus_per_core(), 1); - - let vm_spec = VmSpec::new(0, 1, false).unwrap(); - assert_eq!(vm_spec.cpu_bits, 0); - assert_eq!(vm_spec.cpus_per_core(), 1); - - let vm_spec = VmSpec::new(0, 2, false).unwrap(); - assert_eq!(vm_spec.cpu_bits, 0); - assert_eq!(vm_spec.cpus_per_core(), 1); - - let vm_spec = VmSpec::new(0, 2, true).unwrap(); - assert_eq!(vm_spec.cpu_bits, 1); - assert_eq!(vm_spec.cpus_per_core(), 2); - } - - const PROCESSED_FN: u32 = 1; - const EXPECTED_INDEX: u32 = 100; - - fn transform_entry(entry: &mut kvm_cpuid_entry2, _vm_spec: &VmSpec) -> Result<(), Error> { - entry.index = EXPECTED_INDEX; - - Ok(()) - } - - struct MockCpuidTransformer {} - - impl CpuidTransformer for MockCpuidTransformer { - fn entry_transformer_fn(&self, entry: &mut kvm_cpuid_entry2) -> Option { - match entry.function { - PROCESSED_FN => Some(transform_entry), - _ => None, - } - } - } - - #[test] - fn test_process_cpuid() { - let num_entries = 5; - - let mut cpuid = CpuId::new(num_entries).unwrap(); - let vm_spec = VmSpec::new(0, 1, false); - cpuid.as_mut_slice()[0].function = PROCESSED_FN; - assert!(MockCpuidTransformer {} - .process_cpuid(&mut cpuid, &vm_spec.unwrap()) - .is_ok()); - - assert!(cpuid.as_mut_slice().len() == num_entries); - for entry in cpuid.as_mut_slice().iter() { - match entry.function { - PROCESSED_FN => { - assert_eq!(entry.index, EXPECTED_INDEX); - } - _ => { - assert_ne!(entry.index, EXPECTED_INDEX); - } - } - } - } -} diff --git a/src/logger/src/metrics.rs b/src/logger/src/metrics.rs index 891d50971be..32caf16a741 100644 --- a/src/logger/src/metrics.rs +++ b/src/logger/src/metrics.rs @@ -731,8 +731,6 @@ pub struct VcpuMetrics { pub exit_mmio_write: SharedIncMetric, /// Number of errors during this VCPU's run. pub failures: SharedIncMetric, - /// Failures in configuring the CPUID. - pub filter_cpuid: SharedIncMetric, } /// Metrics specific to the machine manager as a whole. diff --git a/src/vmm/Cargo.toml b/src/vmm/Cargo.toml index d70b74ca0ff..d356778e06c 100644 --- a/src/vmm/Cargo.toml +++ b/src/vmm/Cargo.toml @@ -35,6 +35,7 @@ vm-memory = { path = "../vm-memory" } [target.'cfg(target_arch = "x86_64")'.dependencies] cpuid = { path = "../cpuid" } +cpuid-templates = { path = "../cpuid-templates" } [dev-dependencies] criterion = "0.4.0" diff --git a/src/vmm/src/builder.rs b/src/vmm/src/builder.rs index ea0e9862dcf..8d42ed67b7c 100644 --- a/src/vmm/src/builder.rs +++ b/src/vmm/src/builder.rs @@ -3,6 +3,7 @@ //! Enables pre-boot setup, instantiation and booting of a Firecracker VMM. +#[cfg(target_arch = "x86_64")] use std::convert::TryFrom; use std::fmt::{Display, Formatter}; use std::io::{self, Read, Seek, SeekFrom}; @@ -10,8 +11,6 @@ use std::os::unix::io::{AsRawFd, RawFd}; use std::sync::{Arc, Mutex}; use arch::InitrdConfig; -#[cfg(target_arch = "x86_64")] -use cpuid::common::is_same_model; #[cfg(target_arch = "aarch64")] use devices::legacy::RTCDevice; use devices::legacy::{ @@ -50,9 +49,9 @@ use crate::vmm_config::boot_source::BootConfig; use crate::vmm_config::instance_info::InstanceInfo; use crate::vmm_config::machine_config::{VmConfigError, VmUpdateConfig}; use crate::vstate::system::KvmContext; -use crate::vstate::vcpu::{Vcpu, VcpuConfig}; +use crate::vstate::vcpu::Vcpu; use crate::vstate::vm::Vm; -use crate::{device_manager, Error, EventManager, Vmm, VmmEventsObserver}; +use crate::{device_manager, Error, EventManager, RestoreVcpusError, Vmm, VmmEventsObserver}; /// Errors associated with starting the instance. #[derive(Debug)] @@ -433,13 +432,6 @@ pub enum BuildMicrovmFromSnapshotError { /// Could not access KVM. #[error("Could not access KVM: {0}")] KvmAccess(#[from] utils::errno::Error), - /// The CPUID specification from the snapshot contains features unsupported by and/or fields - /// outside the supported range, of the KVM. - #[error( - "The CPUID specification from the snapshot contains features unsupported by and/or fields \ - outside the supported range, of the KVM" - )] - UnsupportedCPUID, /// Error configuring the TSC, frequency not present in the given snapshot. #[error("Error configuring the TSC, frequency not present in the given snapshot.")] TscFrequencyNotPresent, @@ -471,7 +463,7 @@ pub enum BuildMicrovmFromSnapshotError { StartVcpus(#[from] crate::StartVcpusError), /// Failed to restore vCPUs. #[error("Failed to restore vCPUs: {0}")] - RestoreVcpus(#[from] crate::RestoreVcpusError), + RestoreVcpus(#[from] RestoreVcpusError), /// Failed to apply VMM secccomp filter as none found. #[error("Failed to apply VMM secccomp filter as none found.")] MissingVmmSeccompFilters, @@ -510,21 +502,15 @@ pub fn build_microvm_from_snapshot( )?; #[cfg(target_arch = "x86_64")] - // Check if we need to scale the TSC. - // We start by checking if the CPU model in the snapshot is - // the same as this host's. If they are the same, we don't - // need to do anything else. - if !is_same_model(µvm_state.vcpu_states[0].cpuid) { - // Extract the TSC freq from the state. - // No TSC freq in snapshot means we have to fail-fast. - let state_tsc = microvm_state.vcpu_states[0] - .tsc_khz - .ok_or(BuildMicrovmFromSnapshotError::TscFrequencyNotPresent)?; - - // Scale the TSC frequency for all VCPUs, if needed. - if vcpus[0].kvm_vcpu.is_tsc_scaling_required(state_tsc)? { - for vcpu in &vcpus { - vcpu.kvm_vcpu.set_tsc_khz(state_tsc)?; + { + // Scale TSC to match, extract the TSC freq from the state if specified + if let Some(state_tsc) = microvm_state.vcpu_states[0].tsc_khz { + // Scale the TSC frequency for all VCPUs. If a TSC frequency is not specified in the + // snapshot, by default it uses the host frequency. + if vcpus[0].kvm_vcpu.is_tsc_scaling_required(state_tsc)? { + for vcpu in &vcpus { + vcpu.kvm_vcpu.set_tsc_khz(state_tsc)?; + } } } } @@ -833,7 +819,7 @@ fn create_vcpus(vm: &Vm, vcpu_count: u8, exit_evt: &EventFd) -> super::Result, boot_cmdline: LoaderKernelCmdline, diff --git a/src/vmm/src/persist.rs b/src/vmm/src/persist.rs index 5d12b9089c5..4c2f872e3ff 100644 --- a/src/vmm/src/persist.rs +++ b/src/vmm/src/persist.rs @@ -13,7 +13,7 @@ use std::sync::{Arc, Mutex}; #[cfg(target_arch = "aarch64")] use arch::regs::{get_manufacturer_id_from_host, get_manufacturer_id_from_state}; #[cfg(target_arch = "x86_64")] -use cpuid::common::{get_vendor_id_from_cpuid, get_vendor_id_from_host}; +use cpuid::CpuidTrait; use devices::virtio::TYPE_NET; use logger::{error, info, warn}; use seccompiler::BpfThreadMap; @@ -347,10 +347,10 @@ pub fn get_snapshot_data_version( pub enum ValidateCpuVendorError { /// Failed to read host vendor. #[error("Failed to read host vendor: {0}")] - Host(cpuid::common::Error), + Host(#[from] cpuid::common::GetCpuidError), /// Failed to read snapshot vendor. #[error("Failed to read snapshot vendor: {0}")] - Snapshot(cpuid::common::Error), + Snapshot(#[from] cpuid::common::Leaf0NotFoundInCpuid), } /// Validates that snapshot CPU vendor matches the host CPU vendor. @@ -364,12 +364,15 @@ pub enum ValidateCpuVendorError { pub fn validate_cpu_vendor( microvm_state: &MicrovmState, ) -> std::result::Result { - let host_vendor_id = get_vendor_id_from_host().map_err(ValidateCpuVendorError::Host)?; + let host_vendor_id = cpuid::common::get_vendor_id_from_host()?; - let snapshot_vendor_id = get_vendor_id_from_cpuid(µvm_state.vcpu_states[0].cpuid) + let snapshot_vendor_id = microvm_state.vcpu_states[0] + .cpuid + .manufacturer_id() + .ok_or(cpuid::common::Leaf0NotFoundInCpuid) .map_err(|err| { error!("Snapshot CPU vendor is missing."); - ValidateCpuVendorError::Snapshot(err) + err })?; if host_vendor_id == snapshot_vendor_id { diff --git a/src/vmm/src/vmm_config/machine_config.rs b/src/vmm/src/vmm_config/machine_config.rs index 4a63b7a59ea..c5783095a7f 100644 --- a/src/vmm/src/vmm_config/machine_config.rs +++ b/src/vmm/src/vmm_config/machine_config.rs @@ -280,9 +280,9 @@ mod tests { #[test] fn test_display_cpu_features_template() { - assert_eq!(CpuFeaturesTemplate::C3.to_string(), "C3".to_string()); - assert_eq!(CpuFeaturesTemplate::T2.to_string(), "T2".to_string()); - assert_eq!(CpuFeaturesTemplate::T2S.to_string(), "T2S".to_string()); + assert_eq!(CpuFeaturesTemplate::C3.to_string(), String::from("C3")); + assert_eq!(CpuFeaturesTemplate::T2.to_string(), String::from("T2")); + assert_eq!(CpuFeaturesTemplate::T2S.to_string(), String::from("T2S")); } #[test] diff --git a/src/vmm/src/vstate/vcpu/x86_64.rs b/src/vmm/src/vstate/vcpu/x86_64.rs index 119f8d23e07..7d7ed6ce17c 100644 --- a/src/vmm/src/vstate/vcpu/x86_64.rs +++ b/src/vmm/src/vstate/vcpu/x86_64.rs @@ -5,17 +5,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the THIRD-PARTY file. -use std::collections::HashSet; -use std::fmt::{Display, Formatter}; -use std::{fmt, result}; +use std::convert::TryFrom; use arch::x86_64::interrupts; -use arch::x86_64::msr::SetMSRsError; +use arch::x86_64::msr::{ArchCapaMSRFlags, SetMSRsError, MSR_IA32_ARCH_CAPABILITIES}; use arch::x86_64::regs::{SetupFpuError, SetupRegistersError, SetupSpecialRegistersError}; -use cpuid::{c3, filter_cpuid, msrs_to_save_by_cpuid, t2, t2s, VmSpec}; use kvm_bindings::{ - kvm_debugregs, kvm_lapic_state, kvm_mp_state, kvm_regs, kvm_sregs, kvm_vcpu_events, kvm_xcrs, - kvm_xsave, CpuId, Msrs, KVM_MAX_MSR_ENTRIES, + kvm_debugregs, kvm_lapic_state, kvm_mp_state, kvm_msr_entry, kvm_regs, kvm_sregs, + kvm_vcpu_events, kvm_xcrs, kvm_xsave, CpuId, Msrs, KVM_MAX_MSR_ENTRIES, }; use kvm_ioctls::{VcpuExit, VcpuFd}; use logger::{error, warn, IncMetric, METRICS}; @@ -33,11 +30,34 @@ use crate::vstate::vm::Vm; // https://bugzilla.redhat.com/show_bug.cgi?id=1839095 const TSC_KHZ_TOL: f64 = 250.0 / 1_000_000.0; +/// Add the MSR entries specific to this T2S template. +#[inline] +pub fn update_t2s_msr_entries(msr_entries: &mut Vec) { + let capabilities = ArchCapaMSRFlags::RSBA + | ArchCapaMSRFlags::SKIP_L1DFL_VMENTRY + | ArchCapaMSRFlags::IF_PSCHANGE_MC_NO + | ArchCapaMSRFlags::MISC_PACKAGE_CTRLS + | ArchCapaMSRFlags::ENERGY_FILTERING_CTL; + msr_entries.push(kvm_msr_entry { + index: MSR_IA32_ARCH_CAPABILITIES, + data: capabilities.bits(), + ..kvm_msr_entry::default() + }); +} + +#[allow(clippy::missing_docs_in_private_items)] +static EXTRA_MSR_ENTRIES: &[u32] = &[MSR_IA32_ARCH_CAPABILITIES]; + +/// Return a list of MSRs specific to this T2S template. +#[inline] +#[must_use] +pub fn msr_entries_to_save() -> &'static [u32] { + EXTRA_MSR_ENTRIES +} + /// Errors associated with the wrappers over KVM ioctls. #[derive(Debug)] pub enum Error { - /// A call to cpuid instruction failed. - CpuId(cpuid::Error), /// A FamStructWrapper operation has failed. Fam(utils::fam::Error), /// Error configuring the floating point related registers @@ -104,12 +124,11 @@ pub enum Error { VcpuTemplateError, } -impl Display for Error { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { use self::Error::*; match self { - CpuId(err) => write!(f, "Cpuid error: {:?}", err), LocalIntConfiguration(err) => write!( f, "Cannot set the local interruption due to bad configuration: {:?}", @@ -163,40 +182,31 @@ impl Display for Error { } } -type Result = result::Result; +type Result = std::result::Result; /// Error type for [`KvmVcpu::get_tsc_khz`] and [`KvmVcpu::is_tsc_scaling_required`]. -#[derive(Debug, derive_more::From, PartialEq, Eq)] +#[derive(Debug, thiserror::Error, derive_more::From, Eq, PartialEq)] +#[error("{0}")] pub struct GetTscError(utils::errno::Error); -impl fmt::Display for GetTscError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0) - } -} -impl std::error::Error for GetTscError {} + /// Error type for [`KvmVcpu::set_tsc_khz`]. -#[derive(Debug, derive_more::From, PartialEq, Eq)] -pub struct SetTscError(kvm_ioctls::Error); -impl fmt::Display for SetTscError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0) - } -} -impl std::error::Error for SetTscError {} +#[derive(Debug, thiserror::Error, Eq, PartialEq)] +#[error("{0}")] +pub struct SetTscError(#[from] kvm_ioctls::Error); /// Error type for [`KvmVcpu::configure`]. -#[derive(Debug, thiserror::Error)] +#[derive(Debug, thiserror::Error, Eq, PartialEq)] pub enum KvmVcpuConfigureError { - #[error("Failed to create `VmSpec`: {0}")] - VmSpec(cpuid::Error), - #[error("Failed to filter CPUID: {0}")] - FilterCpuid(cpuid::Error), - #[error("Failed to set CPUID entries: {0}")] - SetCpuidEntries(cpuid::Error), + /// Failed to construct `cpuid::Cpuid` from snapshot. + #[error("Failed to construct `cpuid::RawCpuid` from `kvm_bindings::CpuId`")] + SnapshotCpuid(cpuid::CpuidTryFromRawCpuid), + /// Failed to apply modifications to CPUID. + #[error("Failed to apply modifications to CPUID: {0}")] + NormalizeCpuidError(cpuid::NormalizeCpuidError), #[error("Failed to set CPUID: {0}")] SetCpuid(#[from] utils::errno::Error), - #[error("Failed to push MSR entry to FamStructWrapper.")] - PushMsrEntries(utils::fam::Error), + #[error("Failed to get MSRs to save from CPUID: {0}")] + MsrsToSaveByCpuid(cpuid::common::Leaf0NotFoundInCpuid), #[error("Failed to set MSRs: {0}")] SetMsrs(#[from] SetMSRsError), #[error("Failed to setup registers: {0}")] @@ -205,7 +215,7 @@ pub enum KvmVcpuConfigureError { SetupFpu(#[from] SetupFpuError), #[error("Failed to setup special registers: {0}")] SetupSpecialRegisters(#[from] SetupSpecialRegistersError), - #[error("Faiuled to configure LAPICs: {0}")] + #[error("Failed to configure LAPICs: {0}")] SetLint(#[from] interrupts::Error), } @@ -217,7 +227,7 @@ pub struct KvmVcpu { pub pio_bus: Option, pub mmio_bus: Option, - msr_list: HashSet, + msr_list: std::collections::HashSet, } impl KvmVcpu { @@ -252,35 +262,36 @@ impl KvmVcpu { guest_mem: &GuestMemoryMmap, kernel_start_addr: GuestAddress, vcpu_config: &VcpuConfig, - mut cpuid: CpuId, + cpuid: CpuId, ) -> std::result::Result<(), KvmVcpuConfigureError> { - let cpuid_vm_spec = VmSpec::new(self.index, vcpu_config.vcpu_count, vcpu_config.smt) - .map_err(KvmVcpuConfigureError::VmSpec)?; - - filter_cpuid(&mut cpuid, &cpuid_vm_spec) - .map_err(|err| { - METRICS.vcpu.filter_cpuid.inc(); - error!( - "Failure in configuring CPUID for vcpu {}: {:?}", - self.index, err - ); - err - }) - .map_err(KvmVcpuConfigureError::FilterCpuid)?; - - // Update the CPUID based on the template - match vcpu_config.cpu_template { - CpuFeaturesTemplate::T2 => t2::set_cpuid_entries(&mut cpuid, &cpuid_vm_spec) - .map_err(KvmVcpuConfigureError::SetCpuidEntries)?, - CpuFeaturesTemplate::T2S => t2s::set_cpuid_entries(&mut cpuid, &cpuid_vm_spec) - .map_err(KvmVcpuConfigureError::SetCpuidEntries)?, - CpuFeaturesTemplate::C3 => c3::set_cpuid_entries(&mut cpuid, &cpuid_vm_spec) - .map_err(KvmVcpuConfigureError::SetCpuidEntries)?, - CpuFeaturesTemplate::None => {} - } + // If a template is specified, get the CPUID template, else use `cpuid`. + let mut config_cpuid = match vcpu_config.cpu_template { + CpuFeaturesTemplate::T2 => cpuid_templates::t2(), + CpuFeaturesTemplate::T2S => cpuid_templates::t2s(), + CpuFeaturesTemplate::C3 => cpuid_templates::c3(), + // If a template is not supplied we use the given `cpuid` as the base. + CpuFeaturesTemplate::None => cpuid::Cpuid::try_from(cpuid::RawCpuid::from(cpuid)) + .map_err(KvmVcpuConfigureError::SnapshotCpuid)?, + }; + + // Apply machine specific changes to CPUID. + config_cpuid + .normalize( + // The index of the current logical CPU in the range [0..cpu_count]. + self.index, + // The total number of logical CPUs. + vcpu_config.vcpu_count, + // The number of bits needed to enumerate logical CPUs per core. + u8::from(vcpu_config.vcpu_count > 1 && vcpu_config.smt), + ) + .map_err(KvmVcpuConfigureError::NormalizeCpuidError)?; + // Set CPUID. + let kvm_cpuid = kvm_bindings::CpuId::from(config_cpuid); + + // Set CPUID in the KVM self.fd - .set_cpuid2(&cpuid) + .set_cpuid2(&kvm_cpuid) .map_err(KvmVcpuConfigureError::SetCpuid)?; // Initialize some architectural MSRs that will be set for boot. @@ -293,8 +304,8 @@ impl KvmVcpu { // value when we restore the microVM since the Guest may need that value. // Since CPUID tells us what features are enabled for the Guest, we can infer // the extra MSRs that we need to save based on a dependency map. - let extra_msrs = - msrs_to_save_by_cpuid(&cpuid).map_err(KvmVcpuConfigureError::FilterCpuid)?; + let extra_msrs = cpuid::common::msrs_to_save_by_cpuid(&kvm_cpuid) + .map_err(KvmVcpuConfigureError::MsrsToSaveByCpuid)?; self.msr_list.extend(extra_msrs); // TODO: Some MSRs depend on values of other MSRs. This dependency will need to @@ -307,8 +318,8 @@ impl KvmVcpu { // to save at snapshot as well. // C3 and T2 currently don't have extra MSRs to save/set if vcpu_config.cpu_template == CpuFeaturesTemplate::T2S { - self.msr_list.extend(t2s::msr_entries_to_save()); - t2s::update_msr_entries(&mut msr_boot_entries); + self.msr_list.extend(msr_entries_to_save()); + update_t2s_msr_entries(&mut msr_boot_entries); } // By this point we know that at snapshot, the list of MSRs we need to // save is `architectural MSRs` + `MSRs inferred through CPUID` + `other @@ -319,6 +330,7 @@ impl KvmVcpu { arch::x86_64::regs::setup_fpu(&self.fd)?; arch::x86_64::regs::setup_sregs(guest_mem, &self.fd)?; arch::x86_64::interrupts::set_lint(&self.fd)?; + Ok(()) } @@ -619,7 +631,8 @@ mod tests { use std::os::unix::io::AsRawFd; - use cpuid::common::{get_vendor_id_from_host, VENDOR_ID_INTEL}; + use cpuid::common::get_vendor_id_from_host; + use cpuid::VENDOR_ID_INTEL; use kvm_ioctls::Cap; use super::*; @@ -662,14 +675,15 @@ mod tests { cpu_template: CpuFeaturesTemplate::None, }; - assert!(vcpu - .configure( + assert_eq!( + vcpu.configure( &vm_mem, GuestAddress(0), &vcpu_config, vm.supported_cpuid().clone() - ) - .is_ok()); + ), + Ok(()) + ); // Test configure while using the T2 template. vcpu_config.cpu_template = CpuFeaturesTemplate::T2; @@ -700,9 +714,9 @@ mod tests { match &get_vendor_id_from_host().unwrap() { VENDOR_ID_INTEL => { - assert!(t2_res.is_ok()); - assert!(c3_res.is_ok()); - assert!(t2s_res.is_ok()); + assert_eq!(t2_res, Ok(())); + assert_eq!(c3_res, Ok(())); + assert_eq!(t2s_res, Ok(())); } _ => { assert!(t2_res.is_err()); diff --git a/src/vmm/tests/integration_tests.rs b/src/vmm/tests/integration_tests.rs index a081d1a4b03..662d0a22687 100644 --- a/src/vmm/tests/integration_tests.rs +++ b/src/vmm/tests/integration_tests.rs @@ -378,7 +378,7 @@ fn test_snapshot_cpu_vendor_mismatch() { assert_eq!( validate_cpu_vendor(µvm_state), Err(vmm::persist::ValidateCpuVendorError::Snapshot( - cpuid::common::Error::NotSupported + cpuid::common::Leaf0NotFoundInCpuid )) ); } diff --git a/tests/framework/dependencies.txt b/tests/framework/dependencies.txt index b644f6efb77..825249821d2 100644 --- a/tests/framework/dependencies.txt +++ b/tests/framework/dependencies.txt @@ -20,6 +20,7 @@ 'convert_case v0.6.0', 'cpufeatures v0.2.5', 'cpuid v0.1.0 (/firecracker/src/cpuid)', + 'cpuid-templates v0.1.0 (/firecracker/src/cpuid-templates)', 'crc64 v1.0.0', 'crypto-common v0.1.6', 'ctr v0.9.2', @@ -33,7 +34,7 @@ 'ghash v0.5.0', 'glob v0.3.0', 'inout v0.1.3', - 'io-lifetimes v0.6.1', + 'io-lifetimes v1.0.3', 'io_uring v0.1.0 (/firecracker/src/io_uring)', 'itoa v1.0.4', 'jailer v1.2.0 (/firecracker/src/jailer)', @@ -42,10 +43,10 @@ 'kvm-ioctls v0.12.0', 'lazy_static v1.4.0', 'lazycell v1.3.0', - 'libc v0.2.137', + 'libc v0.2.138', 'libloading v0.7.4', 'linux-loader v0.8.0', - 'linux-raw-sys v0.0.46', + 'linux-raw-sys v0.1.3', 'log v0.4.17', 'logger v0.1.0 (/firecracker/src/logger)', 'memchr v2.5.0', @@ -55,12 +56,12 @@ 'minimal-lexical v0.2.1', 'mmds v0.1.0 (/firecracker/src/mmds)', 'net_gen v0.1.0 (/firecracker/src/net_gen)', - 'nix v0.23.1', + 'nix v0.23.2', 'nom v7.1.1', 'opaque-debug v0.3.0', 'peeking_take_while v0.1.2', 'polyval v0.6.0', - 'proc-macro2 v1.0.43', + 'proc-macro2 v1.0.47', 'quote v1.0.21', 'rand_core v0.6.4', 'rate_limiter v0.1.0 (/firecracker/src/rate_limiter)', @@ -68,21 +69,21 @@ 'regex v1.7.0', 'regex-syntax v0.6.28', 'rustc-hash v1.1.0', - 'rustix v0.34.8', + 'rustix v0.36.5', 'ryu v1.0.11', 'seccompiler v1.2.0 (/firecracker/src/seccompiler)', - 'serde v1.0.144', - 'serde_derive v1.0.144 (proc-macro)', - 'serde_json v1.0.85', + 'serde v1.0.149', + 'serde_derive v1.0.149 (proc-macro)', + 'serde_json v1.0.91', 'shlex v1.1.0', 'snapshot v0.1.0 (/firecracker/src/snapshot)', 'subtle v2.4.1', - 'syn v1.0.99', + 'syn v1.0.105', 'thiserror v1.0.37', 'thiserror-impl v1.0.37 (proc-macro)', - 'timerfd v1.3.0', - 'typenum v1.15.0', - 'unicode-ident v1.0.3', + 'timerfd v1.4.0', + 'typenum v1.16.0', + 'unicode-ident v1.0.5', 'unicode-segmentation v1.10.0', 'universal-hash v0.5.0', 'userfaultfd v0.5.0', diff --git a/tests/integration_tests/build/test_coverage.py b/tests/integration_tests/build/test_coverage.py index 03c87975d1c..7e3a297cd5c 100644 --- a/tests/integration_tests/build/test_coverage.py +++ b/tests/integration_tests/build/test_coverage.py @@ -18,9 +18,9 @@ # Checkout the cpuid crate. In the future other # differences may appear. if utils.is_io_uring_supported(): - COVERAGE_DICT = {"Intel": 82.99, "AMD": 82.31, "ARM": 82.51} + COVERAGE_DICT = {"Intel": 77.00, "AMD": 75.44, "ARM": 67.84} else: - COVERAGE_DICT = {"Intel": 80.15, "AMD": 79.48, "ARM": 79.59} + COVERAGE_DICT = {"Intel": 74.43, "AMD": 72.17, "ARM": 64.66} PROC_MODEL = proc.proc_type() diff --git a/tests/integration_tests/functional/test_api.py b/tests/integration_tests/functional/test_api.py index 49354ab0803..38e4d0e533c 100644 --- a/tests/integration_tests/functional/test_api.py +++ b/tests/integration_tests/functional/test_api.py @@ -490,10 +490,7 @@ def test_api_machine_config(test_microvm_with_api): response = test_microvm.actions.put(action_type="InstanceStart") if utils.get_cpu_vendor() == utils.CpuVendor.AMD: # We shouldn't be able to apply Intel templates on AMD hosts - fail_msg = ( - "Internal error while starting microVM: Error configuring the vcpu for boot: Failed to " - "set CPUID entries: The operation is not permitted for the current vendor." - ) + fail_msg = "Internal error while starting microVM: Error configuring the vcpu for boot:" assert test_microvm.api_session.is_status_bad_request(response.status_code) assert fail_msg in response.text else: diff --git a/tests/integration_tests/functional/test_cpu_features.py b/tests/integration_tests/functional/test_cpu_features.py index 06171b563e3..ff1f97c5c5a 100644 --- a/tests/integration_tests/functional/test_cpu_features.py +++ b/tests/integration_tests/functional/test_cpu_features.py @@ -116,18 +116,6 @@ def test_brand_string(test_microvm_with_api, network_config): @type: functional """ - cif = open("/proc/cpuinfo", "r", encoding="utf-8") - host_brand_string = None - while True: - line = cif.readline() - if line == "": - break - mo = re.search("^model name\\s+:\\s+(.+)$", line) - if mo: - host_brand_string = mo.group(1) - cif.close() - assert host_brand_string is not None - test_microvm = test_microvm_with_api test_microvm.spawn() @@ -137,27 +125,37 @@ def test_brand_string(test_microvm_with_api, network_config): ssh_connection = net_tools.SSHConnection(test_microvm.ssh_config) - guest_cmd = "cat /proc/cpuinfo | grep 'model name' | head -1" + guest_cmd = "cat /proc/cpuinfo" _, stdout, stderr = ssh_connection.execute_command(guest_cmd) assert stderr.read() == "" - - line = stdout.readline().rstrip() - mo = re.search("^model name\\s+:\\s+(.+)$", line) - assert mo - guest_brand_string = mo.group(1) - assert guest_brand_string + stdout = stdout.read() cpu_vendor = cpuid_utils.get_cpu_vendor() - expected_guest_brand_string = "" if cpu_vendor == cpuid_utils.CpuVendor.AMD: - expected_guest_brand_string += "AMD EPYC" + # Assert the model name matches "AMD EPYC" + mo = re.search("model name.*: AMD EPYC", stdout) + assert mo elif cpu_vendor == cpuid_utils.CpuVendor.INTEL: - expected_guest_brand_string = "Intel(R) Xeon(R) Processor" - mo = re.search("[.0-9]+[MG]Hz", host_brand_string) - if mo: - expected_guest_brand_string += " @ " + mo.group(0) + # Get host frequency + cif = open("/proc/cpuinfo", "r", encoding="utf-8") + cpu_info = cif.read() + mo = re.search("model name.*:.* ([0-9]*.[0-9]*[G|M|T]Hz)", cpu_info) + assert mo + host_frequency = mo.group(1) + + # Assert the model name matches "Intel(R) Xeon(R) Processor @ " + mo = re.search( + "model name.*: Intel\\(R\\) Xeon\\(R\\) Processor @ ([0-9]*.[0-9]*[T|G|M]Hz)", + stdout, + ) + assert mo + # Get the frequency + guest_frequency = mo.group(1) - assert guest_brand_string == expected_guest_brand_string + # Assert the guest frequency matches the host frequency + assert host_frequency == guest_frequency + else: + assert False # Some MSR values should not be checked since they can change at Guest runtime.