occlum/src/libos/src/exception/cpuid.rs
2024-03-06 17:44:30 +08:00

289 lines
11 KiB
Rust

use super::*;
use crate::syscall::CpuContext;
use sgx_types::*;
use std::collections::HashMap;
use std::rsgx_cpuidex;
pub const CPUID_OPCODE: u16 = 0xA20F;
const CPUID_MIN_BASIC_LEAF: u32 = 0;
const CPUID_MIN_EXTEND_LEAF: u32 = 0x8000_0000;
const CPUID_MAX_SUBLEAF: u32 = u32::max_value();
#[repr(C)]
#[derive(Eq, PartialEq, Hash, Clone, Copy)]
struct CpuIdInput {
leaf: u32,
subleaf: u32,
}
#[repr(C)]
#[derive(Eq, PartialEq, Hash, Clone, Copy, Debug)]
pub struct CpuIdResult {
eax: u32,
ebx: u32,
ecx: u32,
edx: u32,
}
struct CpuIdCache {
inner: HashMap<CpuIdInput, CpuIdResult>,
}
struct CpuId {
cache: CpuIdCache,
max_basic_leaf: u32,
max_extend_leaf: u32,
}
impl CpuIdCache {
pub fn new(max_basic_leaf: u32, max_extend_leaf: u32) -> CpuIdCache {
let mut cache = CpuIdCache {
inner: HashMap::new(),
};
cache.generate_cpuid_cache(max_basic_leaf, max_extend_leaf);
cache
}
pub fn lookup(&self, key: &CpuIdInput) -> Option<&CpuIdResult> {
self.inner.get(key)
}
fn insert(&mut self, key: CpuIdInput, value: CpuIdResult) {
// If EAX/EBX/ECX/EDX return 0, dismiss it
if (value.eax | value.ebx | value.ecx | value.edx) != 0 {
self.inner.insert(key, value);
}
}
fn generate_cpuid_cache(&mut self, max_basic_leaf: u32, max_extend_leaf: u32) {
let mut sgx_support: bool = false;
let mut pconfig_support: bool = false;
// Generate basic leaf cpuid cache
for leaf in CPUID_MIN_BASIC_LEAF..=max_basic_leaf {
// Intel SGX Capability Enumeration Leaf,
// Leaf 12H sub-leaf 0 is supported if CPUID.(EAX=07H, ECX=0H):EBX[SGX] = 1.
if leaf == 0x12 && !sgx_support {
continue;
}
// Intel PCONFIG Enumeration Leaf,
// Leaf 1BH is supported if CPUID.(EAX=07H, ECX=0H):EDX[18] = 1.
if leaf == 0x1B && !pconfig_support {
continue;
}
let mut max_subleaf = 0;
for subleaf in (0..) {
let cpuid_input = CpuIdInput { leaf, subleaf };
let cpuid_result = get_cpuid_info_via_ocall(cpuid_input);
self.insert(cpuid_input, cpuid_result);
// Most leaf only supports (sub-leaf == 0), and many others can determine their
// maximum supported sub-leaf according to CPUID.(EAX=Leaf, ECX=0H).
if subleaf == 0 {
max_subleaf = match leaf {
// EAX Bits 31 - 00: Reports the maximum sub-leaf supported.
0x7 | 0x14 | 0x17 | 0x18 => cpuid_result.eax,
// Reports valid resource type starting at bit position 1 of EDX.
// EDX Bit 00: Reserved.
// Bit 01: Supports L3 Cache Intel RDT Monitoring if 1.
// Bits 31 - 02: Reserved.
0xF => (cpuid_result.edx & 0x0000_0002) >> 1,
// Reports valid ResID starting at bit position 1 of EBX.
// EBX Bit 00: Reserved.
// Bit 01: Supports L3 Cache Allocation Technology if 1.
// Bit 02: Supports L2 Cache Allocation Technology if 1.
// Bit 03: Supports Memory Bandwidth Allocation if 1.
// Bits 31 - 04: Reserved.
0x10 => match cpuid_result.ebx & 0x0000_000E {
0x0000_0008 | 0x0000_000A | 0x0000_000C | 0x0000_000E => 3,
0x0000_0004 | 0x0000_0006 => 2,
0x0000_0002 => 1,
_ => 0,
},
// Processor Extended State Enumeration, Sub-leaf n (0 ≤ n ≤ 63)
0xD => 63,
// (Sub-leaf == 0) can not decide max_subleaf for these leaf,
// later match expression will decide the max_subleaf.
0x4 | 0xB | 0x12 | 0x1B | 0x1F => CPUID_MAX_SUBLEAF,
// Default max_subleaf is 0.
_ => 0,
};
if leaf == 0x7 {
// EBX Bit 02: Supports Intel® SGX Extensions if 1.
sgx_support = (cpuid_result.ebx & 0x0000_0004) != 0;
// EDX Bit 18: Supports PCONFIG if 1.
pconfig_support = (cpuid_result.edx & 0x40000) != 0;
}
}
// These leafs determine the maximum supported sub-leaf according to
// the output of CPUID instruction at every iteration.
if max_subleaf == CPUID_MAX_SUBLEAF {
max_subleaf = match leaf {
// Deterministic Cache Parameters Leaf
// Sub-leaf index n+1 is invalid if subleaf n returns EAX[4:0] as 0.
0x4 if (cpuid_result.eax & 0x1F) == 0 => subleaf,
// Extended Topology Enumeration Leaf
// If an input value n in ECX returns the invalid level-type of 0 in ECX[15:8],
// other input values with ECX > n also return 0 in ECX[15:8].
0xB if (cpuid_result.ecx & 0x0000_FF00) == 0 => subleaf,
// Intel SGX EPC Enumeration Leaf
// EAX Bit 03 - 00: Sub-leaf Type.
// 0000b: Indicates this sub-leaf is invalid.
0x12 if subleaf >= 2 && (cpuid_result.eax & 0x0000000F) == 0 => subleaf,
// If a sub-leaf type (EAX) is 0, the sub-leaf is invalid and zero is returned
// in EBX, ECX, and EDX.
0x1B if (cpuid_result.eax == 0) => subleaf,
// V2 Extended Topology Enumeration Leaf
// CPUID leaf 0x1F is a preferred superset to leaf 0xB.
0x1F if (cpuid_result.ecx & 0x0000_FF00) == 0 => subleaf,
// Condition not met.
_ => max_subleaf,
};
}
if subleaf == max_subleaf {
break;
}
}
}
// Generate extend leaf cpuid cache
for leaf in CPUID_MIN_EXTEND_LEAF..=max_extend_leaf {
let cpuid_input = CpuIdInput { leaf, subleaf: 0 };
let cpuid_result = get_cpuid_info_via_ocall(cpuid_input);
self.insert(cpuid_input, cpuid_result);
}
}
}
impl CpuId {
pub fn new() -> CpuId {
let max_basic_leaf = match rsgx_cpuidex(CPUID_MIN_BASIC_LEAF as i32, 0) {
Ok(sgx_cpuinfo) => sgx_cpuinfo[0] as u32,
_ => panic!("failed to call sgx_cpuidex"),
};
let max_extend_leaf = match rsgx_cpuidex(CPUID_MIN_EXTEND_LEAF as i32, 0) {
Ok(sgx_cpuinfo) => sgx_cpuinfo[0] as u32,
_ => panic!("failed to call sgx_cpuidex"),
};
let cpuid = CpuId {
cache: CpuIdCache::new(max_basic_leaf, max_extend_leaf),
max_basic_leaf,
max_extend_leaf,
};
cpuid
}
fn lookup_cpuid_from_cache(&self, cpuid_input: CpuIdInput) -> Result<CpuIdResult> {
self.cache
.lookup(&cpuid_input)
.map(|result| result.clone())
.ok_or_else(|| errno!(ENOENT, "cpuid_result not found"))
}
pub fn get_max_basic_leaf(&self) -> u32 {
self.max_basic_leaf
}
pub fn get_max_extend_leaf(&self) -> u32 {
self.max_extend_leaf
}
pub fn get_cpuid_info(&self, leaf: u32, subleaf: u32) -> CpuIdResult {
// If a value entered for CPUID.EAX is higher than the maximum input value
// for basic or extended function for that processor then the data for the
// highest basic information leaf is returned.
let fixed_leaf = if (CPUID_MIN_BASIC_LEAF..=self.max_basic_leaf).contains(&leaf)
|| (CPUID_MIN_EXTEND_LEAF..=self.max_extend_leaf).contains(&leaf)
{
leaf
} else {
self.max_basic_leaf
};
let fixed_subleaf = if is_cpuid_leaf_has_subleaves(fixed_leaf) {
subleaf
} else {
0
};
let cpuid_input = CpuIdInput {
leaf: fixed_leaf,
subleaf: fixed_subleaf,
};
let cpuid_result = match self.lookup_cpuid_from_cache(cpuid_input) {
Ok(cpuid_result) => cpuid_result,
// If a value entered for CPUID.EAX is less than or equal to the maximum input value
// and the leaf is not supported on that processor then 0 is returned in all the registers.
Err(error) => CpuIdResult {
eax: 0,
ebx: 0,
ecx: 0,
edx: 0,
},
};
cpuid_result
}
pub fn support_sgx2(&self) -> bool {
const SGX_CPUID: u32 = 0x12;
let cpuid = self.get_cpuid_info(SGX_CPUID, 0);
// The 0th bit set to 1 in `cpuid.eax` indicates that the SGX feature is enabled.
// The 1st bit set to 1 in `cpuid.eax` indicates that the SGX2 feature is enabled.
(cpuid.eax & 0b11) == 0b11
}
}
lazy_static! {
static ref CPUID: CpuId = CpuId::new();
}
fn is_cpuid_leaf_has_subleaves(leaf: u32) -> bool {
const CPUID_LEAF_WITH_SUBLEAF: [u32; 12] = [
0x4, 0x7, 0xB, 0xD, 0xF, 0x10, 0x12, 0x14, 0x17, 0x18, 0x1B, 0x1F,
];
CPUID_LEAF_WITH_SUBLEAF.contains(&leaf)
}
// We cannot do OCalls when handling exceptions. So this function is only useful
// when we call setup_cpuid_info to initialize the CPUID singleton,
// which caches cpuid info for use when handling cpuid exception.
fn get_cpuid_info_via_ocall(cpuid_input: CpuIdInput) -> CpuIdResult {
let cpuid_result = match rsgx_cpuidex(cpuid_input.leaf as i32, cpuid_input.subleaf as i32) {
Ok(sgx_cpuinfo) => CpuIdResult {
eax: sgx_cpuinfo[0] as u32,
ebx: sgx_cpuinfo[1] as u32,
ecx: sgx_cpuinfo[2] as u32,
edx: sgx_cpuinfo[3] as u32,
},
_ => panic!("failed to call sgx_cpuidex"),
};
cpuid_result
}
pub fn is_cpu_support_sgx2() -> bool {
CPUID.support_sgx2()
}
pub fn get_cpuid_info(leaf: u32, subleaf: u32) -> CpuIdResult {
CPUID.get_cpuid_info(leaf, subleaf)
}
pub fn setup_cpuid_info() {
// Make lazy_static to be executed at runtime in order to be initialized
let max_basic_leaf = CPUID.get_max_basic_leaf();
}
pub fn handle_cpuid_exception(user_context: &mut CpuContext) -> Result<isize> {
let leaf = user_context.rax as u32;
let subleaf = user_context.rcx as u32;
debug!(
"handle CPUID exception: leaf = 0x{:x?}, subleaf = 0x{:x?}",
leaf, subleaf
);
let cpuid_result = CPUID.get_cpuid_info(leaf, subleaf);
trace!("cpuid result: {:?}", cpuid_result);
user_context.rax = cpuid_result.eax as u64;
user_context.rbx = cpuid_result.ebx as u64;
user_context.rcx = cpuid_result.ecx as u64;
user_context.rdx = cpuid_result.edx as u64;
user_context.rip += 2;
Ok(0)
}