Spectral/src/asm.rs
2026-04-11 20:23:27 -04:00

69 lines
2.3 KiB
Rust

// Inline asm wrappers.
//
// Userspace-callable: rdtsc, cpuid, svm_supported
// Ring-0 stubs: read_cr3, write_cr3, vmload, vmsave, vmrun, rdmsr, wrmsr
// these are here for when this code runs in a kernel context
// Calling them from userspace will #GP or #UD
/// Read the hardware timestamp counter (TSC)
/// Used for timing guest memory access patterns (cache-timing side channels)
#[inline(always)]
pub fn rdtsc() -> u64 {
let lo: u32;
let hi: u32;
unsafe {
core::arch::asm!(
"rdtsc",
out("eax") lo,
out("edx") hi,
options(nomem, nostack, preserves_flags),
);
}
((hi as u64) << 32) | lo as u64
}
/// CPUID — leaf + subleaf, returns (eax, ebx, ecx, edx).
/// rbx is LLVM-reserved so we save/restore it around the instruction.
pub fn cpuid(leaf: u32, subleaf: u32) -> (u32, u32, u32, u32) {
let (eax, ebx, ecx, edx): (u32, u32, u32, u32);
unsafe {
core::arch::asm!(
"push rbx",
"cpuid",
"mov {ebx_out:e}, ebx",
"pop rbx",
inout("eax") leaf => eax,
inout("ecx") subleaf => ecx,
ebx_out = out(reg) ebx,
out("edx") edx,
options(nomem, nostack, preserves_flags),
);
}
(eax, ebx, ecx, edx)
}
/// Possible delete only used to check for AMD SVM
/// Check whether the host CPU supports AMD SVM (CPUID 0x80000001 ECX bit 2).
pub fn svm_supported() -> bool {
let (_, _, ecx, _) = cpuid(0x8000_0001, 0);
ecx & (1 << 2) != 0
}
// All of these should fault at CPL > 0 (#GP or #UD).
unsafe extern "C" {
/// Read host CR3. Returns the physical address of the host PML4.
pub fn read_cr3() -> u64;
/// Write host CR3 — flushes all non-global TLB entries.
pub fn write_cr3(cr3: u64);
/// VMLOAD — loads guest state from the VMCB at the given physical address. AMD SVM.
pub fn vmload(vmcb_pa: u64);
/// VMSAVE — saves guest state back to the VMCB at the given physical address. AMD SVM.
pub fn vmsave(vmcb_pa: u64);
/// VMRUN — enters the guest. Does not return until a #VMEXIT occurs. AMD SVM.
pub fn vmrun(vmcb_pa: u64);
/// RDMSR — read model-specific register.
pub fn rdmsr(msr: u32) -> u64;
/// WRMSR — write model-specific register.
pub fn wrmsr(msr: u32, val: u64);
}