use super::*;
use kvm_bindings::{CpuId, KVM_CPUID_FLAG_SIGNIFCANT_INDEX};
use crate::bit_helper::BitHelper;
use crate::cpu_leaf::*;
use crate::transformer::common::use_host_cpuid_function;
const LARGEST_EXTENDED_FN: u32 = 0x8000_001f;
const THREAD_ID_MAX_SIZE: u32 = 6;
const NODES_PER_PROCESSOR: u32 = 0;
pub fn update_structured_extended_entry(
entry: &mut kvm_cpuid_entry2,
_vm_spec: &VmSpec,
) -> Result<(), Error> {
use crate::cpu_leaf::leaf_0x7::index0::*;
if entry.index == 0 {
entry.edx.write_bit(edx::ARCH_CAPABILITIES_BITINDEX, false);
}
Ok(())
}
pub fn update_largest_extended_fn_entry(
entry: &mut kvm_cpuid_entry2,
_vm_spec: &VmSpec,
) -> Result<(), Error> {
use crate::cpu_leaf::leaf_0x80000000::*;
entry
.eax
.write_bits_in_range(&eax::LARGEST_EXTENDED_FN_BITRANGE, LARGEST_EXTENDED_FN);
Ok(())
}
pub fn update_extended_feature_info_entry(
entry: &mut kvm_cpuid_entry2,
_vm_spec: &VmSpec,
) -> Result<(), Error> {
use crate::cpu_leaf::leaf_0x80000001::*;
entry.ecx.write_bit(ecx::TOPOEXT_INDEX, true);
Ok(())
}
pub fn update_amd_features_entry(
entry: &mut kvm_cpuid_entry2,
vm_spec: &VmSpec,
) -> Result<(), Error> {
use crate::cpu_leaf::leaf_0x80000008::*;
entry
.ecx
.write_bits_in_range(&ecx::THREAD_ID_SIZE_BITRANGE, THREAD_ID_MAX_SIZE)
.write_bits_in_range(&ecx::NUM_THREADS_BITRANGE, u32::from(vm_spec.cpu_count - 1));
Ok(())
}
pub fn update_extended_cache_topology_entry(
entry: &mut kvm_cpuid_entry2,
vm_spec: &VmSpec,
) -> Result<(), Error> {
entry.flags |= KVM_CPUID_FLAG_SIGNIFCANT_INDEX;
common::update_cache_parameters_entry(entry, vm_spec)
}
pub fn update_extended_apic_id_entry(
entry: &mut kvm_cpuid_entry2,
vm_spec: &VmSpec,
) -> Result<(), Error> {
use crate::cpu_leaf::leaf_0x8000001e::*;
let mut core_id = u32::from(vm_spec.cpu_id);
if vm_spec.ht_enabled {
core_id /= 2;
}
entry
.eax
.write_bits_in_range(&eax::EXTENDED_APIC_ID_BITRANGE, u32::from(vm_spec.cpu_id));
entry
.ebx
.write_bits_in_range(&ebx::CORE_ID_BITRANGE, core_id)
.write_bits_in_range(
&ebx::THREADS_PER_CORE_BITRANGE,
u32::from(vm_spec.ht_enabled),
);
entry
.ecx
.write_bits_in_range(&ecx::NODES_PER_PROCESSOR_BITRANGE, NODES_PER_PROCESSOR)
.write_bits_in_range(&ecx::NODE_ID_BITRANGE, 0);
Ok(())
}
pub fn update_feature_info_entry(
entry: &mut kvm_cpuid_entry2,
vm_spec: &VmSpec,
) -> Result<(), Error> {
use crate::cpu_leaf::leaf_0x1::*;
common::update_feature_info_entry(entry, vm_spec)?;
entry.ecx.write_bit(ecx::TSC_DEADLINE_TIMER_BITINDEX, true);
Ok(())
}
pub struct AmdCpuidTransformer {}
impl CpuidTransformer for AmdCpuidTransformer {
fn process_cpuid(&self, cpuid: &mut CpuId, vm_spec: &VmSpec) -> Result<(), Error> {
use_host_cpuid_function(cpuid, leaf_0x8000001d::LEAF_NUM, false)?;
use_host_cpuid_function(cpuid, leaf_0x8000001d::LEAF_NUM, true)?;
self.process_entries(cpuid, vm_spec)
}
fn entry_transformer_fn(&self, entry: &mut kvm_cpuid_entry2) -> Option<EntryTransformerFn> {
match entry.function {
leaf_0x1::LEAF_NUM => Some(amd::update_feature_info_entry),
leaf_0x7::LEAF_NUM => Some(amd::update_structured_extended_entry),
leaf_0x80000000::LEAF_NUM => Some(amd::update_largest_extended_fn_entry),
leaf_0x80000001::LEAF_NUM => Some(amd::update_extended_feature_info_entry),
leaf_0x80000008::LEAF_NUM => Some(amd::update_amd_features_entry),
leaf_0x8000001d::LEAF_NUM => Some(amd::update_extended_cache_topology_entry),
leaf_0x8000001e::LEAF_NUM => Some(amd::update_extended_apic_id_entry),
0x8000_0002..=0x8000_0004 => Some(common::update_brand_string_entry),
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_update_structured_extended_entry() {
use crate::cpu_leaf::leaf_0x7::index0::*;
let vm_spec = VmSpec::new(0, 1, false).expect("Error creating vm_spec");
let mut entry = kvm_cpuid_entry2 {
function: leaf_0x7::LEAF_NUM,
index: 0,
flags: 0,
eax: 0,
ebx: 0,
ecx: 0,
edx: *(0_u32).write_bit(edx::ARCH_CAPABILITIES_BITINDEX, true),
padding: [0, 0, 0],
};
assert!(update_structured_extended_entry(&mut entry, &vm_spec).is_ok());
assert!(!entry.edx.read_bit(edx::ARCH_CAPABILITIES_BITINDEX));
entry.index = 1;
entry.edx.write_bit(edx::ARCH_CAPABILITIES_BITINDEX, true);
assert!(update_structured_extended_entry(&mut entry, &vm_spec).is_ok());
assert!(entry.edx.read_bit(edx::ARCH_CAPABILITIES_BITINDEX));
}
#[test]
fn test_update_largest_extended_fn_entry() {
use crate::cpu_leaf::leaf_0x80000000::*;
let vm_spec = VmSpec::new(0, 1, false).expect("Error creating vm_spec");
let mut entry = kvm_cpuid_entry2 {
function: LEAF_NUM,
index: 0,
flags: 0,
eax: 0,
ebx: 0,
ecx: 0,
edx: 0,
padding: [0, 0, 0],
};
assert!(update_largest_extended_fn_entry(&mut entry, &vm_spec).is_ok());
assert_eq!(
entry
.eax
.read_bits_in_range(&eax::LARGEST_EXTENDED_FN_BITRANGE),
LARGEST_EXTENDED_FN
);
}
#[test]
fn test_update_extended_feature_info_entry() {
use crate::cpu_leaf::leaf_0x80000001::*;
let vm_spec = VmSpec::new(0, 1, false).expect("Error creating vm_spec");
let mut entry = kvm_cpuid_entry2 {
function: LEAF_NUM,
index: 0,
flags: 0,
eax: 0,
ebx: 0,
ecx: 0,
edx: 0,
padding: [0, 0, 0],
};
assert!(update_extended_feature_info_entry(&mut entry, &vm_spec).is_ok());
assert!(entry.ecx.read_bit(ecx::TOPOEXT_INDEX));
}
fn check_update_amd_features_entry(cpu_count: u8, ht_enabled: bool) {
use crate::cpu_leaf::leaf_0x80000008::*;
let vm_spec = VmSpec::new(0, cpu_count, ht_enabled).expect("Error creating vm_spec");
let mut entry = kvm_cpuid_entry2 {
function: LEAF_NUM,
index: 0,
flags: 0,
eax: 0,
ebx: 0,
ecx: 0,
edx: 0,
padding: [0, 0, 0],
};
assert!(update_amd_features_entry(&mut entry, &vm_spec).is_ok());
assert_eq!(
entry.ecx.read_bits_in_range(&ecx::NUM_THREADS_BITRANGE),
u32::from(cpu_count - 1)
);
assert_eq!(
entry.ecx.read_bits_in_range(&ecx::THREAD_ID_SIZE_BITRANGE),
THREAD_ID_MAX_SIZE
);
}
fn check_update_extended_apic_id_entry(
cpu_id: u8,
cpu_count: u8,
ht_enabled: bool,
expected_core_id: u32,
expected_threads_per_core: u32,
) {
use crate::cpu_leaf::leaf_0x8000001e::*;
let vm_spec = VmSpec::new(cpu_id, cpu_count, ht_enabled).expect("Error creating vm_spec");
let mut entry = kvm_cpuid_entry2 {
function: LEAF_NUM,
index: 0,
flags: 0,
eax: 0,
ebx: 0,
ecx: 0,
edx: 0,
padding: [0, 0, 0],
};
assert!(update_extended_apic_id_entry(&mut entry, &vm_spec).is_ok());
assert_eq!(
entry
.eax
.read_bits_in_range(&eax::EXTENDED_APIC_ID_BITRANGE),
u32::from(cpu_id)
);
assert_eq!(
entry.ebx.read_bits_in_range(&ebx::CORE_ID_BITRANGE),
expected_core_id
);
assert_eq!(
entry
.ebx
.read_bits_in_range(&ebx::THREADS_PER_CORE_BITRANGE),
expected_threads_per_core
);
assert_eq!(
entry
.ecx
.read_bits_in_range(&ecx::NODES_PER_PROCESSOR_BITRANGE),
NODES_PER_PROCESSOR
);
assert_eq!(entry.ecx.read_bits_in_range(&ecx::NODE_ID_BITRANGE), 0);
}
#[test]
fn test_update_extended_cache_topology_entry() {
let vm_spec = VmSpec::new(0, 1, false).expect("Error creating vm_spec");
let mut entry = kvm_cpuid_entry2 {
function: leaf_0x8000001d::LEAF_NUM,
index: 0,
flags: 0,
eax: 0,
ebx: 0,
ecx: 0,
edx: 0,
padding: [0, 0, 0],
};
assert!(update_extended_cache_topology_entry(&mut entry, &vm_spec).is_ok());
assert_eq!(entry.flags & KVM_CPUID_FLAG_SIGNIFCANT_INDEX, 1);
}
#[test]
fn test_1vcpu_ht_off() {
check_update_amd_features_entry(1, false);
check_update_extended_apic_id_entry(0, 1, false, 0, 0);
}
#[test]
fn test_1vcpu_ht_on() {
check_update_amd_features_entry(1, true);
check_update_extended_apic_id_entry(0, 1, true, 0, 1);
}
#[test]
fn test_2vcpu_ht_off() {
check_update_amd_features_entry(2, false);
check_update_extended_apic_id_entry(0, 2, false, 0, 0);
check_update_extended_apic_id_entry(1, 2, false, 1, 0);
}
#[test]
fn test_2vcpu_ht_on() {
check_update_amd_features_entry(2, true);
check_update_extended_apic_id_entry(0, 2, true, 0, 1);
check_update_extended_apic_id_entry(1, 2, true, 0, 1);
}
#[test]
fn test_update_feature_info_entry() {
use crate::cpu_leaf::leaf_0x1::*;
let vm_spec = VmSpec::new(0, 1, false).expect("Error creating vm_spec");
let mut entry = kvm_cpuid_entry2 {
function: leaf_0x1::LEAF_NUM,
index: 0,
flags: 0,
eax: 0,
ebx: 0,
ecx: 0,
edx: 0,
padding: [0, 0, 0],
};
assert!(update_feature_info_entry(&mut entry, &vm_spec).is_ok());
assert!(entry.ecx.read_bit(ecx::TSC_DEADLINE_TIMER_BITINDEX));
}
}