diff --git a/Guide/src/user_guide/openvmm/snapshots.md b/Guide/src/user_guide/openvmm/snapshots.md index 96880f5e7ec..ef5678a2736 100644 --- a/Guide/src/user_guide/openvmm/snapshots.md +++ b/Guide/src/user_guide/openvmm/snapshots.md @@ -320,10 +320,14 @@ generation: family 6, model 85, steppings 0 to 4), `intel.icelake-sp.v1` (Xeon Scalable, third generation: 6/106), `intel.emeraldrapids.v1` (Xeon Scalable, fifth generation: 6/207), `intel.alderlake.v1` (Core, twelfth generation: 6/151 and 6/154), `amd.milan.v1` (AMD EPYC, third generation: -25/1), `amd.genoa.v1` (AMD EPYC, fourth generation: 25/17), and -`amd.turin.v1` (AMD EPYC, fifth generation: 26/2). `--cpu-profile ` -selects one; the default, `auto`, selects the profile of the host's -generation. An Intel or AMD host without one boots on a host profile +25/1), `amd.genoa.v1` and `amd.genoa.v2` (AMD EPYC, fourth generation: +25/17), and `amd.turin.v1` (AMD EPYC, fifth generation: 26/2). +`amd.genoa.v2` drops what `amd.genoa.v1` presents in `0x80000021`, `LFENCE` +serialization and the TSA immunities, which not every Genoa host's +hypervisor presents. `--cpu-profile ` selects one; the default, +`auto`, selects the latest revision of the host's generation's profile. An +earlier revision stays pinned, so its snapshots still restore. An Intel or +AMD host that no pinned profile serves boots on a host profile instead: `auto` falls back to the profile that `--cpu-profile host` selects, for which OpenVMM fingerprints the backend on this host, as `--cpu-fingerprint` does, and applies the pinned profiles' derivation policy diff --git a/vmm_core/cpu_profile/profiles/amd.genoa.v2.json b/vmm_core/cpu_profile/profiles/amd.genoa.v2.json new file mode 100644 index 00000000000..af1a37d47e7 --- /dev/null +++ b/vmm_core/cpu_profile/profiles/amd.genoa.v2.json @@ -0,0 +1,902 @@ +{ + "cpuid": [ + { + "leaf": "0x00000000", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": null, + "value": [ + "0x0000000d", + "0x68747541", + "0x444d4163", + "0x69746e65" + ] + }, + { + "leaf": "0x00000001", + "mask": [ + "0xffffffff", + "0x0000ffff", + "0xf7dfffff", + "0xffffffff" + ], + "subleaf": null, + "value": [ + "0x00a10f11", + "0x00000800", + "0xf6da3203", + "0x078bfbff" + ] + }, + { + "leaf": "0x00000002", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": null, + "value": [ + "0x00000000", + "0x00000000", + "0x00000000", + "0x00000000" + ] + }, + { + "leaf": "0x00000003", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": null, + "value": [ + "0x00000000", + "0x00000000", + "0x00000000", + "0x00000000" + ] + }, + { + "leaf": "0x00000004", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": "0x00000000", + "value": [ + "0x00000000", + "0x00000000", + "0x00000000", + "0x00000000" + ] + }, + { + "leaf": "0x00000005", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": null, + "value": [ + "0x00000000", + "0x00000000", + "0x00000000", + "0x00000000" + ] + }, + { + "leaf": "0x00000006", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": null, + "value": [ + "0x00000004", + "0x00000000", + "0x00000000", + "0x00000000" + ] + }, + { + "leaf": "0x00000007", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffef", + "0xffffffff" + ], + "subleaf": "0x00000000", + "value": [ + "0x00000000", + "0x219c07a9", + "0x00400604", + "0x00000010" + ] + }, + { + "leaf": "0x00000008", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": null, + "value": [ + "0x00000000", + "0x00000000", + "0x00000000", + "0x00000000" + ] + }, + { + "leaf": "0x00000009", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": null, + "value": [ + "0x00000000", + "0x00000000", + "0x00000000", + "0x00000000" + ] + }, + { + "leaf": "0x0000000a", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": null, + "value": [ + "0x00000000", + "0x00000000", + "0x00000000", + "0x00000000" + ] + }, + { + "leaf": "0x0000000c", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": null, + "value": [ + "0x00000000", + "0x00000000", + "0x00000000", + "0x00000000" + ] + }, + { + "leaf": "0x0000000d", + "mask": [ + "0xffffffff", + "0x00000000", + "0xffffffff", + "0xffffffff" + ], + "subleaf": "0x00000000", + "value": [ + "0x00000007", + "0x00000000", + "0x00000340", + "0x00000000" + ] + }, + { + "leaf": "0x0000000d", + "mask": [ + "0xffffffff", + "0x00000000", + "0xffffffff", + "0xffffffff" + ], + "subleaf": "0x00000001", + "value": [ + "0x0000000f", + "0x00000000", + "0x00000000", + "0x00000000" + ] + }, + { + "leaf": "0x0000000d", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": "0x00000002", + "value": [ + "0x00000100", + "0x00000240", + "0x00000000", + "0x00000000" + ] + }, + { + "leaf": "0x80000000", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": null, + "value": [ + "0x80000021", + "0x68747541", + "0x444d4163", + "0x69746e65" + ] + }, + { + "leaf": "0x80000001", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": null, + "value": [ + "0x00a10f11", + "0x00000000", + "0x004002f3", + "0x2fd3fbff" + ] + }, + { + "leaf": "0x80000002", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": null, + "value": [ + "0x20444d41", + "0x43595045", + "0x6f725020", + "0x73736563" + ] + }, + { + "leaf": "0x80000003", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": null, + "value": [ + "0x2820726f", + "0x6f6e6547", + "0x00002961", + "0x00000000" + ] + }, + { + "leaf": "0x80000004", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": null, + "value": [ + "0x00000000", + "0x00000000", + "0x00000000", + "0x00000000" + ] + }, + { + "leaf": "0x80000005", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": null, + "value": [ + "0xff48ff40", + "0xff48ff40", + "0x20080140", + "0x20080140" + ] + }, + { + "leaf": "0x80000006", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": null, + "value": [ + "0x5c002200", + "0x6c004200", + "0x04006140", + "0x0a009140" + ] + }, + { + "leaf": "0x80000007", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": null, + "value": [ + "0x00000000", + "0x00000000", + "0x00000000", + "0x00000100" + ] + }, + { + "leaf": "0x80000008", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffff0f00", + "0xffffffff" + ], + "subleaf": null, + "value": [ + "0x00003030", + "0x00000005", + "0x00000000", + "0x00000000" + ] + }, + { + "leaf": "0x80000009", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": null, + "value": [ + "0x00000000", + "0x00000000", + "0x00000000", + "0x00000000" + ] + }, + { + "leaf": "0x8000000a", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": null, + "value": [ + "0x00000000", + "0x00000000", + "0x00000000", + "0x00000000" + ] + }, + { + "leaf": "0x8000000b", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": null, + "value": [ + "0x00000000", + "0x00000000", + "0x00000000", + "0x00000000" + ] + }, + { + "leaf": "0x8000000c", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": null, + "value": [ + "0x00000000", + "0x00000000", + "0x00000000", + "0x00000000" + ] + }, + { + "leaf": "0x8000000d", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": null, + "value": [ + "0x00000000", + "0x00000000", + "0x00000000", + "0x00000000" + ] + }, + { + "leaf": "0x8000000e", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": null, + "value": [ + "0x00000000", + "0x00000000", + "0x00000000", + "0x00000000" + ] + }, + { + "leaf": "0x8000000f", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": null, + "value": [ + "0x00000000", + "0x00000000", + "0x00000000", + "0x00000000" + ] + }, + { + "leaf": "0x80000010", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": null, + "value": [ + "0x00000000", + "0x00000000", + "0x00000000", + "0x00000000" + ] + }, + { + "leaf": "0x80000011", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": null, + "value": [ + "0x00000000", + "0x00000000", + "0x00000000", + "0x00000000" + ] + }, + { + "leaf": "0x80000012", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": null, + "value": [ + "0x00000000", + "0x00000000", + "0x00000000", + "0x00000000" + ] + }, + { + "leaf": "0x80000013", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": null, + "value": [ + "0x00000000", + "0x00000000", + "0x00000000", + "0x00000000" + ] + }, + { + "leaf": "0x80000014", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": null, + "value": [ + "0x00000000", + "0x00000000", + "0x00000000", + "0x00000000" + ] + }, + { + "leaf": "0x80000015", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": null, + "value": [ + "0x00000000", + "0x00000000", + "0x00000000", + "0x00000000" + ] + }, + { + "leaf": "0x80000016", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": null, + "value": [ + "0x00000000", + "0x00000000", + "0x00000000", + "0x00000000" + ] + }, + { + "leaf": "0x80000017", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": null, + "value": [ + "0x00000000", + "0x00000000", + "0x00000000", + "0x00000000" + ] + }, + { + "leaf": "0x80000018", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": null, + "value": [ + "0x00000000", + "0x00000000", + "0x00000000", + "0x00000000" + ] + }, + { + "leaf": "0x80000019", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": null, + "value": [ + "0x00000000", + "0x00000000", + "0x00000000", + "0x00000000" + ] + }, + { + "leaf": "0x8000001a", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": null, + "value": [ + "0x00000000", + "0x00000000", + "0x00000000", + "0x00000000" + ] + }, + { + "leaf": "0x8000001b", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": null, + "value": [ + "0x00000000", + "0x00000000", + "0x00000000", + "0x00000000" + ] + }, + { + "leaf": "0x8000001c", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": null, + "value": [ + "0x00000000", + "0x00000000", + "0x00000000", + "0x00000000" + ] + }, + { + "leaf": "0x8000001d", + "mask": [ + "0xfc003fff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": "0x00000000", + "value": [ + "0x00000121", + "0x01c0003f", + "0x0000003f", + "0x00000000" + ] + }, + { + "leaf": "0x8000001d", + "mask": [ + "0xfc003fff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": "0x00000001", + "value": [ + "0x00000122", + "0x01c0003f", + "0x0000003f", + "0x00000000" + ] + }, + { + "leaf": "0x8000001d", + "mask": [ + "0xfc003fff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": "0x00000002", + "value": [ + "0x00000143", + "0x01c0003f", + "0x000007ff", + "0x00000002" + ] + }, + { + "leaf": "0x8000001d", + "mask": [ + "0xfc003fff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": "0x00000003", + "value": [ + "0x00000163", + "0x03c0003f", + "0x00007fff", + "0x00000001" + ] + }, + { + "leaf": "0x8000001d", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": "0x00000004", + "value": [ + "0x00000000", + "0x00000000", + "0x00000000", + "0x00000000" + ] + }, + { + "leaf": "0x8000001e", + "mask": [ + "0x00000000", + "0xffff0000", + "0xfffff800", + "0xffffffff" + ], + "subleaf": null, + "value": [ + "0x00000000", + "0x00000000", + "0x00000000", + "0x00000000" + ] + }, + { + "leaf": "0x8000001f", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": null, + "value": [ + "0x00000000", + "0x00000000", + "0x00000000", + "0x00000000" + ] + }, + { + "leaf": "0x80000020", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": null, + "value": [ + "0x00000000", + "0x00000000", + "0x00000000", + "0x00000000" + ] + }, + { + "leaf": "0x80000021", + "mask": [ + "0xffffffff", + "0xffffffff", + "0xffffffff", + "0xffffffff" + ], + "subleaf": null, + "value": [ + "0x00000000", + "0x00000000", + "0x00000000", + "0x00000000" + ] + } + ], + "description": "AMD EPYC, fourth generation (Genoa), on every backend; NVX time ABI v1", + "generation": { + "cpus": [ + { + "family": 25, + "model": 17, + "steppings": [ + 0, + 15 + ] + } + ], + "name": "genoa" + }, + "id": "amd.genoa.v2", + "msrs": [], + "physical_address_width": 48, + "provenance": { + "method": "derived by cpu_profile::derive under openvmm-cpu-profile-policy/v1: per-register consensus within each backend, then across backends, of the hosts' supported CPU surfaces", + "sources": [ + { + "backend": "kvm", + "hosts": 1, + "surface_digests": [ + "sha256:68c11e9ed42a2e64267876eca0d21ba1083d24f36d5b4f9d891a53523646c2aa" + ] + }, + { + "backend": "mshv", + "hosts": 1, + "surface_digests": [ + "sha256:0d4f63812ed2feae52196f88a16733f54b3822389ef062c8a0c31cdb31083585" + ] + } + ] + }, + "schema": "openvmm-cpu-profile/v1", + "vendor": "AuthenticAMD", + "xcr0": "0x0000000000000007", + "xsave_components": [ + { + "align64": false, + "index": 2, + "offset": 576, + "size": 256, + "supervisor": false, + "xfd": false + } + ], + "xss": "0x0000000000000000" +} diff --git a/vmm_core/cpu_profile/src/catalog.rs b/vmm_core/cpu_profile/src/catalog.rs index aec93046179..1284600ca5a 100644 --- a/vmm_core/cpu_profile/src/catalog.rs +++ b/vmm_core/cpu_profile/src/catalog.rs @@ -437,7 +437,7 @@ mod tests { /// The pinned JSON files, the source of the static data, and their golden /// digests. Released profiles are immutable: changing a golden digest is /// a deliberate act, here. - const FILES: [(&str, &str, &str); 7] = [ + const FILES: [(&str, &str, &str); 8] = [ ( "intel.skylake-sp.v1", include_str!("../profiles/intel.skylake-sp.v1.json"), @@ -468,6 +468,11 @@ mod tests { include_str!("../profiles/amd.genoa.v1.json"), "sha256:894ac647399039242eb0f0e6582e91691234446ebc9362576d7c5613f34b966b", ), + ( + "amd.genoa.v2", + include_str!("../profiles/amd.genoa.v2.json"), + "sha256:8ee00e9ee8fd5defe3eebdc343733195d477f5f3449defa403f616d530a84839", + ), ( "amd.turin.v1", include_str!("../profiles/amd.turin.v1.json"), @@ -565,8 +570,8 @@ mod tests { (intel(ALDER_LAKE_S), "intel.alderlake.v1", "alderlake"), (amd(MILAN), "amd.milan.v1", "milan"), (amd(MILAN_X), "amd.milan.v1", "milan"), - (amd(GENOA), "amd.genoa.v1", "genoa"), - (amd(GENOA_STEPPING_2), "amd.genoa.v1", "genoa"), + (amd(GENOA), "amd.genoa.v2", "genoa"), + (amd(GENOA_STEPPING_2), "amd.genoa.v2", "genoa"), (amd(TURIN), "amd.turin.v1", "turin"), ] { assert_eq!(select_auto(&host).unwrap().id(), id); @@ -726,6 +731,13 @@ mod tests { code(select("amd.milan.v1", &amd(GENOA))), ProfileErrorCode::CpuGeneration ); + + // An earlier revision of the host's generation, which auto no longer + // selects. + assert_eq!( + select("amd.genoa.v1", &amd(GENOA)).unwrap().id(), + "amd.genoa.v1" + ); } #[test] diff --git a/vmm_core/cpu_profile/src/check.rs b/vmm_core/cpu_profile/src/check.rs index fa31222e80f..898b18b9027 100644 --- a/vmm_core/cpu_profile/src/check.rs +++ b/vmm_core/cpu_profile/src/check.rs @@ -141,6 +141,7 @@ mod tests { use crate::cpuid::CpuidEntry; use crate::test_support::fingerprint; use crate::test_support::fingerprint_with; + use crate::test_support::genoa_whp_fingerprint; use crate::test_support::host_fingerprint; use crate::test_support::milan_whp_entries; use crate::test_support::profile; @@ -372,6 +373,56 @@ mod tests { ); } + /// The Genoa host of microsoft/nvx#928, an AMD EPYC 9V74 that WHP serves + /// on an Azure VM, does not support `amd.genoa.v1` but supports + /// `amd.genoa.v2`, which `auto` selects. Its probe partition presents + /// CET's XSAVE components, which a partition configured from the profile + /// does not, as on the Milan host. + #[test] + fn the_genoa_whp_host_supports_the_second_genoa_profile() { + let fingerprint = genoa_whp_fingerprint(); + // The check that the host reported on amd.genoa.v1, which auto + // selected before amd.genoa.v2. + let surface = HostCpuSurface::from_fingerprint(&fingerprint.backend); + let error = verify_support(profile("amd.genoa.v1"), &surface).unwrap_err(); + assert_eq!(error.code, ProfileErrorCode::ProfileUnsupported); + assert_eq!( + error.message, + "the backend does not support CPU profile amd.genoa.v1: CPUID 0x0 EAX[31:0] \ + is 0x1c, above the supported 0xd; CPUID 0x80000021 EAX bits 2, 9 are not \ + supported; CPUID 0x80000021 ECX bits 1, 2 are not supported; CPUID 0xd.11 \ + is outside the profile and reads EAX 0x10, EBX 0x0, ECX 0x1, EDX 0x0; \ + CPUID 0xd.12 is outside the profile and reads EAX 0x18, EBX 0x0, ECX 0x1, \ + EDX 0x0" + ); + + let error = check_fingerprint(&fingerprint).result.unwrap_err(); + assert_eq!(error.code, ProfileErrorCode::CpuUnlisted); + assert!( + error.message.starts_with( + "the backend presents CPUID entries outside CPU profile amd.genoa.v2: \ + CPUID 0xd.11 is outside the profile" + ), + "{error}" + ); + let check = check_fingerprint_with(&fingerprint, |profile| { + assert_eq!(profile.id(), "amd.genoa.v2"); + Ok(Some(vec![ + CpuidEntry::new(0xd, Some(11), [0; 4]), + CpuidEntry::new(0xd, Some(12), [0; 4]), + ])) + }); + check.result.as_ref().unwrap(); + assert!( + check.summary_line(&fingerprint).starts_with( + "NVX-CPU-PROFILE: status=pass backend=whp generation=genoa \ + profile=amd.genoa.v2 profile_digest=sha256:8ee00e9e" + ), + "{}", + check.summary_line(&fingerprint) + ); + } + #[test] fn configures_a_partition_only_when_it_decides_the_check() { let unused = |_: &CpuProfile| -> Result>, ProfileError> { diff --git a/vmm_core/cpu_profile/src/derive.rs b/vmm_core/cpu_profile/src/derive.rs index fa8a50be069..9d6ff1e9de7 100644 --- a/vmm_core/cpu_profile/src/derive.rs +++ b/vmm_core/cpu_profile/src/derive.rs @@ -994,6 +994,8 @@ mod tests { use crate::test_support::fingerprint; use crate::test_support::fingerprint_with; use crate::test_support::genoa_kvm_entries; + use crate::test_support::genoa_mshv_fingerprint; + use crate::test_support::genoa_whp_fingerprint; use crate::test_support::host_fingerprint; use crate::test_support::milan_whp_entries; use crate::test_support::profile; @@ -1040,9 +1042,14 @@ mod tests { fn rederiving_a_pinned_profile_reproduces_it() { for pinned in crate::pinned_profiles() { let known = generation(&pinned.generation().name); + let revision = pinned + .id() + .rsplit_once(".v") + .and_then(|(_, revision)| revision.parse().ok()) + .unwrap(); let derived = derive_profile( known, - 1, + revision, &[fingerprint(pinned, "kvm"), fingerprint(pinned, "whp")], ) .unwrap(); @@ -1419,6 +1426,113 @@ mod tests { } } + /// `amd.genoa.v2` is the policy's profile of the KVM host that + /// `amd.genoa.v1` derives from and of an Azure VM with the same CPU whose + /// MSHV enumerates the basic leaves only up to 0xD and presents nothing + /// in `0x80000021` (`test_support::GENOA_MSHV_CPUID`): v1 without + /// `LFENCE` serialization, the TSA immunities, and the zero basic leaves + /// above 0xD. Both hosts support it, and the MSHV host does not support + /// v1. + #[test] + fn the_second_genoa_profile_serves_the_kvm_and_mshv_hosts() { + let pinned = profile("amd.genoa.v2"); + let first = profile("amd.genoa.v1"); + let kvm = host_fingerprint("kvm", genoa_kvm_entries()); + let mshv = genoa_mshv_fingerprint(); + let derived = derive_profile(generation("genoa"), 2, &[kvm.clone(), mshv.clone()]).unwrap(); + assert_eq!(derived.id(), pinned.id()); + assert_eq!(derived.description(), pinned.description()); + assert_eq!(derived.generation(), pinned.generation()); + assert_eq!(derived.cpuid(), pinned.cpuid()); + assert_eq!(derived.xcr0(), pinned.xcr0()); + assert_eq!(derived.xss(), pinned.xss()); + assert_eq!(derived.xsave_components(), pinned.xsave_components()); + assert_eq!( + derived.physical_address_width(), + pinned.physical_address_width() + ); + assert_eq!(derived.msrs(), pinned.msrs()); + + // v1 but for the maximum basic leaf and 0x80000021. + assert_eq!((first.lookup(0, 0)[0], pinned.lookup(0, 0)[0]), (0x1c, 0xd)); + assert_eq!(first.lookup(0x8000_0021, 0), [0x204, 0, 0x6, 0]); + assert_eq!(pinned.lookup(0x8000_0021, 0), [0; 4]); + for entry in first.cpuid() { + let (leaf, subleaf) = entry.key(); + match pinned + .cpuid() + .iter() + .find(|pinned| pinned.key() == (leaf, subleaf)) + { + Some(_) if leaf == 0 || leaf == 0x8000_0021 => {} + Some(pinned) => assert_eq!(pinned, entry), + None => { + assert!((0xe..=0x1c).contains(&leaf), "{leaf:#x}"); + assert_eq!(entry.values(), [0; 4], "{leaf:#x}"); + } + } + } + + // The hosts disagree on AMD's INVLPGB and RDPRU limits, which take + // their minimum, as other limits do, rather than tie. + let limits = |fingerprint: &CpuFingerprint| { + cpuid::lookup(&fingerprint.backend.cpuid, 0x8000_0008, 0).unwrap()[3] + }; + assert_eq!((limits(&kvm), limits(&mshv)), (0, 0x1_0000)); + assert_eq!(pinned.lookup(0x8000_0008, 0)[3], 0); + + for host in [&kvm, &mshv] { + let surface = crate::HostCpuSurface::from_fingerprint(&host.backend); + assert_eq!( + crate::support_violations(pinned, &surface), + Vec::::new(), + "{}", + host.backend.name + ); + } + let surface = crate::HostCpuSurface::from_fingerprint(&mshv.backend); + assert_eq!( + crate::support_violations(first, &surface), + [ + "CPUID 0x0 EAX[31:0] is 0x1c, above the supported 0xd", + "CPUID 0x80000021 EAX bits 2, 9 are not supported", + "CPUID 0x80000021 ECX bits 1, 2 are not supported", + ] + ); + } + + /// The WHP host of `test_support::GENOA_WHP_CPUID` supports + /// `amd.genoa.v2`, and adding it to the KVM and MSHV hosts that v2 + /// derives from derives v2's CPUID, XSAVE state, and MSRs again: it lacks + /// nothing that v2 takes from them. + #[test] + fn the_whp_host_adds_nothing_to_the_second_genoa_profile() { + let pinned = profile("amd.genoa.v2"); + let whp = genoa_whp_fingerprint(); + let surface = crate::HostCpuSurface::from_fingerprint(&whp.backend); + assert_eq!( + crate::support_violations(pinned, &surface), + Vec::::new() + ); + + let hosts = [ + host_fingerprint("kvm", genoa_kvm_entries()), + genoa_mshv_fingerprint(), + whp, + ]; + let derived = derive_profile(generation("genoa"), 2, &hosts).unwrap(); + assert_eq!(derived.id(), pinned.id()); + assert_eq!(derived.cpuid(), pinned.cpuid()); + assert_eq!(derived.xcr0(), pinned.xcr0()); + assert_eq!(derived.xss(), pinned.xss()); + assert_eq!(derived.xsave_components(), pinned.xsave_components()); + assert_eq!( + derived.physical_address_width(), + pinned.physical_address_width() + ); + assert_eq!(derived.msrs(), pinned.msrs()); + } + /// KVM enumerates Intel's speculation controls and /// `IA32_ARCH_CAPABILITIES` on AMD hosts too, as the Genoa and Turin /// hosts' KVM does, and emulates the MSR. An AMD profile has none of diff --git a/vmm_core/cpu_profile/src/hv_banks.rs b/vmm_core/cpu_profile/src/hv_banks.rs index 1726c1eaa58..e282a5af87e 100644 --- a/vmm_core/cpu_profile/src/hv_banks.rs +++ b/vmm_core/cpu_profile/src/hv_banks.rs @@ -1280,4 +1280,47 @@ mod tests { "intel_prefetch_support" )); } + + /// The Genoa hosts of `test_support::GENOA_MSHV_CPUID` and + /// `test_support::GENOA_WHP_CPUID` offer neither TSA immunity, so they + /// cannot present `amd.genoa.v1`, which pins both, and present + /// `amd.genoa.v2`, which clears them. WHP offers its Genoa host the + /// processor features that it offers the Milan host. + #[test] + fn the_genoa_mshv_and_whp_hosts_present_the_second_genoa_profile() { + assert_eq!(crate::test_support::GENOA_WHP_HOST, MILAN_WHP_HOST); + for (backend, host) in [ + ("mshv", crate::test_support::GENOA_MSHV_HOST), + ("whp", crate::test_support::GENOA_WHP_HOST), + ] { + let error = profile_features(profile("amd.genoa.v1"), host).unwrap_err(); + assert_eq!( + error.code, + ProfileErrorCode::ProfileUnsupported, + "{backend}" + ); + assert!( + error + .message + .ends_with("they lack tsa_l1_no_support, tsa_sq_no_support"), + "{backend}: {error}" + ); + + let features = profile_features(profile("amd.genoa.v2"), host).unwrap(); + for word in HvFeatureWord::ALL { + assert_eq!( + features.word(word) & !host.word(word), + 0, + "{backend}: {}", + word.name() + ); + } + for name in ["tsa_l1_no_support", "tsa_sq_no_support"] { + assert!( + !has(&features, HvFeatureWord::Bank1, name), + "{backend}: {name}" + ); + } + } + } } diff --git a/vmm_core/cpu_profile/src/pinned_data.rs b/vmm_core/cpu_profile/src/pinned_data.rs index d2687e175e6..289b6d55972 100644 --- a/vmm_core/cpu_profile/src/pinned_data.rs +++ b/vmm_core/cpu_profile/src/pinned_data.rs @@ -15,7 +15,7 @@ use crate::pinned::xsave; /// The pinned profiles, in catalog order. Released profiles are immutable: a /// change is a new revision with a new ID. -pub(crate) const PINNED: [PinnedProfile; 7] = [ +pub(crate) const PINNED: [PinnedProfile; 8] = [ PinnedProfile { id: "intel.skylake-sp.v1", digest: digest("b36ef861ffec67350a28faff8c446d5a19f78d6d58862378fa9d2282dad87492"), @@ -1990,6 +1990,351 @@ pub(crate) const PINNED: [PinnedProfile; 7] = [ &["sha256:68c11e9ed42a2e64267876eca0d21ba1083d24f36d5b4f9d891a53523646c2aa"], )], }, + PinnedProfile { + id: "amd.genoa.v2", + digest: digest("8ee00e9ee8fd5defe3eebdc343733195d477f5f3449defa403f616d530a84839"), + encoding: r#"{"cpuid":[{"leaf":"0x00000000","mask":["0xffffffff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":null,"value":["0x0000000d","0x68747541","0x444d4163","0x69746e65"]},{"leaf":"0x00000001","mask":["0xffffffff","0x0000ffff","0xf7dfffff","0xffffffff"],"subleaf":null,"value":["0x00a10f11","0x00000800","0xf6da3203","0x078bfbff"]},{"leaf":"0x00000002","mask":["0xffffffff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":null,"value":["0x00000000","0x00000000","0x00000000","0x00000000"]},{"leaf":"0x00000003","mask":["0xffffffff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":null,"value":["0x00000000","0x00000000","0x00000000","0x00000000"]},{"leaf":"0x00000004","mask":["0xffffffff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":"0x00000000","value":["0x00000000","0x00000000","0x00000000","0x00000000"]},{"leaf":"0x00000005","mask":["0xffffffff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":null,"value":["0x00000000","0x00000000","0x00000000","0x00000000"]},{"leaf":"0x00000006","mask":["0xffffffff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":null,"value":["0x00000004","0x00000000","0x00000000","0x00000000"]},{"leaf":"0x00000007","mask":["0xffffffff","0xffffffff","0xffffffef","0xffffffff"],"subleaf":"0x00000000","value":["0x00000000","0x219c07a9","0x00400604","0x00000010"]},{"leaf":"0x00000008","mask":["0xffffffff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":null,"value":["0x00000000","0x00000000","0x00000000","0x00000000"]},{"leaf":"0x00000009","mask":["0xffffffff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":null,"value":["0x00000000","0x00000000","0x00000000","0x00000000"]},{"leaf":"0x0000000a","mask":["0xffffffff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":null,"value":["0x00000000","0x00000000","0x00000000","0x00000000"]},{"leaf":"0x0000000c","mask":["0xffffffff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":null,"value":["0x00000000","0x00000000","0x00000000","0x00000000"]},{"leaf":"0x0000000d","mask":["0xffffffff","0x00000000","0xffffffff","0xffffffff"],"subleaf":"0x00000000","value":["0x00000007","0x00000000","0x00000340","0x00000000"]},{"leaf":"0x0000000d","mask":["0xffffffff","0x00000000","0xffffffff","0xffffffff"],"subleaf":"0x00000001","value":["0x0000000f","0x00000000","0x00000000","0x00000000"]},{"leaf":"0x0000000d","mask":["0xffffffff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":"0x00000002","value":["0x00000100","0x00000240","0x00000000","0x00000000"]},{"leaf":"0x80000000","mask":["0xffffffff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":null,"value":["0x80000021","0x68747541","0x444d4163","0x69746e65"]},{"leaf":"0x80000001","mask":["0xffffffff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":null,"value":["0x00a10f11","0x00000000","0x004002f3","0x2fd3fbff"]},{"leaf":"0x80000002","mask":["0xffffffff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":null,"value":["0x20444d41","0x43595045","0x6f725020","0x73736563"]},{"leaf":"0x80000003","mask":["0xffffffff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":null,"value":["0x2820726f","0x6f6e6547","0x00002961","0x00000000"]},{"leaf":"0x80000004","mask":["0xffffffff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":null,"value":["0x00000000","0x00000000","0x00000000","0x00000000"]},{"leaf":"0x80000005","mask":["0xffffffff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":null,"value":["0xff48ff40","0xff48ff40","0x20080140","0x20080140"]},{"leaf":"0x80000006","mask":["0xffffffff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":null,"value":["0x5c002200","0x6c004200","0x04006140","0x0a009140"]},{"leaf":"0x80000007","mask":["0xffffffff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":null,"value":["0x00000000","0x00000000","0x00000000","0x00000100"]},{"leaf":"0x80000008","mask":["0xffffffff","0xffffffff","0xffff0f00","0xffffffff"],"subleaf":null,"value":["0x00003030","0x00000005","0x00000000","0x00000000"]},{"leaf":"0x80000009","mask":["0xffffffff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":null,"value":["0x00000000","0x00000000","0x00000000","0x00000000"]},{"leaf":"0x8000000a","mask":["0xffffffff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":null,"value":["0x00000000","0x00000000","0x00000000","0x00000000"]},{"leaf":"0x8000000b","mask":["0xffffffff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":null,"value":["0x00000000","0x00000000","0x00000000","0x00000000"]},{"leaf":"0x8000000c","mask":["0xffffffff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":null,"value":["0x00000000","0x00000000","0x00000000","0x00000000"]},{"leaf":"0x8000000d","mask":["0xffffffff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":null,"value":["0x00000000","0x00000000","0x00000000","0x00000000"]},{"leaf":"0x8000000e","mask":["0xffffffff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":null,"value":["0x00000000","0x00000000","0x00000000","0x00000000"]},{"leaf":"0x8000000f","mask":["0xffffffff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":null,"value":["0x00000000","0x00000000","0x00000000","0x00000000"]},{"leaf":"0x80000010","mask":["0xffffffff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":null,"value":["0x00000000","0x00000000","0x00000000","0x00000000"]},{"leaf":"0x80000011","mask":["0xffffffff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":null,"value":["0x00000000","0x00000000","0x00000000","0x00000000"]},{"leaf":"0x80000012","mask":["0xffffffff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":null,"value":["0x00000000","0x00000000","0x00000000","0x00000000"]},{"leaf":"0x80000013","mask":["0xffffffff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":null,"value":["0x00000000","0x00000000","0x00000000","0x00000000"]},{"leaf":"0x80000014","mask":["0xffffffff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":null,"value":["0x00000000","0x00000000","0x00000000","0x00000000"]},{"leaf":"0x80000015","mask":["0xffffffff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":null,"value":["0x00000000","0x00000000","0x00000000","0x00000000"]},{"leaf":"0x80000016","mask":["0xffffffff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":null,"value":["0x00000000","0x00000000","0x00000000","0x00000000"]},{"leaf":"0x80000017","mask":["0xffffffff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":null,"value":["0x00000000","0x00000000","0x00000000","0x00000000"]},{"leaf":"0x80000018","mask":["0xffffffff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":null,"value":["0x00000000","0x00000000","0x00000000","0x00000000"]},{"leaf":"0x80000019","mask":["0xffffffff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":null,"value":["0x00000000","0x00000000","0x00000000","0x00000000"]},{"leaf":"0x8000001a","mask":["0xffffffff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":null,"value":["0x00000000","0x00000000","0x00000000","0x00000000"]},{"leaf":"0x8000001b","mask":["0xffffffff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":null,"value":["0x00000000","0x00000000","0x00000000","0x00000000"]},{"leaf":"0x8000001c","mask":["0xffffffff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":null,"value":["0x00000000","0x00000000","0x00000000","0x00000000"]},{"leaf":"0x8000001d","mask":["0xfc003fff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":"0x00000000","value":["0x00000121","0x01c0003f","0x0000003f","0x00000000"]},{"leaf":"0x8000001d","mask":["0xfc003fff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":"0x00000001","value":["0x00000122","0x01c0003f","0x0000003f","0x00000000"]},{"leaf":"0x8000001d","mask":["0xfc003fff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":"0x00000002","value":["0x00000143","0x01c0003f","0x000007ff","0x00000002"]},{"leaf":"0x8000001d","mask":["0xfc003fff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":"0x00000003","value":["0x00000163","0x03c0003f","0x00007fff","0x00000001"]},{"leaf":"0x8000001d","mask":["0xffffffff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":"0x00000004","value":["0x00000000","0x00000000","0x00000000","0x00000000"]},{"leaf":"0x8000001e","mask":["0x00000000","0xffff0000","0xfffff800","0xffffffff"],"subleaf":null,"value":["0x00000000","0x00000000","0x00000000","0x00000000"]},{"leaf":"0x8000001f","mask":["0xffffffff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":null,"value":["0x00000000","0x00000000","0x00000000","0x00000000"]},{"leaf":"0x80000020","mask":["0xffffffff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":null,"value":["0x00000000","0x00000000","0x00000000","0x00000000"]},{"leaf":"0x80000021","mask":["0xffffffff","0xffffffff","0xffffffff","0xffffffff"],"subleaf":null,"value":["0x00000000","0x00000000","0x00000000","0x00000000"]}],"description":"AMD EPYC, fourth generation (Genoa), on every backend; NVX time ABI v1","generation":{"cpus":[{"family":25,"model":17,"steppings":[0,15]}],"name":"genoa"},"id":"amd.genoa.v2","msrs":[],"physical_address_width":48,"provenance":{"method":"derived by cpu_profile::derive under openvmm-cpu-profile-policy/v1: per-register consensus within each backend, then across backends, of the hosts' supported CPU surfaces","sources":[{"backend":"kvm","hosts":1,"surface_digests":["sha256:68c11e9ed42a2e64267876eca0d21ba1083d24f36d5b4f9d891a53523646c2aa"]},{"backend":"mshv","hosts":1,"surface_digests":["sha256:0d4f63812ed2feae52196f88a16733f54b3822389ef062c8a0c31cdb31083585"]}]},"schema":"openvmm-cpu-profile/v1","vendor":"AuthenticAMD","xcr0":"0x0000000000000007","xsave_components":[{"align64":false,"index":2,"offset":576,"size":256,"supervisor":false,"xfd":false}],"xss":"0x0000000000000000"}"#, + description: "AMD EPYC, fourth generation (Genoa), on every backend; NVX time ABI v1", + generation: &KNOWN_GENERATIONS[5], + cpuid: &[ + leaf( + 0x0, + None, + [0xd, 0x6874_7541, 0x444d_4163, 0x6974_6e65], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0x1, + None, + [0xa1_0f11, 0x800, 0xf6da_3203, 0x78b_fbff], + [0xffff_ffff, 0xffff, 0xf7df_ffff, 0xffff_ffff], + ), + leaf( + 0x2, + None, + [0x0, 0x0, 0x0, 0x0], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0x3, + None, + [0x0, 0x0, 0x0, 0x0], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0x4, + Some(0x0), + [0x0, 0x0, 0x0, 0x0], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0x5, + None, + [0x0, 0x0, 0x0, 0x0], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0x6, + None, + [0x4, 0x0, 0x0, 0x0], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0x7, + Some(0x0), + [0x0, 0x219c_07a9, 0x40_0604, 0x10], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffef, 0xffff_ffff], + ), + leaf( + 0x8, + None, + [0x0, 0x0, 0x0, 0x0], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0x9, + None, + [0x0, 0x0, 0x0, 0x0], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0xa, + None, + [0x0, 0x0, 0x0, 0x0], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0xc, + None, + [0x0, 0x0, 0x0, 0x0], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0xd, + Some(0x0), + [0x7, 0x0, 0x340, 0x0], + [0xffff_ffff, 0x0, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0xd, + Some(0x1), + [0xf, 0x0, 0x0, 0x0], + [0xffff_ffff, 0x0, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0xd, + Some(0x2), + [0x100, 0x240, 0x0, 0x0], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0x8000_0000, + None, + [0x8000_0021, 0x6874_7541, 0x444d_4163, 0x6974_6e65], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0x8000_0001, + None, + [0xa1_0f11, 0x0, 0x40_02f3, 0x2fd3_fbff], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0x8000_0002, + None, + [0x2044_4d41, 0x4359_5045, 0x6f72_5020, 0x7373_6563], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0x8000_0003, + None, + [0x2820_726f, 0x6f6e_6547, 0x2961, 0x0], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0x8000_0004, + None, + [0x0, 0x0, 0x0, 0x0], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0x8000_0005, + None, + [0xff48_ff40, 0xff48_ff40, 0x2008_0140, 0x2008_0140], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0x8000_0006, + None, + [0x5c00_2200, 0x6c00_4200, 0x400_6140, 0xa00_9140], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0x8000_0007, + None, + [0x0, 0x0, 0x0, 0x100], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0x8000_0008, + None, + [0x3030, 0x5, 0x0, 0x0], + [0xffff_ffff, 0xffff_ffff, 0xffff_0f00, 0xffff_ffff], + ), + leaf( + 0x8000_0009, + None, + [0x0, 0x0, 0x0, 0x0], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0x8000_000a, + None, + [0x0, 0x0, 0x0, 0x0], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0x8000_000b, + None, + [0x0, 0x0, 0x0, 0x0], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0x8000_000c, + None, + [0x0, 0x0, 0x0, 0x0], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0x8000_000d, + None, + [0x0, 0x0, 0x0, 0x0], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0x8000_000e, + None, + [0x0, 0x0, 0x0, 0x0], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0x8000_000f, + None, + [0x0, 0x0, 0x0, 0x0], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0x8000_0010, + None, + [0x0, 0x0, 0x0, 0x0], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0x8000_0011, + None, + [0x0, 0x0, 0x0, 0x0], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0x8000_0012, + None, + [0x0, 0x0, 0x0, 0x0], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0x8000_0013, + None, + [0x0, 0x0, 0x0, 0x0], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0x8000_0014, + None, + [0x0, 0x0, 0x0, 0x0], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0x8000_0015, + None, + [0x0, 0x0, 0x0, 0x0], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0x8000_0016, + None, + [0x0, 0x0, 0x0, 0x0], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0x8000_0017, + None, + [0x0, 0x0, 0x0, 0x0], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0x8000_0018, + None, + [0x0, 0x0, 0x0, 0x0], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0x8000_0019, + None, + [0x0, 0x0, 0x0, 0x0], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0x8000_001a, + None, + [0x0, 0x0, 0x0, 0x0], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0x8000_001b, + None, + [0x0, 0x0, 0x0, 0x0], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0x8000_001c, + None, + [0x0, 0x0, 0x0, 0x0], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0x8000_001d, + Some(0x0), + [0x121, 0x1c0_003f, 0x3f, 0x0], + [0xfc00_3fff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0x8000_001d, + Some(0x1), + [0x122, 0x1c0_003f, 0x3f, 0x0], + [0xfc00_3fff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0x8000_001d, + Some(0x2), + [0x143, 0x1c0_003f, 0x7ff, 0x2], + [0xfc00_3fff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0x8000_001d, + Some(0x3), + [0x163, 0x3c0_003f, 0x7fff, 0x1], + [0xfc00_3fff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0x8000_001d, + Some(0x4), + [0x0, 0x0, 0x0, 0x0], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0x8000_001e, + None, + [0x0, 0x0, 0x0, 0x0], + [0x0, 0xffff_0000, 0xffff_f800, 0xffff_ffff], + ), + leaf( + 0x8000_001f, + None, + [0x0, 0x0, 0x0, 0x0], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0x8000_0020, + None, + [0x0, 0x0, 0x0, 0x0], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + leaf( + 0x8000_0021, + None, + [0x0, 0x0, 0x0, 0x0], + [0xffff_ffff, 0xffff_ffff, 0xffff_ffff, 0xffff_ffff], + ), + ], + xcr0: 0x7, + xss: 0x0, + xsave_components: &[xsave(2, 256, 576, false, false, false)], + physical_address_width: 48, + msrs: &[], + provenance_method: "derived by cpu_profile::derive under openvmm-cpu-profile-policy/v1: per-register consensus within each backend, then across backends, of the hosts' supported CPU surfaces", + provenance_sources: &[ + source( + "kvm", + 1, + &["sha256:68c11e9ed42a2e64267876eca0d21ba1083d24f36d5b4f9d891a53523646c2aa"], + ), + source( + "mshv", + 1, + &["sha256:0d4f63812ed2feae52196f88a16733f54b3822389ef062c8a0c31cdb31083585"], + ), + ], + }, PinnedProfile { id: "amd.turin.v1", digest: digest("0d9d044922eae3f27442178249f0b60feb0db07efe446e6641a1fcbfbb3a04ff"), diff --git a/vmm_core/cpu_profile/src/surface.rs b/vmm_core/cpu_profile/src/surface.rs index fe8e186d011..246a6b27ba8 100644 --- a/vmm_core/cpu_profile/src/surface.rs +++ b/vmm_core/cpu_profile/src/surface.rs @@ -155,14 +155,18 @@ pub(crate) enum RegisterClass { pub(crate) fn register_class(leaf: u32, subleaf: u32, register: usize) -> RegisterClass { const ALL: &[(u32, u32)] = &[(0, 32)]; const ADDRESS_WIDTHS: &[(u32, u32)] = &[(0, 8), (8, 8)]; + // AMD's INVLPGB page count and RDPRU's largest register index; Intel + // reserves the register. + const AMD_INSTRUCTION_LIMITS: &[(u32, u32)] = &[(0, 16), (16, 10)]; match (leaf, subleaf, register) { (0x0, _, 0) | (0x8000_0000, _, 0) | (0x7, 0, 0) => RegisterClass::Limits(ALL), (0x8000_0008, _, 0) => RegisterClass::Limits(ADDRESS_WIDTHS), + (0x8000_0008, _, 3) => RegisterClass::Limits(AMD_INSTRUCTION_LIMITS), (0x0 | 0x2 | 0x3 | 0x4 | 0xd | 0x18 | 0x8000_001d, _, _) | (0x1 | 0x8000_0001, _, 0) | (0x1, _, 1) | (0x8000_0002..=0x8000_0006, _, _) - | (0x8000_0008, _, 2 | 3) => RegisterClass::Informational, + | (0x8000_0008, _, 2) => RegisterClass::Informational, _ => RegisterClass::Features, } } diff --git a/vmm_core/cpu_profile/src/test_support.rs b/vmm_core/cpu_profile/src/test_support.rs index 217593e5332..b6d086a9c13 100644 --- a/vmm_core/cpu_profile/src/test_support.rs +++ b/vmm_core/cpu_profile/src/test_support.rs @@ -14,6 +14,7 @@ use crate::fingerprint::ToolIdentity; use crate::host::HostCpu; use crate::host::HostIdentity; use crate::host::HostOs; +use crate::hv_banks::HvFeatures; use crate::signature::decode_signature; /// Returns the pinned profile `id`. @@ -55,14 +56,16 @@ pub(crate) fn fingerprint_with( /// Returns the fingerprint of a host whose `backend` reports `cpuid`, of the /// CPU whose vendor, signature, and brand `cpuid` reports. pub(crate) fn host_fingerprint(backend: &str, cpuid: Vec) -> CpuFingerprint { - let leaf = |leaf| crate::cpuid::lookup(&cpuid, leaf, 0).unwrap_or_default(); + backend_host_fingerprint(BackendFingerprint::new(backend, "test", cpuid)) +} + +/// Returns the fingerprint of a host whose backend reports `backend`, of the +/// CPU whose vendor, signature, and brand the backend's CPUID reports. +fn backend_host_fingerprint(backend: BackendFingerprint) -> CpuFingerprint { + let leaf = |leaf| crate::cpuid::lookup(&backend.cpuid, leaf, 0).unwrap_or_default(); let [_, ebx, ecx, edx] = leaf(0); let host = host_identity(&crate::signature::vendor_bytes(ebx, edx, ecx), &leaf); - CpuFingerprint::new( - tool(), - host, - BackendFingerprint::new(backend, "test", cpuid), - ) + CpuFingerprint::new(tool(), host, backend) } fn tool() -> ToolIdentity { @@ -320,6 +323,245 @@ pub(crate) fn genoa_kvm_entries() -> Vec { entries(&GENOA_KVM_CPUID) } +/// The CPUID of an AMD EPYC 9V74 (Genoa: family 0x19, model 0x11, +/// stepping 1) as MSHV presents it in an Azure VM with nested +/// virtualization, which `--cpu-fingerprint` recorded: each leaf, subleaf, +/// and the registers of a probe partition that enables every processor and +/// XSAVE feature of the host partition ([`GENOA_MSHV_HOST`]). That MSHV +/// offers AVX-512 and CET's XSAVE states, enumerates the basic leaves only up +/// to 0xD, and presents nothing in `0x80000021`: neither `LFENCE` +/// serialization nor the TSA immunities that the Genoa runner's Azure host +/// presents to KVM ([`GENOA_KVM_CPUID`]). +pub(crate) const GENOA_MSHV_CPUID: [(u32, Option, [u32; 4]); 61] = [ + (0x0, None, [0xd, 0x6874_7541, 0x444d_4163, 0x6974_6e65]), + (0x1, None, [0x00a1_0f11, 0x800, 0x76fa_3203, 0x078b_fbff]), + (0x2, None, [0; 4]), + (0x3, None, [0; 4]), + (0x4, Some(0), [0; 4]), + (0x5, None, [0; 4]), + (0x6, None, [0, 0, 0x1, 0]), + (0x7, Some(0), [0x1, 0xf1bf_07a9, 0x0040_5fc6, 0x10]), + (0x7, Some(1), [0x20, 0, 0, 0]), + (0x8, None, [0; 4]), + (0x9, None, [0; 4]), + (0xa, None, [0; 4]), + (0xb, Some(0), [0; 4]), + (0xc, None, [0; 4]), + (0xd, Some(0), [0xe7, 0x980, 0x980, 0]), + (0xd, Some(1), [0xf, 0x9a8, 0x1800, 0]), + (0xd, Some(2), [0x100, 0x240, 0, 0]), + (0xd, Some(5), [0x40, 0x340, 0, 0]), + (0xd, Some(6), [0x200, 0x380, 0, 0]), + (0xd, Some(7), [0x400, 0x580, 0, 0]), + (0xd, Some(0xb), [0x10, 0, 0x1, 0]), + (0xd, Some(0xc), [0x18, 0, 0x1, 0]), + (0x4000_0000, None, [0; 4]), + ( + 0x8000_0000, + None, + [0x8000_0021, 0x6874_7541, 0x444d_4163, 0x6974_6e65], + ), + ( + 0x8000_0001, + None, + [0x00a1_0f11, 0x4000_0000, 0x0040_03f3, 0x2fd3_fbff], + ), + ( + 0x8000_0002, + None, + [0x2044_4d41, 0x4359_5045, 0x3756_3920, 0x3038_2034], + ), + ( + 0x8000_0003, + None, + [0x726f_432d, 0x7250_2065, 0x7365_636f, 0x2072_6f73], + ), + ( + 0x8000_0004, + None, + [0x2020_2020, 0x2020_2020, 0x2020_2020, 0x0020_2020], + ), + ( + 0x8000_0005, + None, + [0xff48_ff40, 0xff48_ff40, 0x2008_0140, 0x2008_0140], + ), + ( + 0x8000_0006, + None, + [0x5c00_2200, 0x6c00_4200, 0x0400_6140, 0x0a00_9140], + ), + (0x8000_0007, None, [0; 4]), + (0x8000_0008, None, [0x3030, 0x3000_0015, 0, 0x0001_0000]), + (0x8000_0009, None, [0; 4]), + (0x8000_000a, None, [0; 4]), + (0x8000_000b, None, [0; 4]), + (0x8000_000c, None, [0; 4]), + (0x8000_000d, None, [0; 4]), + (0x8000_000e, None, [0; 4]), + (0x8000_000f, None, [0; 4]), + (0x8000_0010, None, [0; 4]), + (0x8000_0011, None, [0; 4]), + (0x8000_0012, None, [0; 4]), + (0x8000_0013, None, [0; 4]), + (0x8000_0014, None, [0; 4]), + (0x8000_0015, None, [0; 4]), + (0x8000_0016, None, [0; 4]), + (0x8000_0017, None, [0; 4]), + (0x8000_0018, None, [0; 4]), + (0x8000_0019, None, [0; 4]), + (0x8000_001a, None, [0x2, 0, 0, 0]), + (0x8000_001b, None, [0; 4]), + (0x8000_001c, None, [0; 4]), + (0x8000_001d, Some(0), [0x121, 0x01c0_003f, 0x3f, 0]), + (0x8000_001d, Some(1), [0x122, 0x01c0_003f, 0x3f, 0]), + (0x8000_001d, Some(2), [0x143, 0x01c0_003f, 0x7ff, 0x2]), + (0x8000_001d, Some(3), [0x163, 0x03c0_003f, 0x7fff, 0x1]), + (0x8000_001d, Some(4), [0; 4]), + (0x8000_001e, None, [0; 4]), + (0x8000_001f, None, [0; 4]), + (0x8000_0020, None, [0; 4]), + (0x8000_0021, None, [0; 4]), +]; + +/// The processor features that the host partition of [`GENOA_MSHV_CPUID`]'s +/// host offers its child partitions: none of the TSA immunities. +pub(crate) const GENOA_MSHV_HOST: HvFeatures = HvFeatures { + banks: [0x0602_0fcb_67f7_9fbf, 0x0000_0050_e000_01ed], + xsave: 0xff_ffdf, +}; + +/// Returns the fingerprint of the MSHV host of [`GENOA_MSHV_CPUID`], with +/// its processor features. +pub(crate) fn genoa_mshv_fingerprint() -> CpuFingerprint { + let mut backend = BackendFingerprint::new("mshv", "test", entries(&GENOA_MSHV_CPUID)); + let [bank0, bank1] = GENOA_MSHV_HOST.banks; + backend.set_feature_bank("mshv.host.ProcessorFeatures0", bank0); + backend.set_feature_bank("mshv.host.ProcessorFeatures1", bank1); + backend.set_feature_bank("mshv.host.ProcessorXsaveFeatures", GENOA_MSHV_HOST.xsave); + backend_host_fingerprint(backend) +} + +/// The CPUID of an AMD EPYC 9V74 (Genoa: family 0x19, model 0x11, +/// stepping 1) as WHP presents it in an Azure VM with nested +/// virtualization, which `--cpu-fingerprint` recorded (microsoft/nvx#928): +/// each leaf, subleaf, and the registers of a probe partition that enables +/// every processor and XSAVE feature WHP offers ([`GENOA_WHP_HOST`]). That +/// WHP offers the x87, SSE, AVX, and CET XSAVE states, enumerates the basic +/// leaves only up to 0xD, and presents nothing in `0x80000021`: neither +/// `LFENCE` serialization nor the TSA immunities that the Genoa runner's +/// Azure host presents to KVM ([`GENOA_KVM_CPUID`]). Its CPUID is the Milan +/// host's ([`MILAN_WHP_CPUID`]) but for the CPU's identity and its cache and +/// TLB descriptors. +pub(crate) const GENOA_WHP_CPUID: [(u32, Option, [u32; 4]); 57] = [ + (0x0, None, [0xd, 0x6874_7541, 0x444d_4163, 0x6974_6e65]), + (0x1, None, [0x00a1_0f11, 0x800, 0x76fa_3203, 0x078b_fbff]), + (0x2, None, [0; 4]), + (0x3, None, [0; 4]), + (0x4, Some(0), [0; 4]), + (0x5, None, [0; 4]), + (0x6, None, [0, 0, 0x1, 0]), + (0x7, Some(0), [0, 0x219c_07a9, 0x0040_0684, 0x10]), + (0x8, None, [0; 4]), + (0x9, None, [0; 4]), + (0xa, None, [0; 4]), + (0xb, Some(0), [0; 4]), + (0xc, None, [0; 4]), + (0xd, Some(0), [0x7, 0x340, 0x340, 0]), + (0xd, Some(1), [0xf, 0x368, 0x1800, 0]), + (0xd, Some(2), [0x100, 0x240, 0, 0]), + (0xd, Some(0xb), [0x10, 0, 0x1, 0]), + (0xd, Some(0xc), [0x18, 0, 0x1, 0]), + (0x4000_0000, None, [0; 4]), + ( + 0x8000_0000, + None, + [0x8000_0021, 0x6874_7541, 0x444d_4163, 0x6974_6e65], + ), + ( + 0x8000_0001, + None, + [0x00a1_0f11, 0x4000_0000, 0x0040_03f3, 0x2fd3_fbff], + ), + ( + 0x8000_0002, + None, + [0x2044_4d41, 0x4359_5045, 0x3756_3920, 0x3038_2034], + ), + ( + 0x8000_0003, + None, + [0x726f_432d, 0x7250_2065, 0x7365_636f, 0x2072_6f73], + ), + ( + 0x8000_0004, + None, + [0x2020_2020, 0x2020_2020, 0x2020_2020, 0x0020_2020], + ), + ( + 0x8000_0005, + None, + [0xff48_ff40, 0xff48_ff40, 0x2008_0140, 0x2008_0140], + ), + ( + 0x8000_0006, + None, + [0x5c00_2200, 0x6c00_4200, 0x0400_6140, 0x0a00_9140], + ), + (0x8000_0007, None, [0; 4]), + (0x8000_0008, None, [0x3030, 0x3000_0015, 0, 0x0001_0000]), + (0x8000_0009, None, [0; 4]), + (0x8000_000a, None, [0; 4]), + (0x8000_000b, None, [0; 4]), + (0x8000_000c, None, [0; 4]), + (0x8000_000d, None, [0; 4]), + (0x8000_000e, None, [0; 4]), + (0x8000_000f, None, [0; 4]), + (0x8000_0010, None, [0; 4]), + (0x8000_0011, None, [0; 4]), + (0x8000_0012, None, [0; 4]), + (0x8000_0013, None, [0; 4]), + (0x8000_0014, None, [0; 4]), + (0x8000_0015, None, [0; 4]), + (0x8000_0016, None, [0; 4]), + (0x8000_0017, None, [0; 4]), + (0x8000_0018, None, [0; 4]), + (0x8000_0019, None, [0; 4]), + (0x8000_001a, None, [0x2, 0, 0, 0]), + (0x8000_001b, None, [0; 4]), + (0x8000_001c, None, [0; 4]), + (0x8000_001d, Some(0), [0x121, 0x01c0_003f, 0x3f, 0]), + (0x8000_001d, Some(1), [0x122, 0x01c0_003f, 0x3f, 0]), + (0x8000_001d, Some(2), [0x143, 0x01c0_003f, 0x7ff, 0x2]), + (0x8000_001d, Some(3), [0x163, 0x03c0_003f, 0x7fff, 0x1]), + (0x8000_001d, Some(4), [0; 4]), + (0x8000_001e, None, [0; 4]), + (0x8000_001f, None, [0; 4]), + (0x8000_0020, None, [0; 4]), + (0x8000_0021, None, [0; 4]), +]; + +/// The processor features that WHP offers the host of [`GENOA_WHP_CPUID`], +/// as it does the Milan host: none of the TSA immunities. +pub(crate) const GENOA_WHP_HOST: HvFeatures = HvFeatures { + banks: [0x0602_0fcb_67f7_9fbf, 0x0000_0000_2000_01ed], + xsave: 0x50_381f, +}; + +/// Returns the fingerprint of the WHP host of [`GENOA_WHP_CPUID`], with its +/// processor features. +pub(crate) fn genoa_whp_fingerprint() -> CpuFingerprint { + let mut backend = BackendFingerprint::new("whp", "test", entries(&GENOA_WHP_CPUID)); + let [bank0, bank1] = GENOA_WHP_HOST.banks; + backend.set_feature_bank("whp.capability.ProcessorFeaturesBanks.bank0", bank0); + backend.set_feature_bank("whp.capability.ProcessorFeaturesBanks.bank1", bank1); + backend.set_feature_bank( + "whp.capability.ProcessorXsaveFeatures", + GENOA_WHP_HOST.xsave, + ); + backend_host_fingerprint(backend) +} + /// The CPUID of an AMD EPYC 9V45 (Turin: family 0x1a, model 2, stepping 1) /// as KVM reports it in an Azure VM with nested virtualization, a /// GitHub-hosted Actions runner, which `--cpu-fingerprint` recorded. That diff --git a/vmm_core/virt_whp/src/time_abi.rs b/vmm_core/virt_whp/src/time_abi.rs index 56066e274aa..b9d9e6e4fef 100644 --- a/vmm_core/virt_whp/src/time_abi.rs +++ b/vmm_core/virt_whp/src/time_abi.rs @@ -1279,12 +1279,13 @@ pub(crate) mod test_cpuid { use vm_topology::processor::x86::X2ApicState; /// Pinned CPU profiles of each vendor. - pub const PROFILES: [&str; 6] = [ + pub const PROFILES: [&str; 7] = [ "intel.skylake-sp.v1", "intel.icelake-sp.v1", "intel.emeraldrapids.v1", "amd.milan.v1", "amd.genoa.v1", + "amd.genoa.v2", "amd.turin.v1", ];