use crate::{error::EfiError, pi::protocols::cpu_arch::CpuFlushType};
use core::arch::asm;
use core::num::NonZeroU64;
use r_efi::efi;
pub(crate) struct X64;
impl super::ArchSupport for X64 {}
const CACHE_WRITEBACK_GRANULE: u32 = 4;
pub unsafe fn io_out8(port: u16, value: u8) {
unsafe {
core::arch::asm!(
"out dx, al",
in("dx") port,
in("al") value,
options(nostack, nomem, preserves_flags)
);
}
}
pub fn rdtsc() -> u64 {
let lo: u32;
let hi: u32;
unsafe { core::arch::asm!("rdtsc", out("eax") lo, out("edx") hi, options(nostack, nomem)) };
((hi as u64) << 32) | lo as u64
}
impl super::Interrupts for X64 {
fn enable_interrupts() {
unsafe {
asm!("sti", options(nostack, nomem, preserves_flags));
}
}
fn disable_interrupts() {
unsafe {
asm!("cli", options(nostack, nomem, preserves_flags));
}
}
fn interrupts_enabled() -> bool {
let eflags: u64;
const IF: u64 = 0x200;
unsafe {
asm!("pushfq; pop {}", out(reg) eflags);
}
eflags & IF != 0
}
fn enable_interrupts_and_sleep() {
unsafe {
asm!("sti; hlt", options(nostack, nomem, preserves_flags));
}
}
}
impl super::CacheMgmt for X64 {
fn flush_data_cache(_start: efi::PhysicalAddress, _length: u64, flush_type: CpuFlushType) -> Result<(), EfiError> {
match flush_type {
CpuFlushType::EfiCpuFlushTypeWriteBackInvalidate => {
asm_wbinvd();
Ok(())
}
CpuFlushType::EfiCpuFlushTypeInvalidate => {
asm_invd();
Ok(())
}
_ => Err(EfiError::Unsupported),
}
}
fn cache_writeback_granule() -> u32 {
CACHE_WRITEBACK_GRANULE
}
}
impl super::Timer for X64 {
fn get_timer_value() -> u64 {
rdtsc()
}
fn get_timer_frequency() -> Option<NonZeroU64> {
#[allow(unused_unsafe)]
let max_leaf = unsafe { core::arch::x86_64::__cpuid(0) }.eax;
if max_leaf >= 0x15 {
#[allow(unused_unsafe)]
let core::arch::x86_64::CpuidResult { eax, ebx, ecx, .. } = unsafe { core::arch::x86_64::__cpuid(0x15) };
if eax != 0 && ebx != 0 && ecx != 0 {
return NonZeroU64::new((ecx as u64 * ebx as u64) / eax as u64);
}
}
if max_leaf >= 0x16 {
#[allow(unused_unsafe)]
let core::arch::x86_64::CpuidResult { eax, .. } = unsafe { core::arch::x86_64::__cpuid(0x16) };
if eax != 0 {
return NonZeroU64::new((eax * 1_000_000) as u64);
}
}
None
}
}
fn asm_wbinvd() {
unsafe {
asm!("wbinvd");
}
}
fn asm_invd() {
unsafe {
asm!("invd");
}
}