Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix AMD boot freeze #1015

Merged
merged 4 commits into from
Mar 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 134 additions & 0 deletions cpuid/src/common.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

#[cfg(target_arch = "x86")]
andreeaflorescu marked this conversation as resolved.
Show resolved Hide resolved
use std::arch::x86::{CpuidResult, __cpuid_count, __get_cpuid_max};
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::{CpuidResult, __cpuid_count, __get_cpuid_max};

use cpu_leaf::*;

pub const VENDOR_ID_INTEL: &[u8; 12] = b"GenuineIntel";
pub const VENDOR_ID_AMD: &[u8; 12] = b"AuthenticAMD";

#[derive(Clone, Debug)]
pub enum Error {
InvalidParameters(String),
NotSupported,
}

#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
pub fn get_cpuid(function: u32, count: u32) -> Result<CpuidResult, Error> {
// TODO: replace with validation based on `has_cpuid()` when it becomes stable:
// https://doc.rust-lang.org/core/arch/x86/fn.has_cpuid.html
#[cfg(target_env = "sgx")]
{
return Err(Error::NotSupported);
}
// For x86 the host supports the `cpuid` instruction if SSE is enabled. Otherwise it's hard to check.
#[cfg(target_arch = "x86")]
{
#[cfg(not(target_feature = "sse"))]
{
return Err(Error::NotSupported);
}
}

// this is safe because the host supports the `cpuid` instruction
let max_function = unsafe { __get_cpuid_max(function & leaf_0x80000000::LEAF_NUM).0 };
if function > max_function {
return Err(Error::InvalidParameters(format!(
"Function not supported: 0x{:x}",
function
)));
}

// this is safe because the host supports the `cpuid` instruction
let entry = unsafe { __cpuid_count(function, count) };
if entry.eax == 0 && entry.ebx == 0 && entry.ecx == 0 && entry.edx == 0 {
return Err(Error::InvalidParameters(format!(
"Invalid count: {}",
count
)));
}

Ok(entry)
}

/// Extracts the CPU vendor id from leaf 0x0.
///
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
pub fn get_vendor_id() -> Result<[u8; 12], Error> {
match get_cpuid(0, 0) {
Ok(vendor_entry) => {
let bytes: [u8; 12] = unsafe {
std::mem::transmute([vendor_entry.ebx, vendor_entry.edx, vendor_entry.ecx])
};
Ok(bytes)
}
Err(_e) => Err(Error::NotSupported),
}
}

#[cfg(test)]
pub mod tests {
use common::*;
use cpu_leaf::*;

#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
pub fn get_topoext_fn() -> u32 {
let vendor_id = get_vendor_id();
assert!(vendor_id.is_ok());
let function = match &vendor_id.ok().unwrap() {
VENDOR_ID_INTEL => leaf_0x4::LEAF_NUM,
VENDOR_ID_AMD => leaf_0x8000001d::LEAF_NUM,
_ => 0,
};
assert!(function != 0);

function
}

#[test]
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
fn test_get_cpu_id() {
// get_cpu_id should work correctly here
let topoext_fn = get_topoext_fn();

// check that get_cpuid works for valid parameters
match get_cpuid(topoext_fn, 0) {
Ok(topoext_entry) => {
assert!(topoext_entry.eax != 0);
}
_ => panic!("Wrong behavior"),
}

// check that get_cpuid returns correct error for invalid `function`
match get_cpuid(0x90000000, 0) {
Err(Error::InvalidParameters(s)) => {
assert!(s == "Function not supported: 0x90000000");
}
_ => panic!("Wrong behavior"),
}

// check that get_cpuid returns correct error for invalid `count`
match get_cpuid(topoext_fn, 100) {
Err(Error::InvalidParameters(s)) => {
assert!(s == "Invalid count: 100");
}
_ => panic!("Wrong behavior"),
}
}

#[test]
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
fn test_get_vendor_id() {
let vendor_id = get_vendor_id();
assert!(vendor_id.is_ok());
assert!(match &vendor_id.ok().unwrap() {
VENDOR_ID_INTEL => true,
VENDOR_ID_AMD => true,
_ => false,
});
}
}
41 changes: 39 additions & 2 deletions cpuid/src/cpu_leaf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

// Basic CPUID Information
pub mod leaf_0x1 {
pub const LEAF_NUM: u32 = 0x1;

pub mod eax {

pub const EXTENDED_FAMILY_ID_SHIFT: u32 = 20;
Expand Down Expand Up @@ -63,19 +65,41 @@ pub mod leaf_0x1 {
}
}

// Deterministic Cache Parameters Leaf
pub mod leaf_0x4 {
pub mod leaf_cache_parameters {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this node have a leaf number const as well?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this is in fact a sub-leaf of the 0x4 leaf.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not an actual leaf. Since 0x4 and 0x8000_001d are very similar, but not completely the same (see ox4 - page 32 and 0x8000_001d - page 76 ), I created this "parent leaf" in order to hold the common properties. 0x4 and 0x8000_001d inherit these common properties, and then add their specific ones.

pub mod eax {
use bit_helper::BitRange;

pub const CACHE_LEVEL_BITRANGE: BitRange = bit_range!(7, 5);
pub const MAX_CPUS_PER_CORE_BITRANGE: BitRange = bit_range!(25, 14);
}
}

// Deterministic Cache Parameters Leaf
pub mod leaf_0x4 {
pub const LEAF_NUM: u32 = 0x4;

pub mod eax {
use bit_helper::BitRange;

// inherit eax from leaf_cache_parameters
pub use cpu_leaf::leaf_cache_parameters::eax::*;

pub const MAX_CORES_PER_PACKAGE_BITRANGE: BitRange = bit_range!(31, 26);
}
}

// Extended Cache Topology Leaf
pub mod leaf_0x8000001d {
pub const LEAF_NUM: u32 = 0x8000_001d;

// inherit eax from leaf_cache_parameters
pub use cpu_leaf::leaf_cache_parameters::eax;
}

// Thermal and Power Management Leaf
pub mod leaf_0x6 {
pub const LEAF_NUM: u32 = 0x6;

pub mod eax {
pub const TURBO_BOOST_BITINDEX: u32 = 1;
}
Expand All @@ -88,6 +112,8 @@ pub mod leaf_0x6 {

// Structured Extended Feature Flags Enumeration Leaf
pub mod leaf_0x7 {
pub const LEAF_NUM: u32 = 0x7;

pub mod index0 {
pub mod ebx {
// 1 = TSC_ADJUST
Expand Down Expand Up @@ -143,7 +169,13 @@ pub mod leaf_0x7 {
}
}

pub mod leaf_0x80000000 {
pub const LEAF_NUM: u32 = 0x8000_0000;
}

pub mod leaf_0x80000001 {
pub const LEAF_NUM: u32 = 0x8000_0001;

pub mod ecx {
pub const PREFETCH_SHIFT: u32 = 8; // 3DNow! PREFETCH/PREFETCHW instructions
pub const LZCNT_SHIFT: u32 = 5; // advanced bit manipulation
Expand All @@ -154,8 +186,13 @@ pub mod leaf_0x80000001 {
}
}

pub mod leaf_0xa {
pub const LEAF_NUM: u32 = 0xa;
}

// Extended Topology Leaf
pub mod leaf_0xb {
pub const LEAF_NUM: u32 = 0xb;

pub const LEVEL_TYPE_INVALID: u32 = 0;
pub const LEVEL_TYPE_THREAD: u32 = 1;
Expand Down
18 changes: 15 additions & 3 deletions cpuid/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ extern crate kvm_bindings;

use kvm::CpuId;

mod common;
use common::*;

/// Contains helper methods for bit operations.
pub mod bit_helper;

Expand Down Expand Up @@ -59,11 +62,20 @@ pub fn filter_cpuid(
ht_enabled: bool,
kvm_cpuid: &mut CpuId,
) -> Result<(), Error> {
let vendor_id = get_vendor_id().map_err(Error::InternalError)?;
let vm_spec = VmSpec::new(cpu_id, cpu_count, ht_enabled);

let transform_entry_fn: EntryTransformerFn = intel::transform_entry;
for entry in kvm_cpuid.mut_entries_slice().iter_mut() {
transform_entry_fn(entry, &vm_spec)?;
let maybe_cpuid_transformer: Option<&dyn CpuidTransformer> = match &vendor_id {
VENDOR_ID_INTEL => Some(&intel::IntelCpuidTransformer {}),
VENDOR_ID_AMD => Some(&amd::AmdCpuidTransformer {}),
_ => None,
};

if let Some(cpuid_transformer) = maybe_cpuid_transformer {
cpuid_transformer.preprocess_cpuid(kvm_cpuid)?;
for entry in kvm_cpuid.mut_entries_slice().iter_mut() {
cpuid_transformer.transform_entry(entry, &vm_spec)?;
}
}

Ok(())
Expand Down
6 changes: 3 additions & 3 deletions cpuid/src/template/c3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use kvm_bindings::kvm_cpuid_entry2;
pub fn set_cpuid_entries(entries: &mut [kvm_cpuid_entry2]) {
for entry in entries.iter_mut() {
match entry.function {
0x1 => {
leaf_0x1::LEAF_NUM => {
// Set CPU Basic Information
// EAX[20:27] Extended Family ID = 0
entry.eax &= !(0b1111_1111 << leaf_0x1::eax::EXTENDED_FAMILY_ID_SHIFT);
Expand Down Expand Up @@ -52,7 +52,7 @@ pub fn set_cpuid_entries(entries: &mut [kvm_cpuid_entry2]) {
entry.edx &= !(1 << leaf_0x1::edx::TM_SHIFT);
entry.edx &= !(1 << leaf_0x1::edx::PBE_SHIFT);
}
0x7 => {
leaf_0x7::LEAF_NUM => {
if entry.index == 0 {
entry.ebx &= !(1 << leaf_0x7::index0::ebx::SGX_SHIFT);
entry.ebx &= !(1 << leaf_0x7::index0::ebx::BMI1_SHIFT);
Expand All @@ -76,7 +76,7 @@ pub fn set_cpuid_entries(entries: &mut [kvm_cpuid_entry2]) {
entry.ecx &= !(1 << leaf_0x7::index0::ecx::SGX_LC_SHIFT);
}
}
0x8000_0001 => {
leaf_0x80000001::LEAF_NUM => {
entry.ecx &= !(1 << leaf_0x80000001::ecx::PREFETCH_SHIFT);
entry.ecx &= !(1 << leaf_0x80000001::ecx::LZCNT_SHIFT);
entry.edx &= !(1 << leaf_0x80000001::edx::PDPE1GB_SHIFT);
Expand Down
6 changes: 3 additions & 3 deletions cpuid/src/template/t2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use kvm_bindings::kvm_cpuid_entry2;
pub fn set_cpuid_entries(entries: &mut [kvm_cpuid_entry2]) {
for entry in entries.iter_mut() {
match entry.function {
0x1 => {
leaf_0x1::LEAF_NUM => {
// Set CPU Basic Information
// EAX[20:27] Extended Family ID = 0
entry.eax &= !(0b1111_1111 << leaf_0x1::eax::EXTENDED_FAMILY_ID_SHIFT);
Expand Down Expand Up @@ -50,7 +50,7 @@ pub fn set_cpuid_entries(entries: &mut [kvm_cpuid_entry2]) {
entry.edx &= !(1 << leaf_0x1::edx::TM_SHIFT);
entry.edx &= !(1 << leaf_0x1::edx::PBE_SHIFT);
}
0x7 => {
leaf_0x7::LEAF_NUM => {
if entry.index == 0 {
entry.ebx &= !(1 << leaf_0x7::index0::ebx::SGX_SHIFT);
entry.ebx &= !(1 << leaf_0x7::index0::ebx::HLE_SHIFT);
Expand All @@ -69,7 +69,7 @@ pub fn set_cpuid_entries(entries: &mut [kvm_cpuid_entry2]) {
entry.ecx &= !(1 << leaf_0x7::index0::ecx::SGX_LC_SHIFT);
}
}
0x8000_0001 => {
leaf_0x80000001::LEAF_NUM => {
entry.ecx &= !(1 << leaf_0x80000001::ecx::PREFETCH_SHIFT);
entry.edx &= !(1 << leaf_0x80000001::edx::PDPE1GB_SHIFT);
}
Expand Down
38 changes: 38 additions & 0 deletions cpuid/src/transformer/amd.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use super::*;
use cpu_leaf::*;
use kvm::CpuId;
use kvm_bindings::KVM_CPUID_FLAG_SIGNIFCANT_INDEX;
use transformer::common::use_host_cpuid_function;

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 struct AmdCpuidTransformer {}

impl CpuidTransformer for AmdCpuidTransformer {
fn preprocess_cpuid(&self, cpuid: &mut CpuId) -> Result<(), Error> {
use_host_cpuid_function(cpuid, leaf_0x8000001d::LEAF_NUM)
}

fn transform_entry(&self, entry: &mut kvm_cpuid_entry2, vm_spec: &VmSpec) -> Result<(), Error> {
let maybe_transformer_fn: Option<EntryTransformerFn> = match entry.function {
leaf_0x8000001d::LEAF_NUM => Some(amd::update_extended_cache_topology_entry),
_ => None,
};

if let Some(transformer_fn) = maybe_transformer_fn {
return transformer_fn(entry, vm_spec);
}

Ok(())
}
}
Loading