Add the `arch::cpu::extension` module
This commit is contained in:
parent
9de70e38de
commit
75ca7e0377
|
|
@ -8,7 +8,7 @@ use spin::Once;
|
|||
use crate::arch::boot::DEVICE_TREE;
|
||||
|
||||
/// Detects available RISC-V ISA extensions.
|
||||
pub fn init() {
|
||||
pub(in crate::arch) fn init() {
|
||||
let mut global_isa_extensions = IsaExtensions::all();
|
||||
|
||||
let device_tree = DEVICE_TREE.get().expect("Device tree not initialized");
|
||||
|
|
@ -33,6 +33,8 @@ pub fn init() {
|
|||
global_isa_extensions &= cpu_isa_extensions;
|
||||
}
|
||||
|
||||
log::info!("Detected ISA extensions: {:?}", global_isa_extensions);
|
||||
|
||||
GLOBAL_ISA_EXTENSIONS.call_once(|| global_isa_extensions);
|
||||
}
|
||||
|
||||
|
|
@ -107,7 +109,7 @@ macro_rules! define_isa_extensions {
|
|||
)*
|
||||
) => {
|
||||
bitflags! {
|
||||
/// RISC-V ISA extensions
|
||||
/// RISC-V ISA extensions.
|
||||
pub struct IsaExtensions: u128 {
|
||||
$(
|
||||
#[doc = $doc]
|
||||
|
|
|
|||
|
|
@ -18,10 +18,7 @@ use x86_64::registers::{
|
|||
};
|
||||
|
||||
use crate::{
|
||||
arch::{
|
||||
trap::{RawUserContext, TrapFrame},
|
||||
CPU_FEATURES,
|
||||
},
|
||||
arch::trap::{RawUserContext, TrapFrame},
|
||||
mm::Vaddr,
|
||||
task::scheduler,
|
||||
trap::call_irq_callback_functions,
|
||||
|
|
@ -505,8 +502,8 @@ impl FpuContext {
|
|||
/// Creates a new FPU context.
|
||||
pub fn new() -> Self {
|
||||
let mut area_size = size_of::<FxSaveArea>();
|
||||
if CPU_FEATURES.get().unwrap().has_xsave() {
|
||||
area_size = area_size.max(*XSAVE_AREA_SIZE.get().unwrap());
|
||||
if let Some(xsave_area_size) = XSAVE_AREA_SIZE.get() {
|
||||
area_size = area_size.max(*xsave_area_size);
|
||||
}
|
||||
|
||||
Self {
|
||||
|
|
@ -519,7 +516,7 @@ impl FpuContext {
|
|||
pub fn save(&mut self) {
|
||||
let mem_addr = self.as_bytes_mut().as_mut_ptr();
|
||||
|
||||
if CPU_FEATURES.get().unwrap().has_xsave() {
|
||||
if XSTATE_MAX_FEATURES.is_completed() {
|
||||
unsafe { _xsave64(mem_addr, XFEATURE_MASK_USER_RESTORE) };
|
||||
} else {
|
||||
unsafe { _fxsave64(mem_addr) };
|
||||
|
|
@ -532,8 +529,8 @@ impl FpuContext {
|
|||
pub fn load(&mut self) {
|
||||
let mem_addr = self.as_bytes().as_ptr();
|
||||
|
||||
if CPU_FEATURES.get().unwrap().has_xsave() {
|
||||
let rs_mask = XFEATURE_MASK_USER_RESTORE & XSTATE_MAX_FEATURES.get().unwrap();
|
||||
if let Some(xstate_max_features) = XSTATE_MAX_FEATURES.get() {
|
||||
let rs_mask = XFEATURE_MASK_USER_RESTORE & *xstate_max_features;
|
||||
|
||||
unsafe { _xrstor64(mem_addr, rs_mask) };
|
||||
} else {
|
||||
|
|
@ -593,8 +590,8 @@ struct XSaveArea {
|
|||
|
||||
impl XSaveArea {
|
||||
fn new() -> Self {
|
||||
let features = if CPU_FEATURES.get().unwrap().has_xsave() {
|
||||
XCr0::read().bits() & XSTATE_MAX_FEATURES.get().unwrap()
|
||||
let features = if let Some(xstate_max_features) = XSTATE_MAX_FEATURES.get() {
|
||||
XCr0::read().bits() & *xstate_max_features
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
|
@ -647,7 +644,9 @@ static XSAVE_AREA_SIZE: Once<usize> = Once::new();
|
|||
const MAX_XSAVE_AREA_SIZE: usize = 4096;
|
||||
|
||||
pub(in crate::arch) fn enable_essential_features() {
|
||||
if CPU_FEATURES.get().unwrap().has_xsave() {
|
||||
use super::extension::{has_extensions, IsaExtensions};
|
||||
|
||||
if has_extensions(IsaExtensions::XSAVE) {
|
||||
XSTATE_MAX_FEATURES.call_once(|| super::cpuid::query_xstate_max_features().unwrap());
|
||||
XSAVE_AREA_SIZE.call_once(|| {
|
||||
let xsave_area_size = super::cpuid::query_xsave_area_size().unwrap() as usize;
|
||||
|
|
@ -656,7 +655,9 @@ pub(in crate::arch) fn enable_essential_features() {
|
|||
});
|
||||
}
|
||||
|
||||
if CPU_FEATURES.get().unwrap().has_fpu() {
|
||||
// We now assume that all x86-64 CPUs should have the FPU. Otherwise, we should check
|
||||
// `has_extensions(IsaExtensions::FPU)` here.
|
||||
{
|
||||
let mut cr0 = Cr0::read();
|
||||
cr0.remove(Cr0Flags::TASK_SWITCHED | Cr0Flags::EMULATE_COPROCESSOR);
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,121 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! x86 ISA extensions.
|
||||
|
||||
use core::arch::x86_64::CpuidResult;
|
||||
|
||||
use bitflags::bitflags;
|
||||
use spin::Once;
|
||||
|
||||
use super::cpuid::cpuid;
|
||||
|
||||
/// Detects available x86 ISA extensions.
|
||||
pub(in crate::arch) fn init() {
|
||||
let mut global_isa_extensions = IsaExtensions::empty();
|
||||
|
||||
for ext_leaf in EXTENSION_TABLE.iter() {
|
||||
let Some(CpuidResult { ebx, ecx, edx, .. }) = cpuid(ext_leaf.leaf, ext_leaf.subleaf) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
for ext_data in ext_leaf.data.iter() {
|
||||
let bits = match ext_data.reg {
|
||||
Reg::Ebx => ebx,
|
||||
Reg::Ecx => ecx,
|
||||
Reg::Edx => edx,
|
||||
};
|
||||
if bits & (1 << ext_data.bit) != 0 {
|
||||
global_isa_extensions |= ext_data.flag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log::info!("Detected ISA extensions: {:?}", global_isa_extensions);
|
||||
|
||||
GLOBAL_ISA_EXTENSIONS.call_once(|| global_isa_extensions);
|
||||
}
|
||||
|
||||
/// Checks if the specified set of ISA extensions are available.
|
||||
pub fn has_extensions(required: IsaExtensions) -> bool {
|
||||
GLOBAL_ISA_EXTENSIONS.get().unwrap().contains(required)
|
||||
}
|
||||
|
||||
static GLOBAL_ISA_EXTENSIONS: Once<IsaExtensions> = Once::new();
|
||||
|
||||
macro_rules! define_isa_extensions {
|
||||
{ $(leaf $leaf:literal, subleaf $subleaf:literal => {
|
||||
$($name:ident, $reg:ident ($bit:literal), $doc:literal; )*
|
||||
})* } => {
|
||||
define_isa_extension_type! {
|
||||
$($($name, $doc;)*)*
|
||||
}
|
||||
|
||||
const EXTENSION_TABLE: &[ExtensionLeaf] = &[
|
||||
$(ExtensionLeaf {
|
||||
leaf: $leaf,
|
||||
subleaf: $subleaf,
|
||||
data: &[
|
||||
$(ExtensionData {
|
||||
reg: Reg::$reg,
|
||||
bit: $bit,
|
||||
flag: IsaExtensions::$name,
|
||||
},)*
|
||||
]
|
||||
},)*
|
||||
];
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! define_isa_extension_type {
|
||||
{ $($name:ident, $doc:literal;)* } => {
|
||||
bitflags! {
|
||||
/// x86 ISA extensions.
|
||||
pub struct IsaExtensions: u128 {
|
||||
$(
|
||||
#[doc = $doc]
|
||||
const $name = 1u128 << ${index()};
|
||||
)*
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Extensions that describe in a CPUID leaf.
|
||||
struct ExtensionLeaf {
|
||||
leaf: u32,
|
||||
subleaf: u32,
|
||||
data: &'static [ExtensionData],
|
||||
}
|
||||
|
||||
/// An extension and its position (i.e., the register and the bit) in the CPUID result.
|
||||
struct ExtensionData {
|
||||
reg: Reg,
|
||||
bit: u32,
|
||||
flag: IsaExtensions,
|
||||
}
|
||||
|
||||
enum Reg {
|
||||
Ebx,
|
||||
Ecx,
|
||||
Edx,
|
||||
}
|
||||
|
||||
define_isa_extensions! {
|
||||
leaf 1, subleaf 0 => {
|
||||
X2APIC, Ecx(21), "The processor supports x2APIC feature.";
|
||||
TSC_DEADLINE, Ecx(24), "The processor's local APIC timer supports \
|
||||
one-shot operation using a TSC deadline value.";
|
||||
XSAVE, Ecx(26), "The processor supports the XSAVE/XRSTOR \
|
||||
processor extended states feature, \
|
||||
the XSETBV/XGETBV instructions, and XCR0.";
|
||||
AVX, Ecx(28), "The processor supports the AVX instruction extensions.";
|
||||
RDRAND, Ecx(30), "The processor supports RDRAND instruction.";
|
||||
|
||||
XAPIC, Edx( 9), "APIC On-Chip.";
|
||||
}
|
||||
|
||||
leaf 7, subleaf 0 => {
|
||||
FSGSBASE, Ebx( 0), "Supports RDFSBASE/RDGSBASE/WRFSBASE/WRGSBASE.";
|
||||
AVX512F, Ebx(16), "Supports the AVX512F instruction extensions.";
|
||||
}
|
||||
}
|
||||
|
|
@ -4,4 +4,5 @@
|
|||
|
||||
pub mod context;
|
||||
pub mod cpuid;
|
||||
pub mod extension;
|
||||
pub mod local;
|
||||
|
|
|
|||
|
|
@ -27,9 +27,9 @@ impl X2Apic {
|
|||
}
|
||||
|
||||
pub(super) fn has_x2apic() -> bool {
|
||||
// x2apic::X2APIC::new()
|
||||
let value = unsafe { core::arch::x86_64::__cpuid(1) };
|
||||
value.ecx & 0x20_0000 != 0
|
||||
use crate::arch::cpu::extension::{has_extensions, IsaExtensions};
|
||||
|
||||
has_extensions(IsaExtensions::X2APIC)
|
||||
}
|
||||
|
||||
pub fn enable(&mut self) {
|
||||
|
|
|
|||
|
|
@ -34,6 +34,12 @@ impl XApic {
|
|||
})
|
||||
}
|
||||
|
||||
pub(super) fn has_xapic() -> bool {
|
||||
use crate::arch::cpu::extension::{has_extensions, IsaExtensions};
|
||||
|
||||
has_extensions(IsaExtensions::XAPIC)
|
||||
}
|
||||
|
||||
/// Reads a register from the MMIO region.
|
||||
fn read(&self, offset: u32) -> u32 {
|
||||
assert!(offset as usize % 4 == 0);
|
||||
|
|
@ -58,11 +64,6 @@ impl XApic {
|
|||
let svr: u32 = (1 << 8) | 15;
|
||||
self.write(xapic::XAPIC_SVR, svr);
|
||||
}
|
||||
|
||||
pub(super) fn has_xapic() -> bool {
|
||||
let value = unsafe { core::arch::x86_64::__cpuid(1) };
|
||||
value.edx & 0x100 != 0
|
||||
}
|
||||
}
|
||||
|
||||
impl super::Apic for XApic {
|
||||
|
|
|
|||
|
|
@ -18,17 +18,9 @@ pub(crate) mod task;
|
|||
pub mod timer;
|
||||
pub mod trap;
|
||||
|
||||
use io::construct_io_mem_allocator_builder;
|
||||
use spin::Once;
|
||||
use x86::cpuid::{CpuId, FeatureInfo};
|
||||
|
||||
#[cfg(feature = "cvm_guest")]
|
||||
pub(crate) mod tdx_guest;
|
||||
|
||||
use core::sync::atomic::Ordering;
|
||||
|
||||
use log::warn;
|
||||
|
||||
#[cfg(feature = "cvm_guest")]
|
||||
pub(crate) fn init_cvm_guest() {
|
||||
match ::tdx_guest::init_tdx() {
|
||||
|
|
@ -50,8 +42,6 @@ pub(crate) fn init_cvm_guest() {
|
|||
}
|
||||
}
|
||||
|
||||
static CPU_FEATURES: Once<FeatureInfo> = Once::new();
|
||||
|
||||
/// Architecture-specific initialization on the bootstrapping processor.
|
||||
///
|
||||
/// It should be called when the heap and frame allocators are available.
|
||||
|
|
@ -64,7 +54,7 @@ pub(crate) unsafe fn late_init_on_bsp() {
|
|||
// SAFETY: This function is only called once on BSP.
|
||||
unsafe { trap::init() };
|
||||
|
||||
let io_mem_builder = construct_io_mem_allocator_builder();
|
||||
let io_mem_builder = io::construct_io_mem_allocator_builder();
|
||||
|
||||
kernel::apic::init(&io_mem_builder).expect("APIC doesn't exist");
|
||||
kernel::irq::init(&io_mem_builder);
|
||||
|
|
@ -79,7 +69,7 @@ pub(crate) unsafe fn late_init_on_bsp() {
|
|||
} else {
|
||||
match iommu::init(&io_mem_builder) {
|
||||
Ok(_) => {}
|
||||
Err(err) => warn!("IOMMU initialization error:{:?}", err),
|
||||
Err(err) => log::warn!("IOMMU initialization error:{:?}", err),
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -112,6 +102,8 @@ pub(crate) fn interrupts_ack(irq_number: usize) {
|
|||
|
||||
/// Returns the frequency of TSC. The unit is Hz.
|
||||
pub fn tsc_freq() -> u64 {
|
||||
use core::sync::atomic::Ordering;
|
||||
|
||||
kernel::tsc::TSC_FREQ.load(Ordering::Acquire)
|
||||
}
|
||||
|
||||
|
|
@ -125,10 +117,16 @@ pub fn read_tsc() -> u64 {
|
|||
|
||||
/// Reads a hardware generated 64-bit random value.
|
||||
///
|
||||
/// Returns None if no random value was generated.
|
||||
/// Returns `None` if no random value was generated.
|
||||
pub fn read_random() -> Option<u64> {
|
||||
use core::arch::x86_64::_rdrand64_step;
|
||||
|
||||
use cpu::extension::{has_extensions, IsaExtensions};
|
||||
|
||||
if !has_extensions(IsaExtensions::RDRAND) {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Recommendation from "Intel® Digital Random Number Generator (DRNG) Software
|
||||
// Implementation Guide" - Section 5.2.1 and "Intel® 64 and IA-32 Architectures
|
||||
// Software Developer’s Manual" - Volume 1 - Section 7.3.17.1.
|
||||
|
|
@ -144,72 +142,46 @@ pub fn read_random() -> Option<u64> {
|
|||
None
|
||||
}
|
||||
|
||||
fn has_avx() -> bool {
|
||||
use core::arch::x86_64::{__cpuid, __cpuid_count};
|
||||
|
||||
let cpuid_result = unsafe { __cpuid(0) };
|
||||
if cpuid_result.eax < 1 {
|
||||
// CPUID function 1 is not supported
|
||||
return false;
|
||||
}
|
||||
|
||||
let cpuid_result = unsafe { __cpuid_count(1, 0) };
|
||||
// Check for AVX (bit 28 of ecx)
|
||||
cpuid_result.ecx & (1 << 28) != 0
|
||||
}
|
||||
|
||||
fn has_avx512() -> bool {
|
||||
use core::arch::x86_64::{__cpuid, __cpuid_count};
|
||||
|
||||
let cpuid_result = unsafe { __cpuid(0) };
|
||||
if cpuid_result.eax < 7 {
|
||||
// CPUID function 7 is not supported
|
||||
return false;
|
||||
}
|
||||
|
||||
let cpuid_result = unsafe { __cpuid_count(7, 0) };
|
||||
// Check for AVX-512 Foundation (bit 16 of ebx)
|
||||
cpuid_result.ebx & (1 << 16) != 0
|
||||
}
|
||||
|
||||
pub(crate) fn enable_cpu_features() {
|
||||
use cpu::extension::{has_extensions, IsaExtensions};
|
||||
use x86_64::registers::{control::Cr4Flags, model_specific::EferFlags, xcontrol::XCr0Flags};
|
||||
|
||||
CPU_FEATURES.call_once(|| {
|
||||
let cpuid = CpuId::new();
|
||||
cpuid.get_feature_info().unwrap()
|
||||
});
|
||||
cpu::extension::init();
|
||||
|
||||
let mut cr4 = x86_64::registers::control::Cr4::read();
|
||||
cr4 |= Cr4Flags::FSGSBASE
|
||||
| Cr4Flags::OSXSAVE
|
||||
| Cr4Flags::OSFXSR
|
||||
| Cr4Flags::OSXMMEXCPT_ENABLE
|
||||
| Cr4Flags::PAGE_GLOBAL;
|
||||
unsafe {
|
||||
x86_64::registers::control::Cr4::write(cr4);
|
||||
cr4 |= Cr4Flags::OSFXSR | Cr4Flags::OSXMMEXCPT_ENABLE | Cr4Flags::PAGE_GLOBAL;
|
||||
if has_extensions(IsaExtensions::XSAVE) {
|
||||
cr4 |= Cr4Flags::OSXSAVE;
|
||||
}
|
||||
|
||||
let mut xcr0 = x86_64::registers::xcontrol::XCr0::read();
|
||||
|
||||
xcr0 |= XCr0Flags::SSE;
|
||||
|
||||
if has_avx() {
|
||||
xcr0 |= XCr0Flags::AVX;
|
||||
// For now, we unconditionally require the `rdfsbase`, `wrfsbase`, `rdgsbase`, and `wrgsbase`
|
||||
// instructions because they are used when switching contexts, getting the address of a
|
||||
// CPU-local variable, e.t.c. Meanwhile, this is at a very early stage of the boot process, so
|
||||
// we want to avoid failing immediately even if we cannot enable these instructions (though the
|
||||
// kernel will certainly fail later when they are absent).
|
||||
//
|
||||
// Note that this also enables the userspace to control their own FS/GS bases, which requires
|
||||
// the kernel to properly deal with the arbitrary base values set by the userspace program.
|
||||
if has_extensions(IsaExtensions::FSGSBASE) {
|
||||
cr4 |= Cr4Flags::FSGSBASE;
|
||||
}
|
||||
unsafe { x86_64::registers::control::Cr4::write(cr4) };
|
||||
|
||||
if has_avx512() {
|
||||
xcr0 |= XCr0Flags::OPMASK | XCr0Flags::ZMM_HI256 | XCr0Flags::HI16_ZMM;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
x86_64::registers::xcontrol::XCr0::write(xcr0);
|
||||
if has_extensions(IsaExtensions::XSAVE) {
|
||||
let mut xcr0 = x86_64::registers::xcontrol::XCr0::read();
|
||||
xcr0 |= XCr0Flags::SSE;
|
||||
if has_extensions(IsaExtensions::AVX) {
|
||||
xcr0 |= XCr0Flags::AVX;
|
||||
}
|
||||
if has_extensions(IsaExtensions::AVX512F) {
|
||||
xcr0 |= XCr0Flags::OPMASK | XCr0Flags::ZMM_HI256 | XCr0Flags::HI16_ZMM;
|
||||
}
|
||||
unsafe { x86_64::registers::xcontrol::XCr0::write(xcr0) };
|
||||
}
|
||||
|
||||
cpu::context::enable_essential_features();
|
||||
|
||||
unsafe {
|
||||
// enable non-executable page protection
|
||||
// Enable non-executable page protection.
|
||||
x86_64::registers::model_specific::Efer::update(|efer| {
|
||||
*efer |= EferFlags::NO_EXECUTE_ENABLE;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -54,11 +54,9 @@ pub(super) fn timer_callback() {
|
|||
|
||||
/// Determines if the current system supports tsc_deadline mode APIC timer
|
||||
fn is_tsc_deadline_mode_supported() -> bool {
|
||||
use x86::cpuid::cpuid;
|
||||
use crate::arch::cpu::extension::{has_extensions, IsaExtensions};
|
||||
|
||||
const TSC_DEADLINE_MODE_SUPPORT: u32 = 1 << 24;
|
||||
let cpuid = cpuid!(1);
|
||||
(cpuid.ecx & TSC_DEADLINE_MODE_SUPPORT) > 0
|
||||
has_extensions(IsaExtensions::TSC_DEADLINE)
|
||||
}
|
||||
|
||||
fn init_timer(timer_irq: &IrqLine) {
|
||||
|
|
|
|||
|
|
@ -18,10 +18,8 @@
|
|||
|
||||
use core::arch::global_asm;
|
||||
|
||||
use x86::cpuid::CpuId;
|
||||
use x86_64::{
|
||||
registers::{
|
||||
control::{Cr4, Cr4Flags},
|
||||
model_specific::{Efer, EferFlags, LStar, SFMask},
|
||||
rflags::RFlags,
|
||||
},
|
||||
|
|
@ -43,13 +41,8 @@ global_asm!(
|
|||
/// The caller needs to ensure that `gdt::init` has been called before, so the segment selectors
|
||||
/// used in the `syscall` and `sysret` instructions have been properly initialized.
|
||||
pub(super) unsafe fn init() {
|
||||
let cpuid = CpuId::new();
|
||||
|
||||
assert!(cpuid
|
||||
.get_extended_processor_and_feature_identifiers()
|
||||
.unwrap()
|
||||
.has_syscall_sysret());
|
||||
assert!(cpuid.get_extended_feature_info().unwrap().has_fsgsbase());
|
||||
// We now assume that all x86-64 CPUs should support the `syscall` and `sysret` instructions.
|
||||
// Otherwise, we should check `has_extensions(IsaExtensions::SYSCALL)` here.
|
||||
|
||||
// Flags to clear on syscall.
|
||||
//
|
||||
|
|
@ -69,15 +62,6 @@ pub(super) unsafe fn init() {
|
|||
efer.insert(EferFlags::SYSTEM_CALL_EXTENSIONS);
|
||||
});
|
||||
}
|
||||
|
||||
// SAFETY: Enabling the `rdfsbase`, `wrfsbase`, `rdgsbase`, and `wrgsbase` instructions is safe
|
||||
// as long as the kernel properly deals with the arbitrary base values set by the userspace
|
||||
// program. (FIXME: Do we really need to unconditionally enable them?)
|
||||
unsafe {
|
||||
Cr4::update(|cr4| {
|
||||
cr4.insert(Cr4Flags::FSGSBASE);
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
extern "sysv64" {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#![feature(iter_from_coroutine)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(linkage)]
|
||||
#![feature(macro_metavar_expr)]
|
||||
#![feature(min_specialization)]
|
||||
#![feature(negative_impls)]
|
||||
#![feature(ptr_metadata)]
|
||||
|
|
|
|||
Loading…
Reference in New Issue