@@ -15,6 +15,9 @@ const LARGEST_EXTENDED_FN: u32 = 0x8000_001f;
1515// This value allows at most 64 logical threads within a package.
1616// See also the documentation for leaf_0x80000008::ecx::THREAD_ID_SIZE_BITRANGE
1717const THREAD_ID_MAX_SIZE : u32 = 6 ;
18+ // This value means there is 1 node per processor.
19+ // See also the documentation for leaf_0x8000001e::ecx::NODES_PER_PROCESSOR_BITRANGE.
20+ const NODES_PER_PROCESSOR : u32 = 0 ;
1821
1922pub fn update_structured_extended_entry (
2023 entry : & mut kvm_cpuid_entry2 ,
@@ -84,11 +87,52 @@ pub fn update_extended_cache_topology_entry(
8487 common:: update_cache_parameters_entry ( entry, vm_spec)
8588}
8689
90+ pub fn update_extended_apic_id_entry (
91+ entry : & mut kvm_cpuid_entry2 ,
92+ vm_spec : & VmSpec ,
93+ ) -> Result < ( ) , Error > {
94+ use cpu_leaf:: leaf_0x8000001e:: * ;
95+
96+ let mut core_id = u32:: from ( vm_spec. cpu_id ) ;
97+ // When hyper-threading is enabled each pair of 2 consecutive logical CPUs
98+ // will have the same core id since they represent 2 threads in the same core.
99+ // For Example:
100+ // logical CPU 0 -> core id: 0
101+ // logical CPU 1 -> core id: 0
102+ // logical CPU 2 -> core id: 1
103+ // logical CPU 3 -> core id: 1
104+ if vm_spec. ht_enabled {
105+ core_id /= 2 ;
106+ }
107+
108+ entry
109+ . eax
110+ // the Extended APIC ID is the id of the current logical CPU
111+ . write_bits_in_range ( & eax:: EXTENDED_APIC_ID_BITRANGE , u32:: from ( vm_spec. cpu_id ) ) ;
112+
113+ entry
114+ . ebx
115+ . write_bits_in_range ( & ebx:: CORE_ID_BITRANGE , core_id)
116+ . write_bits_in_range (
117+ & ebx:: THREADS_PER_CORE_BITRANGE ,
118+ u32:: from ( vm_spec. ht_enabled ) ,
119+ ) ;
120+
121+ entry
122+ . ecx
123+ . write_bits_in_range ( & ecx:: NODES_PER_PROCESSOR_BITRANGE , NODES_PER_PROCESSOR )
124+ // Put all the cpus in the same node.
125+ . write_bits_in_range ( & ecx:: NODE_ID_BITRANGE , 0 ) ;
126+
127+ Ok ( ( ) )
128+ }
129+
87130pub struct AmdCpuidTransformer { }
88131
89132impl CpuidTransformer for AmdCpuidTransformer {
90133 fn preprocess_cpuid ( & self , cpuid : & mut CpuId ) -> Result < ( ) , Error > {
91- use_host_cpuid_function ( cpuid, leaf_0x8000001d:: LEAF_NUM )
134+ use_host_cpuid_function ( cpuid, leaf_0x8000001e:: LEAF_NUM , false ) ?;
135+ use_host_cpuid_function ( cpuid, leaf_0x8000001d:: LEAF_NUM , true )
92136 }
93137
94138 fn transform_entry ( & self , entry : & mut kvm_cpuid_entry2 , vm_spec : & VmSpec ) -> Result < ( ) , Error > {
@@ -99,6 +143,7 @@ impl CpuidTransformer for AmdCpuidTransformer {
99143 leaf_0x80000001:: LEAF_NUM => Some ( amd:: update_extended_feature_info_entry) ,
100144 leaf_0x80000008:: LEAF_NUM => Some ( amd:: update_amd_features_entry) ,
101145 leaf_0x8000001d:: LEAF_NUM => Some ( amd:: update_extended_cache_topology_entry) ,
146+ leaf_0x8000001e:: LEAF_NUM => Some ( amd:: update_extended_apic_id_entry) ,
102147 0x8000_0002 ..=0x8000_0004 => Some ( common:: update_brand_string_entry) ,
103148 _ => None ,
104149 } ;
@@ -216,6 +261,56 @@ mod test {
216261 ) ;
217262 }
218263
264+ fn check_update_extended_apic_id_entry (
265+ cpu_id : u8 ,
266+ cpu_count : u8 ,
267+ ht_enabled : bool ,
268+ expected_core_id : u32 ,
269+ expected_threads_per_core : u32 ,
270+ ) {
271+ use cpu_leaf:: leaf_0x8000001e:: * ;
272+
273+ let vm_spec = VmSpec :: new ( VENDOR_ID_AMD , cpu_id, cpu_count, ht_enabled) ;
274+ let mut entry = & mut kvm_cpuid_entry2 {
275+ function : LEAF_NUM ,
276+ index : 0 ,
277+ flags : 0 ,
278+ eax : 0 ,
279+ ebx : 0 ,
280+ ecx : 0 ,
281+ edx : 0 ,
282+ padding : [ 0 , 0 , 0 ] ,
283+ } ;
284+
285+ assert ! ( update_extended_apic_id_entry( & mut entry, & vm_spec) . is_ok( ) ) ;
286+
287+ assert_eq ! (
288+ entry
289+ . eax
290+ . read_bits_in_range( & eax:: EXTENDED_APIC_ID_BITRANGE ) ,
291+ u32 :: from( cpu_id)
292+ ) ;
293+
294+ assert_eq ! (
295+ entry. ebx. read_bits_in_range( & ebx:: CORE_ID_BITRANGE ) ,
296+ expected_core_id
297+ ) ;
298+ assert_eq ! (
299+ entry
300+ . ebx
301+ . read_bits_in_range( & ebx:: THREADS_PER_CORE_BITRANGE ) ,
302+ expected_threads_per_core
303+ ) ;
304+
305+ assert_eq ! (
306+ entry
307+ . ecx
308+ . read_bits_in_range( & ecx:: NODES_PER_PROCESSOR_BITRANGE ) ,
309+ NODES_PER_PROCESSOR
310+ ) ;
311+ assert_eq ! ( entry. ecx. read_bits_in_range( & ecx:: NODE_ID_BITRANGE ) , 0 ) ;
312+ }
313+
219314 #[ test]
220315 fn test_update_extended_cache_topology_entry ( ) {
221316 let vm_spec = VmSpec :: new ( VENDOR_ID_AMD , 0 , 1 , false ) ;
@@ -238,20 +333,30 @@ mod test {
238333 #[ test]
239334 fn test_1vcpu_ht_off ( ) {
240335 check_update_amd_features_entry ( 1 , false ) ;
336+
337+ check_update_extended_apic_id_entry ( 0 , 1 , false , 0 , 0 ) ;
241338 }
242339
243340 #[ test]
244341 fn test_1vcpu_ht_on ( ) {
245342 check_update_amd_features_entry ( 1 , true ) ;
343+
344+ check_update_extended_apic_id_entry ( 0 , 1 , true , 0 , 1 ) ;
246345 }
247346
248347 #[ test]
249348 fn test_2vcpu_ht_off ( ) {
250349 check_update_amd_features_entry ( 2 , false ) ;
350+
351+ check_update_extended_apic_id_entry ( 0 , 2 , false , 0 , 0 ) ;
352+ check_update_extended_apic_id_entry ( 1 , 2 , false , 1 , 0 ) ;
251353 }
252354
253355 #[ test]
254356 fn test_2vcpu_ht_on ( ) {
255357 check_update_amd_features_entry ( 2 , true ) ;
358+
359+ check_update_extended_apic_id_entry ( 0 , 2 , true , 0 , 1 ) ;
360+ check_update_extended_apic_id_entry ( 1 , 2 , true , 0 , 1 ) ;
256361 }
257362}
0 commit comments