Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
17b494d
refactor(vmm): Reorder normalization of feature info leaf
zulinx86 Mar 13, 2025
7c8fc68
refactor(vmm): Use CPUID notation used in Intel SDM
zulinx86 Mar 13, 2025
7ebf98b
refactor(vmm): Use CPUID notation used in AMD APM
zulinx86 Mar 13, 2025
b805228
refactor(vmm): Remove unneeded constants
zulinx86 Mar 13, 2025
8bc742b
refactor(vmm): Refer to Intel SDM rather than Intel x2APIC spec
zulinx86 Mar 13, 2025
35230df
refactor(vmm): Update variable names and comments with SDM notation
zulinx86 Mar 13, 2025
7747883
refactor(vmm): Error if subleaf >= 2 given for leaf 0xB
zulinx86 Mar 13, 2025
b486b58
fix(vmm): Calc right-shift bits to address socket ID
zulinx86 Mar 13, 2025
1c009b5
refactor(vmm): Better error messages for CPUID normalization
zulinx86 Mar 13, 2025
a3bbd80
refactor(vmm): Bit-range manipulation with RangeInclusive
zulinx86 Mar 13, 2025
625c4f9
doc: Simplify normalized register list
zulinx86 Mar 13, 2025
9d58fc3
feat(vmm): Normalize CPUID leaf 0x1F
zulinx86 Mar 13, 2025
e11eb34
refactor: Remove unneeded turbo fish (::) in unit test
zulinx86 Mar 13, 2025
906e888
test: Skip frequency check if host not report it
zulinx86 Mar 13, 2025
30199d1
test: Skip CPUID.(EAX=1FH,ECX=2) in test_cpu_config_dump_vs_actual
zulinx86 Mar 13, 2025
bd5367e
test: Skip MSR 0xE1 in test_cpu_config_dump_vs_actual
zulinx86 Mar 13, 2025
71dfa63
test: Skip MSR 0x1C4 in test_cpu_config_dump_vs_actual
zulinx86 Mar 13, 2025
4b7b11a
test: Use any guest kernels in test_host_vs_guest_cpu_features
zulinx86 Mar 11, 2025
d35479a
test: test_host_vs_guest_cpu_features on Intel Sapphire Rapids
zulinx86 Mar 11, 2025
4dd97bb
test: Ignore REPTAR on Intel Sapphire Rapids
zulinx86 Mar 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 10 additions & 9 deletions docs/cpu_templates/cpuid-normalization.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,16 @@ See also: [boot protocol settings](boot-protocol.md)

## Intel-specific CPUID normalization

| Description | Leaf | Subleaf | Register | Bits |
| -------------------------------------------------------------- | :--------------------------------: | :-----: | :----------------: | :---: |
| Update deterministic cache parameters | 0x4 | all | EAX | 31:14 |
| Disable Intel Turbo Boost technology | 0x6 | - | EAX | 1 |
| Disable frequency selection | 0x6 | - | ECX | 3 |
| Set FDP_EXCPTN_ONLY bit | 0x7 | 0x0 | EBX | 6 |
| Set "Deprecates FPU CS and FPU DS values" bit | 0x7 | 0x0 | EBX | 13 |
| Disable performance monitoring | 0xa | - | EAX, EBX, ECX, EDX | all |
| Update brand string to use a default format and real frequency | 0x80000002, 0x80000003, 0x80000004 | - | EAX, EBX, ECX, EDX | all |
| Description | Leaf | Subleaf | Register | Bits |
| -------------------------------------------------------------- | :--------------------------------: | :-----: | :------: | :---: |
| Update deterministic cache parameters | 0x4 | all | EAX | 31:14 |
| Disable Intel Turbo Boost technology | 0x6 | - | EAX | 1 |
| Disable frequency selection | 0x6 | - | ECX | 3 |
| Set FDP_EXCPTN_ONLY bit | 0x7 | 0x0 | EBX | 6 |
| Set "Deprecates FPU CS and FPU DS values" bit | 0x7 | 0x0 | EBX | 13 |
| Disable performance monitoring | 0xa | - | all | all |
| Fill v2 extended topology enumeration leaf | 0x1f | all | all | all |
| Update brand string to use a default format and real frequency | 0x80000002, 0x80000003, 0x80000004 | - | all | all |

## AMD-specifc CPUID normalization

Expand Down
87 changes: 40 additions & 47 deletions src/vmm/src/cpu_config/x86_64/cpuid/amd/normalize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,33 +48,35 @@ pub enum PassthroughCacheTopologyError {
pub enum FeatureEntryError {
/// Missing leaf 0x80000008.
MissingLeaf0x80000008,
/// Failed to set `nt` (number of physical threads) due to overflow.
NumberOfPhysicalThreadsOverflow,
/// Failed to set `nt` (number of physical threads).
/// Failed to set number of physical threads (CPUID.80000008H:ECX[7:0]): {0}
NumberOfPhysicalThreads(CheckedAssignError),
/// Failed to set number of physical threads (CPUID.80000008H:ECX[7:0]) due to overflow.
NumberOfPhysicalThreadsOverflow,
}

/// Error type for setting leaf 0x8000001d section of [`super::AmdCpuid::normalize`].
#[derive(Debug, thiserror::Error, displaydoc::Display, Eq, PartialEq)]
pub enum ExtendedCacheTopologyError {
/// Missing leaf 0x8000001d.
MissingLeaf0x8000001d,
/// Failed to set `num_sharing_cache` due to overflow.
NumSharingCacheOverflow,
/// Failed to set `num_sharing_cache`: {0}
NumSharingCache(CheckedAssignError),
#[rustfmt::skip]
/// Failed to set number of logical processors sharing cache(CPUID.(EAX=8000001DH,ECX={0}):EAX[25:14]): {1}
NumSharingCache(u32, CheckedAssignError),
#[rustfmt::skip]
/// Failed to set number of logical processors sharing cache (CPUID.(EAX=8000001DH,ECX={0}):EAX[25:14]) due to overflow.
NumSharingCacheOverflow(u32),
}

/// Error type for setting leaf 0x8000001e section of [`super::AmdCpuid::normalize`].
#[derive(Debug, thiserror::Error, displaydoc::Display, Eq, PartialEq)]
pub enum ExtendedApicIdError {
/// Failed to set compute unit ID (CPUID.8000001EH:EBX[7:0]): {0}
ComputeUnitId(CheckedAssignError),
/// Failed to set extended APIC ID (CPUID.8000001EH:EAX[31:0]): {0}
ExtendedApicId(CheckedAssignError),
/// Missing leaf 0x8000001e.
MissingLeaf0x8000001e,
/// Failed to set `extended_apic_id`: {0}
ExtendedApicId(CheckedAssignError),
/// Failed to set `compute_unit_id`: {0}
ComputeUnitId(CheckedAssignError),
/// Failed to set `threads_per_compute_unit`: {0}
/// Failed to set threads per core unit (CPUID:8000001EH:EBX[15:8]): {0}
ThreadPerComputeUnit(CheckedAssignError),
}

Expand Down Expand Up @@ -153,6 +155,7 @@ impl super::AmdCpuid {
// On non-AMD hosts this condition may never be true thus this loop may be
// indefinite.

// CPUID Fn8000_0001D_EAX_x[4:0] (Field Name: CacheType)
// Cache type. Identifies the type of cache.
// ```text
// Bits Description
Expand All @@ -162,8 +165,6 @@ impl super::AmdCpuid {
// 03h Unified cache
// 1Fh-04h Reserved.
// ```
//
// cache_type: 0..4,
let cache_type = result.eax & 15;
if cache_type == 0 {
break;
Expand All @@ -186,10 +187,9 @@ impl super::AmdCpuid {
let leaf_80000001 = self
.get_mut(&CpuidKey::leaf(0x80000001))
.ok_or(NormalizeCpuidError::MissingLeaf0x80000001)?;
// CPUID Fn8000_0001_ECX[22] (Field Name: TopologyExtensions)
// Topology extensions support. Indicates support for CPUID Fn8000_001D_EAX_x[N:0]-CPUID
// Fn8000_001E_EDX.
//
// topology_extensions: 22,
set_bit(&mut leaf_80000001.result.ecx, 22, true);
Ok(())
}
Expand Down Expand Up @@ -220,6 +220,7 @@ impl super::AmdCpuid {
.get_mut(&CpuidKey::leaf(0x80000008))
.ok_or(FeatureEntryError::MissingLeaf0x80000008)?;

// CPUID Fn8000_0008_ECX[15:12] (Field Name: ApicIdSize)
// 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
Expand All @@ -228,19 +229,15 @@ impl super::AmdCpuid {
// 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,
set_range(&mut leaf_80000008.result.ecx, 12..16, THREAD_ID_MAX_SIZE).unwrap();
set_range(&mut leaf_80000008.result.ecx, 12..=15, THREAD_ID_MAX_SIZE).unwrap();

// CPUID Fn8000_0008_ECX[7:0] (Field Name: NC)
// 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,
//
let sub = cpu_count
.checked_sub(1)
.ok_or(FeatureEntryError::NumberOfPhysicalThreadsOverflow)?;
set_range(&mut leaf_80000008.result.ecx, 0..8, u32::from(sub))
set_range(&mut leaf_80000008.result.ecx, 0..=7, u32::from(sub))
.map_err(FeatureEntryError::NumberOfPhysicalThreads)?;

Ok(())
Expand All @@ -255,6 +252,7 @@ impl super::AmdCpuid {
) -> Result<(), ExtendedCacheTopologyError> {
for i in 0.. {
if let Some(subleaf) = self.get_mut(&CpuidKey::subleaf(0x8000001d, i)) {
// CPUID Fn8000_001D_EAX_x[7:5] (Field Name: CacheLevel)
// Cache level. Identifies the level of this cache. Note that the enumeration value
// is not necessarily equal to the cache level.
// ```text
Expand All @@ -265,10 +263,9 @@ impl super::AmdCpuid {
// 011b Level 3
// 111b-100b Reserved.
// ```
//
// cache_level: 5..8
let cache_level = get_range(subleaf.result.eax, 5..8);
let cache_level = get_range(subleaf.result.eax, 5..=7);

// CPUID Fn8000_001D_EAX_x[25:14] (Field Name: NumSharingCache)
// 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
Expand All @@ -279,26 +276,24 @@ impl super::AmdCpuid {
//
// 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,

match cache_level {
// L1 & L2 Cache
// The L1 & L2 cache is shared by at most 2 hyper-threads
1 | 2 => {
// SAFETY: We know `cpus_per_core > 0` therefore this is always safe.
let sub = u32::from(cpus_per_core.checked_sub(1).unwrap());
set_range(&mut subleaf.result.eax, 14..26, sub)
.map_err(ExtendedCacheTopologyError::NumSharingCache)?;
set_range(&mut subleaf.result.eax, 14..=25, sub)
.map_err(|err| ExtendedCacheTopologyError::NumSharingCache(i, err))?;
}
// L3 Cache
// The L3 cache is shared among all the logical threads
3 => {
let sub = cpu_count
.checked_sub(1)
.ok_or(ExtendedCacheTopologyError::NumSharingCacheOverflow)?;
set_range(&mut subleaf.result.eax, 14..26, u32::from(sub))
.map_err(ExtendedCacheTopologyError::NumSharingCache)?;
.ok_or(ExtendedCacheTopologyError::NumSharingCacheOverflow(i))?;
set_range(&mut subleaf.result.eax, 14..=25, u32::from(sub))
.map_err(|err| ExtendedCacheTopologyError::NumSharingCache(i, err))?;
}
_ => (),
}
Expand Down Expand Up @@ -334,16 +329,18 @@ impl super::AmdCpuid {
.get_mut(&CpuidKey::leaf(0x8000001e))
.ok_or(ExtendedApicIdError::MissingLeaf0x8000001e)?;

// CPUID Fn8000_001E_EAX[31:0] (Field Name: ExtendedApicId)
// Extended APIC ID. If MSR0000_001B[ApicEn] = 0, this field is reserved.
//
// extended_apic_id: 0..32,
set_range(&mut leaf_8000001e.result.eax, 0..32, u32::from(cpu_index))
set_range(&mut leaf_8000001e.result.eax, 0..=31, u32::from(cpu_index))
.map_err(ExtendedApicIdError::ExtendedApicId)?;

// compute_unit_id: 0..8,
set_range(&mut leaf_8000001e.result.ebx, 0..8, core_id)
// CPUID Fn8000_001E_EBX[7:0] (Field Name: ComputeUnitId)
// Compute unit ID. Identifies a Compute Unit, which may be one or more physical cores that
// each implement one or more logical processors.
set_range(&mut leaf_8000001e.result.ebx, 0..=7, core_id)
.map_err(ExtendedApicIdError::ComputeUnitId)?;

// CPUID Fn8000_001E_EBX[15:8] (Field Name: ThreadsPerComputeUnit)
// 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
Expand All @@ -355,30 +352,26 @@ impl super::AmdCpuid {
// 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,
//
// SAFETY: We know `cpus_per_core > 0` therefore this is always safe.
let sub = u32::from(cpus_per_core.checked_sub(1).unwrap());
set_range(&mut leaf_8000001e.result.ebx, 8..16, sub)
set_range(&mut leaf_8000001e.result.ebx, 8..=15, sub)
.map_err(ExtendedApicIdError::ThreadPerComputeUnit)?;

// CPUID Fn8000_001E_ECX[10:8] (Field Name: NodesPerProcessor)
// 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..11,
//
// SAFETY: We know the value always fits within the range and thus is always safe.
// Set nodes per processor.
set_range(&mut leaf_8000001e.result.ecx, 8..11, NODES_PER_PROCESSOR).unwrap();
set_range(&mut leaf_8000001e.result.ecx, 8..=10, NODES_PER_PROCESSOR).unwrap();

// CPUID Fn8000_001E_ECX[7:0] (Field Name: NodeId)
// Specifies the ID of the node containing the current logical processor. NodeId
// values are unique across the system.
//
// node_id: 0..8,
//
// Put all the cpus in the same node.
set_range(&mut leaf_8000001e.result.ecx, 0..8, 0).unwrap();
set_range(&mut leaf_8000001e.result.ecx, 0..=7, 0).unwrap();

Ok(())
}
Expand Down
Loading