Skip to content

Commit

Permalink
cpuid crate refactoring - part 2
Browse files Browse the repository at this point in the history
- split cpu_leaf into separate files for each leaf
- move leaf handlers into cpu_leaf
- test leaf handlers individually
- move all the templates into a folder

Signed-off-by: Serban Iorga <seriorga@amazon.com>
  • Loading branch information
Serban Iorga committed Feb 21, 2019
1 parent f68a218 commit 82d2d56
Show file tree
Hide file tree
Showing 31 changed files with 1,116 additions and 1,482 deletions.
26 changes: 26 additions & 0 deletions cpuid/src/bit_helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ impl BitRangeExt<u32> for BitRange {
/// Trait containing helper methods for bit operations.
///
pub trait BitHelper {
/// Reads the value of the bit at position `pos`
///
fn read_bit(&self, pos: u32) -> bool;

/// Changes the value of the bit at position `pos` to `val`
///
fn write_bit(&mut self, pos: u32, val: bool) -> &mut Self;
Expand Down Expand Up @@ -144,6 +148,12 @@ pub trait BitHelper {
}

impl BitHelper for u32 {
fn read_bit(&self, pos: u32) -> bool {
assert!(pos <= MAX_U32_BIT_INDEX, "Invalid pos");

(*self & (1 << pos)) > 0
}

fn write_bit(&mut self, pos: u32, val: bool) -> &mut Self {
assert!(pos <= MAX_U32_BIT_INDEX, "Invalid pos");

Expand Down Expand Up @@ -215,6 +225,22 @@ mod tests {
assert!(val == 0);
}

#[test]
#[should_panic]
fn test_invalid_read_bit() {
// Set bit to 1
let val: u32 = 0;
val.read_bit(32);
}

#[test]
fn test_simple_read_bit() {
// Set bit to 1
let val: u32 = 0b100000;
assert!(val.read_bit(5));
assert!(!val.read_bit(4));
}

#[test]
fn test_chained_write_bit() {
let mut val: u32 = 1 << 12;
Expand Down
3 changes: 2 additions & 1 deletion cpuid/src/brand_string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
use std::arch::x86_64::__cpuid as host_cpuid;
use std::slice;

#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Clone)]
pub enum Error {
NotSupported,
Overflow(String),
Expand All @@ -23,6 +23,7 @@ pub enum Reg {
/// This is achieved by bypassing the `O(n)` indexing, heap allocation, and the unicode checks
/// done by `std::string::String`.
///
#[derive(Clone)]
pub struct BrandString {
/// Flattened buffer, holding an array of 32-bit register values.
///
Expand Down
216 changes: 0 additions & 216 deletions cpuid/src/cpu_leaf.rs

This file was deleted.

114 changes: 114 additions & 0 deletions cpuid/src/cpu_leaf/leaf_0x1/engine.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use super::registers::*;
use bit_helper::BitHelper;
use cpu_leaf::*;
use kvm_bindings::kvm_cpuid_entry2;

// CPUID bits in ebx, ecx, and edx.
const EBX_CLFLUSH_CACHELINE: u32 = 8;

/// Sets leaf 01H EBX[23-16].
///
/// The maximum number of addressable logical CPUs is computed as the closest power of 2
/// higher or equal to the CPU count configured by the user.
pub fn get_max_addressable_lprocessors(cpu_count: u8) -> Result<u8, Error> {
let mut max_addressable_lcpu = (cpu_count as f64).log2().ceil();
max_addressable_lcpu = (2 as f64).powf(max_addressable_lcpu);
// check that this number is still an u8
if max_addressable_lcpu > u8::max_value().into() {
return Err(Error::VcpuCountOverflow);
}
Ok(max_addressable_lcpu as u8)
}

pub struct Engine {
pub cpu_id: u8,
pub cpu_count: u8,
}

impl LeafEngine for Engine {
fn get_leaf_engine_id(&self) -> String {
"leaf_0x1".to_string()
}

fn process(&self, entry: &mut kvm_cpuid_entry2) -> Result<(), Error> {
let max_addr_cpu = get_max_addressable_lprocessors(self.cpu_count)? as u32;

// X86 hypervisor feature
entry
.ecx
.write_bit(ecx::TSC_DEADLINE_TIMER_BITINDEX, true)
.write_bit(ecx::HYPERVISOR_BITINDEX, true);

entry
.ebx
.write_bits_in_range(&ebx::APICID_BITRANGE, self.cpu_id as u32)
.write_bits_in_range(&ebx::CLFLUSH_SIZE_BITRANGE, EBX_CLFLUSH_CACHELINE)
.write_bits_in_range(&ebx::CPU_COUNT_BITRANGE, max_addr_cpu);

// A value of 1 for HTT indicates the value in CPUID.1.EBX[23:16]
// (the Maximum number of addressable IDs for logical processors in this package)
// is valid for the package
entry.edx.write_bit(edx::HTT, self.cpu_count > 1);

Ok(())
}
}

#[cfg(test)]
mod tests {
use super::*;
use kvm_bindings::kvm_cpuid_entry2;

#[test]
fn test_get_max_addressable_lprocessors() {
assert_eq!(get_max_addressable_lprocessors(1).unwrap(), 1);
assert_eq!(get_max_addressable_lprocessors(2).unwrap(), 2);
assert_eq!(get_max_addressable_lprocessors(4).unwrap(), 4);
assert_eq!(get_max_addressable_lprocessors(6).unwrap(), 8);
assert!(get_max_addressable_lprocessors(u8::max_value()).is_err());
}

fn test_process(cpu_count: u8, expected_htt: bool) {
let engine: Engine = Engine {
cpu_id: 0,
cpu_count: cpu_count,
};

let mut entry = &mut kvm_cpuid_entry2 {
function: 0x1,
index: 0,
flags: 0,
eax: 0,
ebx: 0,
ecx: 0,
edx: 0,
padding: [0, 0, 0],
};

assert!(engine.process(&mut entry).is_ok());

assert!(entry.ecx.read_bit(ecx::TSC_DEADLINE_TIMER_BITINDEX));
assert!(entry.ecx.read_bit(ecx::HYPERVISOR_BITINDEX));

assert!(entry.ebx.read_bits_in_range(&ebx::APICID_BITRANGE) == engine.cpu_id as u32);
assert!(entry.ebx.read_bits_in_range(&ebx::CLFLUSH_SIZE_BITRANGE) == EBX_CLFLUSH_CACHELINE);
assert!(
entry.ebx.read_bits_in_range(&ebx::CPU_COUNT_BITRANGE)
== get_max_addressable_lprocessors(engine.cpu_count).unwrap() as u32
);
assert!(entry.edx.read_bit(edx::HTT) == expected_htt)
}

#[test]
fn test_1vcpu() {
test_process(1, false);
}

#[test]
fn test_2vcpu() {
test_process(2, true);
}
}
Loading

0 comments on commit 82d2d56

Please sign in to comment.