Skip to content

Commit 8e4c363

Browse files
Serban Iorgaacatangiu
authored andcommitted
AMD cpuid improvements
- set the extended apic id - fix the number of threads per core - set the number of nodes per processor Signed-off-by: Serban Iorga <[email protected]>
1 parent f13dd57 commit 8e4c363

File tree

3 files changed

+181
-13
lines changed

3 files changed

+181
-13
lines changed

cpuid/src/cpu_leaf.rs

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -88,14 +88,6 @@ pub mod leaf_0x4 {
8888
}
8989
}
9090

91-
// Extended Cache Topology Leaf
92-
pub mod leaf_0x8000001d {
93-
pub const LEAF_NUM: u32 = 0x8000_001d;
94-
95-
// inherit eax from leaf_cache_parameters
96-
pub use cpu_leaf::leaf_cache_parameters::eax;
97-
}
98-
9991
// Thermal and Power Management Leaf
10092
pub mod leaf_0x6 {
10193
pub const LEAF_NUM: u32 = 0x6;
@@ -250,3 +242,42 @@ pub mod leaf_0x80000008 {
250242
pub const NUM_THREADS_BITRANGE: BitRange = bit_range!(7, 0);
251243
}
252244
}
245+
246+
// Extended Cache Topology Leaf
247+
pub mod leaf_0x8000001d {
248+
pub const LEAF_NUM: u32 = 0x8000_001d;
249+
250+
// inherit eax from leaf_cache_parameters
251+
pub use cpu_leaf::leaf_cache_parameters::eax;
252+
}
253+
254+
// Extended APIC ID Leaf
255+
pub mod leaf_0x8000001e {
256+
pub const LEAF_NUM: u32 = 0x8000_001e;
257+
258+
pub mod eax {
259+
use bit_helper::BitRange;
260+
261+
pub const EXTENDED_APIC_ID_BITRANGE: BitRange = bit_range!(31, 0);
262+
}
263+
264+
pub mod ebx {
265+
use bit_helper::BitRange;
266+
267+
// The number of threads per core - 1
268+
pub const THREADS_PER_CORE_BITRANGE: BitRange = bit_range!(15, 8);
269+
pub const CORE_ID_BITRANGE: BitRange = bit_range!(7, 0);
270+
}
271+
272+
pub mod ecx {
273+
use bit_helper::BitRange;
274+
275+
// The number of nodes per processor. Possible values:
276+
// 0 -> 1 node per processor
277+
// 1 -> 2 nodes per processor
278+
// 2 -> Reserved
279+
// 3 -> 4 nodes per processor
280+
pub const NODES_PER_PROCESSOR_BITRANGE: BitRange = bit_range!(10, 8);
281+
pub const NODE_ID_BITRANGE: BitRange = bit_range!(7, 0);
282+
}
283+
}

cpuid/src/transformer/amd.rs

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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
1717
const 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

1922
pub 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+
87130
pub struct AmdCpuidTransformer {}
88131

89132
impl 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
}

cpuid/src/transformer/common.rs

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,11 @@ pub fn update_cache_parameters_entry(
9696

9797
/// Replaces the `cpuid` entries corresponding to `function` with the entries from the host's cpuid.
9898
///
99-
pub fn use_host_cpuid_function(cpuid: &mut CpuId, function: u32) -> Result<(), Error> {
99+
pub fn use_host_cpuid_function(
100+
cpuid: &mut CpuId,
101+
function: u32,
102+
use_count: bool,
103+
) -> Result<(), Error> {
100104
// copy all the CpuId entries, except for the ones with the provided function
101105
let mut entries: Vec<kvm_cpuid_entry2> = Vec::new();
102106
for entry in cpuid.mut_entries_slice().iter() {
@@ -108,6 +112,9 @@ pub fn use_host_cpuid_function(cpuid: &mut CpuId, function: u32) -> Result<(), E
108112
// add all the host leaves with the provided function
109113
let mut count: u32 = 0;
110114
while let Ok(entry) = get_cpuid(function, count) {
115+
if count > 0 && !use_count {
116+
break;
117+
}
111118
// check if there's enough space to add a new entry to the cpuid
112119
if entries.len() == MAX_KVM_CPUID_ENTRIES {
113120
return Err(Error::SizeLimitExceeded);
@@ -254,14 +261,14 @@ mod test {
254261

255262
#[test]
256263
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
257-
fn use_host_cpuid_function_test() {
264+
fn test_use_host_cpuid_function_with_count() {
258265
// try to emulate the extended cache topology leaves
259266
let topoext_fn = get_topoext_fn();
260267

261268
// check that it behaves correctly for TOPOEXT function
262269
let mut cpuid = CpuId::new(1);
263270
cpuid.mut_entries_slice()[0].function = topoext_fn;
264-
assert!(use_host_cpuid_function(&mut cpuid, topoext_fn).is_ok());
271+
assert!(use_host_cpuid_function(&mut cpuid, topoext_fn, true).is_ok());
265272
let entries = cpuid.mut_entries_slice();
266273
assert!(entries.len() > 1);
267274
let mut count = 0;
@@ -271,10 +278,35 @@ mod test {
271278
assert!(entry.eax != 0);
272279
count = count + 1;
273280
}
281+
}
274282

283+
#[test]
284+
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
285+
fn test_use_host_cpuid_function_without_count() {
286+
use cpu_leaf::leaf_0x1::*;
287+
// try to emulate the extended cache topology leaves
288+
let feature_info_fn = LEAF_NUM;
289+
290+
// check that it behaves correctly for TOPOEXT function
291+
let mut cpuid = CpuId::new(1);
292+
cpuid.mut_entries_slice()[0].function = feature_info_fn;
293+
assert!(use_host_cpuid_function(&mut cpuid, feature_info_fn, false).is_ok());
294+
let entries = cpuid.mut_entries_slice();
295+
assert!(entries.len() == 1);
296+
let entry = entries[0];
297+
298+
assert!(entry.function == feature_info_fn);
299+
assert!(entry.index == 0);
300+
assert!(entry.eax != 0);
301+
}
302+
303+
#[test]
304+
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
305+
fn test_use_host_cpuid_function_err() {
306+
let topoext_fn = get_topoext_fn();
275307
// check that it returns Err when there are too many entriesentry.function == topoext_fn
276308
let mut cpuid = CpuId::new(MAX_KVM_CPUID_ENTRIES);
277-
match use_host_cpuid_function(&mut cpuid, topoext_fn) {
309+
match use_host_cpuid_function(&mut cpuid, topoext_fn, true) {
278310
Err(Error::SizeLimitExceeded) => {}
279311
_ => panic!("Wrong behavior"),
280312
}

0 commit comments

Comments
 (0)