1use crate::CpuidLeaf;
7use std::cmp::min;
8use thiserror::Error;
9use vm_topology::processor::ProcessorTopology;
10use x86defs::cpuid::CacheParametersEax;
11use x86defs::cpuid::CpuidFunction;
12use x86defs::cpuid::ExtendedAddressSpaceSizesEcx;
13use x86defs::cpuid::ExtendedTopologyEax;
14use x86defs::cpuid::ExtendedTopologyEbx;
15use x86defs::cpuid::ExtendedTopologyEcx;
16use x86defs::cpuid::ProcessorTopologyDefinitionEbx;
17use x86defs::cpuid::ProcessorTopologyDefinitionEcx;
18use x86defs::cpuid::TopologyLevelType;
19use x86defs::cpuid::Vendor;
20use x86defs::cpuid::VendorAndMaxFunctionEax;
21use x86defs::cpuid::VersionAndFeaturesEbx;
22
23pub type CpuidFn<'a> = &'a dyn Fn(u32, u32) -> [u32; 4];
26
27#[derive(Debug, Error)]
28#[error("unknown processor vendor {0}")]
29pub struct UnknownVendor(Vendor);
30
31pub fn topology_cpuid<'a>(
36 topology: &'a ProcessorTopology,
37 cpuid: CpuidFn<'a>,
38 leaves: &mut Vec<CpuidLeaf>,
39) -> Result<(), UnknownVendor> {
40 let result = cpuid(CpuidFunction::VendorAndMaxFunction.0, 0);
41 let max = VendorAndMaxFunctionEax::from(result[0]).max_function();
42 let vendor = Vendor::from_ebx_ecx_edx(result[1], result[2], result[3]);
43 if !vendor.is_intel_compatible() && !vendor.is_amd_compatible() {
44 return Err(UnknownVendor(vendor));
45 };
46
47 leaves.push(
49 CpuidLeaf::new(
50 CpuidFunction::VersionAndFeatures.0,
51 [
52 0,
53 VersionAndFeaturesEbx::new()
54 .with_lps_per_package(topology.reserved_vps_per_socket() as u8)
55 .into(),
56 0,
57 0,
58 ],
59 )
60 .masked([
61 0,
62 VersionAndFeaturesEbx::new()
63 .with_lps_per_package(0xff)
64 .into(),
65 0,
66 0,
67 ]),
68 );
69
70 if vendor.is_intel_compatible() {
72 cache_parameters_cpuid(topology, cpuid, leaves);
73 }
74
75 extended_topology_cpuid(topology, CpuidFunction::ExtendedTopologyEnumeration, leaves);
77
78 if max >= CpuidFunction::V2ExtendedTopologyEnumeration.0 {
80 extended_topology_cpuid(
81 topology,
82 CpuidFunction::V2ExtendedTopologyEnumeration,
83 leaves,
84 );
85 }
86
87 if vendor.is_amd_compatible() {
88 amd_extended_address_space_sizes_cpuid(topology, leaves);
90 amd_processor_topology_definition_cpuid(topology, leaves);
91 }
92
93 Ok(())
94}
95
96fn cache_parameters_cpuid(
100 topology: &ProcessorTopology,
101 cpuid: CpuidFn<'_>,
102 leaves: &mut Vec<CpuidLeaf>,
103) {
104 for i in 0..=255 {
105 let result = cpuid(CpuidFunction::CacheParameters.0, i);
106 if result == [0; 4] {
107 break;
108 }
109 let mut eax = CacheParametersEax::new();
110 const MAX_CORES_PER_SOCKET_MINUS_ONE: u32 = 0b111111;
113 if topology.smt_enabled() {
114 eax.set_cores_per_socket_minus_one(min(
115 MAX_CORES_PER_SOCKET_MINUS_ONE,
116 topology.reserved_vps_per_socket() / 2 - 1,
117 ));
118 eax.set_threads_sharing_cache_minus_one(1);
119 } else {
120 eax.set_cores_per_socket_minus_one(min(
121 MAX_CORES_PER_SOCKET_MINUS_ONE,
122 topology.reserved_vps_per_socket() - 1,
123 ));
124 eax.set_threads_sharing_cache_minus_one(0);
125 }
126
127 if eax.cache_level() == 3 {
129 eax.set_threads_sharing_cache_minus_one(topology.reserved_vps_per_socket() - 1);
130 }
131
132 let eax_mask = CacheParametersEax::new()
133 .with_cores_per_socket_minus_one(0x3f)
134 .with_threads_sharing_cache_minus_one(0xfff);
135
136 leaves.push(
137 CpuidLeaf::new(CpuidFunction::CacheParameters.0, [eax.into(), 0, 0, 0]).masked([
138 eax_mask.into(),
139 0,
140 0,
141 0,
142 ]),
143 )
144 }
145}
146
147fn extended_topology_cpuid(
152 topology: &ProcessorTopology,
153 function: CpuidFunction,
154 leaves: &mut Vec<CpuidLeaf>,
155) {
156 assert!(
157 function == CpuidFunction::ExtendedTopologyEnumeration
158 || function == CpuidFunction::V2ExtendedTopologyEnumeration
159 );
160 for (index, (level_type, num_lps)) in [
161 (
162 TopologyLevelType::SMT,
163 if topology.smt_enabled() { 2 } else { 1 },
164 ),
165 (TopologyLevelType::CORE, topology.reserved_vps_per_socket()),
166 ]
167 .into_iter()
168 .enumerate()
169 {
170 if level_type <= TopologyLevelType::CORE
171 || function == CpuidFunction::V2ExtendedTopologyEnumeration
172 {
173 let eax = ExtendedTopologyEax::new().with_x2_apic_shift(num_lps.trailing_zeros());
174 let ebx = ExtendedTopologyEbx::new().with_num_lps(num_lps as u16);
175 let ecx = ExtendedTopologyEcx::new()
176 .with_level_number(index as u8)
177 .with_level_type(level_type.0);
178
179 leaves.push(
182 CpuidLeaf::new(function.0, [eax.into(), ebx.into(), ecx.into(), 0])
183 .indexed(index as u32)
184 .masked([!0, !0, !0, 0]),
185 );
186 }
187 }
188}
189
190fn amd_extended_address_space_sizes_cpuid(
194 topology: &ProcessorTopology,
195 leaves: &mut Vec<CpuidLeaf>,
196) {
197 let nc = (topology.reserved_vps_per_socket() - 1) as u8;
198 let apic_core_id_size = topology.reserved_vps_per_socket().trailing_zeros() as u8;
199 let ecx = ExtendedAddressSpaceSizesEcx::new()
200 .with_nc(nc)
201 .with_apic_core_id_size(apic_core_id_size);
202
203 let ecx_mask = ExtendedAddressSpaceSizesEcx::new()
204 .with_nc(0xff)
205 .with_apic_core_id_size(0xf);
206
207 leaves.push(
208 CpuidLeaf::new(
209 CpuidFunction::ExtendedAddressSpaceSizes.0,
210 [0, 0, ecx.into(), 0],
211 )
212 .masked([0, 0, ecx_mask.into(), 0]),
213 );
214}
215
216fn amd_processor_topology_definition_cpuid(
218 topology: &ProcessorTopology,
219 leaves: &mut Vec<CpuidLeaf>,
220) {
221 let threads_per_compute_unit = if topology.smt_enabled() { 1 } else { 0 };
223 let ebx = ProcessorTopologyDefinitionEbx::new()
224 .with_threads_per_compute_unit(threads_per_compute_unit);
225
226 let ebx_mask = ProcessorTopologyDefinitionEbx::new().with_threads_per_compute_unit(!0);
227
228 let ecx = ProcessorTopologyDefinitionEcx::new().with_nodes_per_processor(0);
230 let ecx_mask = ProcessorTopologyDefinitionEcx::new().with_nodes_per_processor(0x7);
231
232 leaves.push(
233 CpuidLeaf::new(
234 CpuidFunction::ProcessorTopologyDefinition.0,
235 [0, ebx.into(), ecx.into(), 0],
236 )
237 .masked([0, ebx_mask.into(), ecx_mask.into(), 0]),
238 );
239}