#![allow(dead_code)]
#[cfg(feature = "acpi")]
use arch::x86_64::kernel::acpi;
use arch::x86_64::kernel::idt;
use arch::x86_64::kernel::irq;
use arch::x86_64::kernel::pic;
use arch::x86_64::kernel::pit;
use arch::x86_64::kernel::BOOT_INFO;
use core::sync::atomic::spin_loop_hint;
use core::{fmt, intrinsics, u32};
use environment;
use x86::controlregs::*;
use x86::cpuid::*;
use x86::msr::*;
use x86::time::*;
const IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP: u64 = 1 << 16;
const IA32_MISC_ENABLE_SPEEDSTEP_LOCK: u64 = 1 << 20;
const IA32_MISC_ENABLE_TURBO_DISABLE: u64 = 1 << 38;
const EFER_SCE: u64 = 1 << 0;
const EFER_LME: u64 = 1 << 8;
const EFER_LMA: u64 = 1 << 10;
const EFER_NXE: u64 = 1 << 11;
const EFER_SVME: u64 = 1 << 12;
const EFER_LMSLE: u64 = 1 << 13;
const EFER_FFXSR: u64 = 1 << 14;
const EFER_TCE: u64 = 1 << 15;
static mut CPU_FREQUENCY: CpuFrequency = CpuFrequency::new();
static mut CPU_SPEEDSTEP: CpuSpeedStep = CpuSpeedStep::new();
static mut PHYSICAL_ADDRESS_BITS: u8 = 0;
static mut LINEAR_ADDRESS_BITS: u8 = 0;
static mut MEASUREMENT_TIMER_TICKS: u64 = 0;
static mut SUPPORTS_1GIB_PAGES: bool = false;
static mut SUPPORTS_AVX: bool = false;
static mut SUPPORTS_RDRAND: bool = false;
static mut SUPPORTS_TSC_DEADLINE: bool = false;
static mut SUPPORTS_X2APIC: bool = false;
static mut SUPPORTS_XSAVE: bool = false;
static mut SUPPORTS_FSGS: bool = false;
static mut TIMESTAMP_FUNCTION: unsafe fn() -> u64 = get_timestamp_rdtsc;
#[repr(C, align(16))]
pub struct XSaveLegacyRegion {
pub fpu_control_word: u16,
pub fpu_status_word: u16,
pub fpu_tag_word: u16,
pub fpu_opcode: u16,
pub fpu_instruction_pointer: u32,
pub fpu_instruction_pointer_high_or_cs: u32,
pub fpu_data_pointer: u32,
pub fpu_data_pointer_high_or_ds: u32,
pub mxcsr: u32,
pub mxcsr_mask: u32,
pub st_space: [u8; 8 * 16],
pub xmm_space: [u8; 16 * 16],
pub padding: [u8; 96],
}
#[repr(C)]
pub struct XSaveHeader {
pub xstate_bv: u64,
pub xcomp_bv: u64,
pub reserved: [u64; 6],
}
#[repr(C)]
pub struct XSaveAVXState {
pub ymmh_space: [u8; 16 * 16],
}
#[repr(C)]
pub struct XSaveLWPState {
pub lwpcb_address: u64,
pub flags: u32,
pub buffer_head_offset: u32,
pub buffer_base: u64,
pub buffer_size: u32,
pub filters: u32,
pub saved_event_record: [u64; 4],
pub event_counter: [u32; 16],
}
#[repr(C)]
pub struct XSaveBndregs {
pub bound_registers: [u8; 4 * 16],
}
#[repr(C)]
pub struct XSaveBndcsr {
pub bndcfgu_register: u64,
pub bndstatus_register: u64,
}
#[repr(C, align(64))]
pub struct FPUState {
pub legacy_region: XSaveLegacyRegion,
pub header: XSaveHeader,
pub avx_state: XSaveAVXState,
pub lwp_state: XSaveLWPState,
pub bndregs: XSaveBndregs,
pub bndcsr: XSaveBndcsr,
}
impl FPUState {
pub const fn new() -> Self {
Self {
legacy_region: XSaveLegacyRegion {
fpu_control_word: 0x37F,
fpu_status_word: 0,
fpu_tag_word: 0xFFFF,
fpu_opcode: 0,
fpu_instruction_pointer: 0,
fpu_instruction_pointer_high_or_cs: 0,
fpu_data_pointer: 0,
fpu_data_pointer_high_or_ds: 0,
mxcsr: 0x1F80,
mxcsr_mask: 0,
st_space: [0; 8 * 16],
xmm_space: [0; 16 * 16],
padding: [0; 96],
},
header: XSaveHeader {
xstate_bv: 0,
xcomp_bv: 0,
reserved: [0; 6],
},
avx_state: XSaveAVXState {
ymmh_space: [0; 16 * 16],
},
lwp_state: XSaveLWPState {
lwpcb_address: 0,
flags: 0,
buffer_head_offset: 0,
buffer_base: 0,
buffer_size: 0,
filters: 0,
saved_event_record: [0; 4],
event_counter: [0; 16],
},
bndregs: XSaveBndregs {
bound_registers: [0; 4 * 16],
},
bndcsr: XSaveBndcsr {
bndcfgu_register: 0,
bndstatus_register: 0,
},
}
}
pub fn restore(&self) {
if supports_xsave() {
let bitmask = u32::MAX;
unsafe {
asm!("xrstorq $0" :: "*m"(self as *const Self), "{eax}"(bitmask), "{edx}"(bitmask) :: "volatile");
}
} else {
unsafe {
asm!("fxrstor $0" :: "*m"(self as *const Self) :: "volatile");
}
}
}
pub fn save(&mut self) {
if supports_xsave() {
let bitmask: u32 = u32::MAX;
unsafe {
asm!("xsaveq $0" : "=*m"(self as *mut Self) : "{eax}"(bitmask), "{edx}"(bitmask) : "memory" : "volatile");
}
} else {
unsafe {
asm!("fxsave $0; fnclex" : "=*m"(self as *mut Self) :: "memory" : "volatile");
}
}
}
pub fn restore_common(&self) {
unsafe {
asm!("fxrstor $0" :: "*m"(self as *const Self) :: "volatile");
}
}
pub fn save_common(&mut self) {
unsafe {
asm!("fxsave $0; fnclex" : "=*m"(self as *mut Self) :: "memory" : "volatile");
}
}
}
enum CpuFrequencySources {
Invalid,
CommandLine,
CpuIdBrandString,
Measurement,
Hypervisor,
CpuId,
CpuIdTscInfo,
}
impl fmt::Display for CpuFrequencySources {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self {
CpuFrequencySources::CommandLine => write!(f, "Command Line"),
CpuFrequencySources::CpuIdBrandString => write!(f, "CpuId Brand String"),
CpuFrequencySources::Measurement => write!(f, "Measurement"),
CpuFrequencySources::Hypervisor => write!(f, "Hypervisor"),
CpuFrequencySources::CpuId => write!(f, "CpuId"),
CpuFrequencySources::CpuIdTscInfo => write!(f, "CpuId Tsc Info"),
_ => panic!("Attempted to print an invalid CPU Frequency Source"),
}
}
}
struct CpuFrequency {
mhz: u16,
source: CpuFrequencySources,
}
impl CpuFrequency {
const fn new() -> Self {
CpuFrequency {
mhz: 0,
source: CpuFrequencySources::Invalid,
}
}
unsafe fn detect_from_cmdline(&mut self) -> Result<(), ()> {
let mhz = environment::get_command_line_cpu_frequency();
if mhz > 0 {
self.mhz = mhz;
self.source = CpuFrequencySources::CommandLine;
Ok(())
} else {
Err(())
}
}
unsafe fn detect_from_cpuid(&mut self, cpuid: &CpuId) -> Result<(), ()> {
let processor_frequency_info = cpuid.get_processor_frequency_info();
if processor_frequency_info.is_some() {
self.mhz = processor_frequency_info.unwrap().processor_base_frequency();
self.source = CpuFrequencySources::CpuId;
Ok(())
} else {
Err(())
}
}
unsafe fn detect_from_cpid_tsc_info(&mut self, cpuid: &CpuId) -> Result<(), ()> {
let tsc_info = cpuid.get_tsc_info();
if tsc_info.is_some() {
self.mhz = (tsc_info.unwrap().tsc_frequency() / 1000000u64) as u16;
self.source = CpuFrequencySources::CpuIdTscInfo;
Ok(())
} else {
Err(())
}
}
unsafe fn detect_from_cpuid_brand_string(&mut self, cpuid: &CpuId) -> Result<(), ()> {
let extended_function_info = cpuid
.get_extended_function_info()
.expect("CPUID Extended Function Info not available!");
let brand_string = extended_function_info
.processor_brand_string()
.expect("CPUID Brand String not available!");
let ghz_find = brand_string.find("GHz");
if ghz_find.is_some() {
let index = ghz_find.unwrap() - 4;
let thousand_char = brand_string.chars().nth(index).unwrap();
let decimal_char = brand_string.chars().nth(index + 1).unwrap();
let hundred_char = brand_string.chars().nth(index + 2).unwrap();
let ten_char = brand_string.chars().nth(index + 3).unwrap();
if let (Some(thousand), '.', Some(hundred), Some(ten)) = (
thousand_char.to_digit(10),
decimal_char,
hundred_char.to_digit(10),
ten_char.to_digit(10),
) {
self.mhz = (thousand * 1000 + hundred * 100 + ten * 10) as u16;
self.source = CpuFrequencySources::CpuIdBrandString;
return Ok(());
}
}
Err(())
}
unsafe fn detect_from_hypervisor(&mut self) -> Result<(), ()> {
let cpu_freq = intrinsics::volatile_load(&(*BOOT_INFO).cpu_freq);
if cpu_freq > 0 {
self.mhz = cpu_freq as u16;
self.source = CpuFrequencySources::Hypervisor;
return Ok(());
}
Err(())
}
extern "x86-interrupt" fn measure_frequency_timer_handler(
_stack_frame: &mut irq::ExceptionStackFrame,
) {
unsafe {
MEASUREMENT_TIMER_TICKS += 1;
}
pic::eoi(pit::PIT_INTERRUPT_NUMBER);
}
#[cfg(test)]
fn measure_frequency(&mut self) -> Result<(), ()> {
self.source = CpuFrequencySources::Measurement;
Ok(())
}
#[cfg(not(test))]
fn measure_frequency(&mut self) -> Result<(), ()> {
if environment::is_uhyve() {
return Err(());
}
let tick_count = 3;
let measurement_frequency = 100;
idt::set_gate(
pit::PIT_INTERRUPT_NUMBER,
Self::measure_frequency_timer_handler as usize,
0,
);
pit::init(measurement_frequency);
irq::enable();
let first_tick = unsafe { intrinsics::volatile_load(&MEASUREMENT_TIMER_TICKS) };
let start_tick = loop {
let tick = unsafe { intrinsics::volatile_load(&MEASUREMENT_TIMER_TICKS) };
if tick != first_tick {
break tick;
}
spin_loop_hint();
};
let start = get_timestamp();
loop {
let tick = unsafe { intrinsics::volatile_load(&MEASUREMENT_TIMER_TICKS) };
if tick - start_tick >= tick_count {
break;
}
spin_loop_hint();
}
let end = get_timestamp();
irq::disable();
pit::deinit();
let cycle_count = end - start;
self.mhz = (measurement_frequency * cycle_count / (1_000_000 * tick_count)) as u16;
self.source = CpuFrequencySources::Measurement;
Ok(())
}
unsafe fn detect(&mut self) {
let cpuid = CpuId::new();
self.detect_from_cpuid(&cpuid)
.or_else(|_e| self.detect_from_cpid_tsc_info(&cpuid))
.or_else(|_e| self.detect_from_hypervisor())
.or_else(|_e| self.detect_from_cpuid_brand_string(&cpuid))
.or_else(|_e| self.measure_frequency())
.expect("Could not determine the processor frequency");
}
fn get(&self) -> u16 {
self.mhz
}
}
impl fmt::Display for CpuFrequency {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} MHz (from {})", self.mhz, self.source)
}
}
struct CpuFeaturePrinter {
feature_info: FeatureInfo,
extended_feature_info: ExtendedFeatures,
extended_function_info: ExtendedFunctionInfo,
}
impl CpuFeaturePrinter {
fn new(cpuid: &CpuId) -> Self {
CpuFeaturePrinter {
feature_info: cpuid
.get_feature_info()
.expect("CPUID Feature Info not available!"),
extended_feature_info: cpuid
.get_extended_feature_info()
.expect("CPUID Extended Feature Info not available!"),
extended_function_info: cpuid
.get_extended_function_info()
.expect("CPUID Extended Function Info not available!"),
}
}
}
impl fmt::Display for CpuFeaturePrinter {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.feature_info.has_mmx() {
write!(f, "MMX ")?;
}
if self.feature_info.has_sse() {
write!(f, "SSE ")?;
}
if self.feature_info.has_sse2() {
write!(f, "SSE2 ")?;
}
if self.feature_info.has_sse3() {
write!(f, "SSE3 ")?;
}
if self.feature_info.has_ssse3() {
write!(f, "SSSE3 ")?;
}
if self.feature_info.has_sse41() {
write!(f, "SSE4.1 ")?;
}
if self.feature_info.has_sse42() {
write!(f, "SSE4.2 ")?;
}
if self.feature_info.has_avx() {
write!(f, "AVX ")?;
}
if self.feature_info.has_eist() {
write!(f, "EIST ")?;
}
if self.feature_info.has_aesni() {
write!(f, "AESNI ")?;
}
if self.feature_info.has_rdrand() {
write!(f, "RDRAND ")?;
}
if self.feature_info.has_fma() {
write!(f, "FMA ")?;
}
if self.feature_info.has_movbe() {
write!(f, "MOVBE ")?;
}
if self.feature_info.has_mce() {
write!(f, "MCE ")?;
}
if self.feature_info.has_fxsave_fxstor() {
write!(f, "FXSR ")?;
}
if self.feature_info.has_xsave() {
write!(f, "XSAVE ")?;
}
if self.feature_info.has_vmx() {
write!(f, "VMX ")?;
}
if self.extended_function_info.has_rdtscp() {
write!(f, "RDTSCP ")?;
}
if self.feature_info.has_monitor_mwait() {
write!(f, "MWAIT ")?;
}
if self.feature_info.has_clflush() {
write!(f, "CLFLUSH ")?;
}
if self.feature_info.has_dca() {
write!(f, "DCA ")?;
}
if self.feature_info.has_tsc_deadline() {
write!(f, "TSC-DEADLINE ")?;
}
if self.feature_info.has_x2apic() {
write!(f, "X2APIC ")?;
}
if self.extended_feature_info.has_avx2() {
write!(f, "AVX2 ")?;
}
if self.extended_feature_info.has_avx512f() {
write!(f, "AVX512F ")?;
}
if self.extended_feature_info.has_avx512dq() {
write!(f, "AVX512DQ ")?;
}
if self.extended_feature_info.has_avx512_ifma() {
write!(f, "AVX512IFMA ")?;
}
if self.extended_feature_info.has_avx512pf() {
write!(f, "AVX512PF ")?;
}
if self.extended_feature_info.has_avx512er() {
write!(f, "AVX512ER ")?;
}
if self.extended_feature_info.has_avx512cd() {
write!(f, "AVX512CD ")?;
}
if self.extended_feature_info.has_avx512bw() {
write!(f, "AVX512BW ")?;
}
if self.extended_feature_info.has_avx512vl() {
write!(f, "AVX512VL ")?;
}
if self.extended_feature_info.has_bmi1() {
write!(f, "BMI1 ")?;
}
if self.extended_feature_info.has_bmi2() {
write!(f, "BMI2 ")?;
}
if self.extended_feature_info.has_rtm() {
write!(f, "RTM ")?;
}
if self.extended_feature_info.has_hle() {
write!(f, "HLE ")?;
}
if self.extended_feature_info.has_mpx() {
write!(f, "MPX ")?;
}
if self.extended_feature_info.has_pku() {
write!(f, "PKU ")?;
}
if self.extended_feature_info.has_ospke() {
write!(f, "OSPKE ")?;
}
if self.extended_feature_info.has_fsgsbase() {
write!(f, "FSGSBASE ")?;
}
if self.extended_feature_info.has_sgx() {
write!(f, "SGX ")?;
}
Ok(())
}
}
struct CpuSpeedStep {
eist_available: bool,
eist_enabled: bool,
eist_locked: bool,
energy_bias_preference: bool,
max_pstate: u8,
is_turbo_pstate: bool,
}
impl CpuSpeedStep {
const fn new() -> Self {
CpuSpeedStep {
eist_available: false,
eist_enabled: false,
eist_locked: false,
energy_bias_preference: false,
max_pstate: 0,
is_turbo_pstate: false,
}
}
fn detect_features(&mut self, cpuid: &CpuId) {
let feature_info = cpuid
.get_feature_info()
.expect("CPUID Feature Info not available!");
self.eist_available = feature_info.has_eist();
if !self.eist_available {
return;
}
let misc = unsafe { rdmsr(IA32_MISC_ENABLE) };
self.eist_enabled = (misc & IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP) > 0;
self.eist_locked = (misc & IA32_MISC_ENABLE_SPEEDSTEP_LOCK) > 0;
if !self.eist_enabled || self.eist_locked {
return;
}
self.max_pstate = (unsafe { rdmsr(MSR_PLATFORM_INFO) } >> 8) as u8;
if (misc & IA32_MISC_ENABLE_TURBO_DISABLE) == 0 {
let turbo_pstate = unsafe { rdmsr(MSR_TURBO_RATIO_LIMIT) } as u8;
if turbo_pstate > self.max_pstate {
self.max_pstate = turbo_pstate;
self.is_turbo_pstate = true;
}
}
if let Some(thermal_power_info) = cpuid.get_thermal_power_info() {
self.energy_bias_preference = thermal_power_info.has_energy_bias_pref();
}
}
fn configure(&self) {
if !self.eist_available || !self.eist_enabled || self.eist_locked {
return;
}
if self.energy_bias_preference {
unsafe {
wrmsr(IA32_ENERGY_PERF_BIAS, 0);
}
}
let mut perf_ctl_mask = u64::from(self.max_pstate) << 8;
if self.is_turbo_pstate {
perf_ctl_mask |= 1 << 32;
}
unsafe {
wrmsr(IA32_PERF_CTL, perf_ctl_mask);
}
}
}
impl fmt::Display for CpuSpeedStep {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.eist_available {
write!(f, "Available, ")?;
if !self.eist_enabled {
write!(f, "but disabled")?;
} else if self.eist_locked {
write!(f, "but locked")?;
} else {
write!(f, "enabled with maximum P-State {}", self.max_pstate)?;
if self.is_turbo_pstate {
write!(f, " (Turbo Mode)")?;
}
if self.energy_bias_preference {
write!(f, ", disabled Performance/Energy Bias")?;
}
}
} else {
write!(f, "Not Available")?;
}
Ok(())
}
}
pub fn detect_features() {
let cpuid = CpuId::new();
let feature_info = cpuid
.get_feature_info()
.expect("CPUID Feature Info not available!");
let extended_feature_info = cpuid
.get_extended_feature_info()
.expect("CPUID Extended Feature Info not available!");
let extended_function_info = cpuid
.get_extended_function_info()
.expect("CPUID Extended Function Info not available!");
unsafe {
PHYSICAL_ADDRESS_BITS = extended_function_info
.physical_address_bits()
.expect("CPUID Physical Address Bits not available!");
LINEAR_ADDRESS_BITS = extended_function_info
.linear_address_bits()
.expect("CPUID Linear Address Bits not available!");
SUPPORTS_1GIB_PAGES = extended_function_info.has_1gib_pages();
SUPPORTS_AVX = feature_info.has_avx();
SUPPORTS_RDRAND = feature_info.has_rdrand();
SUPPORTS_TSC_DEADLINE = feature_info.has_tsc_deadline();
SUPPORTS_X2APIC = feature_info.has_x2apic();
SUPPORTS_XSAVE = feature_info.has_xsave();
SUPPORTS_FSGS = extended_feature_info.has_fsgsbase();
if extended_function_info.has_rdtscp() {
TIMESTAMP_FUNCTION = get_timestamp_rdtscp;
}
CPU_SPEEDSTEP.detect_features(&cpuid);
}
}
pub fn configure() {
unsafe {
wrmsr(IA32_EFER, rdmsr(IA32_EFER) | EFER_LMA | EFER_SCE | EFER_NXE);
}
let mut cr0 = unsafe { cr0() };
cr0.insert(Cr0::CR0_MONITOR_COPROCESSOR | Cr0::CR0_NUMERIC_ERROR);
cr0.remove(Cr0::CR0_EMULATE_COPROCESSOR);
cr0.insert(Cr0::CR0_TASK_SWITCHED);
cr0.insert(Cr0::CR0_WRITE_PROTECT);
debug!("Set CR0 to 0x{:x}", cr0);
unsafe {
cr0_write(cr0);
}
let mut cr4 = unsafe { cr4() };
cr4.insert(Cr4::CR4_ENABLE_MACHINE_CHECK);
cr4.insert(Cr4::CR4_ENABLE_SSE | Cr4::CR4_UNMASKED_SSE);
if supports_xsave() {
cr4.insert(Cr4::CR4_ENABLE_OS_XSAVE);
}
if supports_fsgs() {
cr4.insert(Cr4::CR4_ENABLE_FSGSBASE);
} else {
error!("libhermit-rs requires the CPU feature FSGSBASE");
loop {
spin_loop_hint();
}
}
debug!("Set CR4 to 0x{:x}", cr4);
unsafe {
cr4_write(cr4);
}
if supports_xsave() {
let mut xcr0 = unsafe { xcr0() };
xcr0.insert(Xcr0::XCR0_FPU_MMX_STATE | Xcr0::XCR0_SSE_STATE);
if supports_avx() {
xcr0.insert(Xcr0::XCR0_AVX_STATE);
}
unsafe {
xcr0_write(xcr0);
}
}
writefs(0);
unsafe {
CPU_SPEEDSTEP.configure();
}
}
pub fn detect_frequency() {
unsafe {
CPU_FREQUENCY.detect();
}
}
pub fn print_information() {
infoheader!(" CPU INFORMATION ");
let cpuid = CpuId::new();
let extended_function_info = cpuid
.get_extended_function_info()
.expect("CPUID Extended Function Info not available!");
let brand_string = extended_function_info
.processor_brand_string()
.expect("CPUID Brand String not available!");
let feature_printer = CpuFeaturePrinter::new(&cpuid);
infoentry!("Model", brand_string);
unsafe {
infoentry!("Frequency", CPU_FREQUENCY);
infoentry!("SpeedStep Technology", CPU_SPEEDSTEP);
}
infoentry!("Features", feature_printer);
infoentry!(
"Physical Address Width",
"{} bits",
get_physical_address_bits()
);
infoentry!("Linear Address Width", "{} bits", get_linear_address_bits());
infoentry!(
"Supports 1GiB Pages",
if supports_1gib_pages() { "Yes" } else { "No" }
);
infofooter!();
}
pub fn generate_random_number() -> Option<u32> {
if unsafe { SUPPORTS_RDRAND } {
let value: u32;
unsafe {
asm!("rdrand $0" : "=r"(value) ::: "volatile");
}
Some(value)
} else {
None
}
}
#[inline]
pub fn get_linear_address_bits() -> u8 {
unsafe { LINEAR_ADDRESS_BITS }
}
#[inline]
pub fn get_physical_address_bits() -> u8 {
unsafe { PHYSICAL_ADDRESS_BITS }
}
#[inline]
pub fn supports_1gib_pages() -> bool {
unsafe { SUPPORTS_1GIB_PAGES }
}
#[inline]
pub fn supports_avx() -> bool {
unsafe { SUPPORTS_AVX }
}
#[inline]
pub fn supports_tsc_deadline() -> bool {
unsafe { SUPPORTS_TSC_DEADLINE }
}
#[inline]
pub fn supports_x2apic() -> bool {
unsafe { SUPPORTS_X2APIC }
}
#[inline]
pub fn supports_xsave() -> bool {
unsafe { SUPPORTS_XSAVE }
}
#[inline]
pub fn supports_fsgs() -> bool {
unsafe { SUPPORTS_FSGS }
}
#[inline(always)]
pub fn msb(value: u64) -> Option<u64> {
if value > 0 {
let ret: u64;
unsafe {
asm!("bsr $1, $0" : "=r"(ret) : "r"(value) : "cc" : "volatile");
}
Some(ret)
} else {
None
}
}
pub fn halt() {
unsafe {
asm!("hlt" :::: "volatile");
}
}
pub fn shutdown() -> ! {
info!("Shutting down system");
#[cfg(feature = "acpi")]
acpi::poweroff();
loop {
halt();
}
}
pub fn get_timer_ticks() -> u64 {
get_timestamp() / u64::from(get_frequency())
}
pub fn get_frequency() -> u16 {
unsafe { CPU_FREQUENCY.get() }
}
#[inline]
pub fn readfs() -> usize {
let val: u64;
unsafe {
asm!("rdfsbase $0" : "=r"(val) ::: "volatile");
}
val as usize
}
#[inline]
pub fn readgs() -> usize {
let val: u64;
unsafe {
asm!("rdgsbase $0" : "=r"(val) ::: "volatile");
}
val as usize
}
#[inline]
pub fn writefs(fs: usize) {
unsafe {
asm!("wrfsbase $0" :: "r"(fs as u64) :: "volatile");
}
}
#[inline]
pub fn writegs(gs: usize) {
unsafe {
asm!("wrgsbase $0" :: "r"(gs as u64) :: "volatile");
}
}
#[inline]
pub fn get_timestamp() -> u64 {
unsafe { TIMESTAMP_FUNCTION() }
}
#[inline]
unsafe fn get_timestamp_rdtsc() -> u64 {
asm!("lfence" ::: "memory" : "volatile");
let value = rdtsc();
asm!("lfence" ::: "memory" : "volatile");
value
}
#[inline]
unsafe fn get_timestamp_rdtscp() -> u64 {
let value = rdtscp();
asm!("lfence" ::: "memory" : "volatile");
value
}
#[inline]
pub fn udelay(usecs: u64) {
let end = get_timestamp() + u64::from(get_frequency()) * usecs;
while get_timestamp() < end {
spin_loop_hint();
}
}