use core::arch::asm;
use zerocopy::{FromBytes, Immutable, IntoBytes};
const KVM_CPUID_SIGNATURE: u32 = 0x40000000;
const KVM_HC_PKVM_OP: u64 = 20;
const PKVM_GHC_IOREAD: u64 = KVM_HC_PKVM_OP + 3;
const PKVM_GHC_IOWRITE: u64 = KVM_HC_PKVM_OP + 4;
const HYP_IO_MAX: usize = 8;
pub fn cpuid_signature() -> [u8; 4] {
let signature: u32;
unsafe {
asm!(
"push rbx",
"cpuid",
"mov r8, rbx",
"pop rbx",
in("eax") KVM_CPUID_SIGNATURE,
out("r8") signature,
out("rcx") _,
out("rdx") _,
);
};
signature.to_le_bytes()
}
fn __vmcall_impl(hypcall: u64, a1: u64, a2: u64, a3: u64, a4: u64) -> u64 {
let ret: u64;
unsafe {
asm!(
"xchg %rbx, {0:r}",
"vmcall",
"xchg %rbx, {0:r}",
in(reg) a1,
inout("rax") hypcall => ret,
in("rcx") a2,
in("rdx") a3,
in("rsi") a4,
options(att_syntax)
);
};
ret
}
macro_rules! vmcall {
($hypcall:expr) => {
__vmcall_impl($hypcall, 0, 0, 0, 0)
};
($hypcall:expr, $a1:expr) => {
__vmcall_impl($hypcall, $a1, 0, 0, 0)
};
($hypcall:expr, $a1:expr, $a2:expr) => {
__vmcall_impl($hypcall, $a1, $a2, 0, 0)
};
($hypcall:expr, $a1:expr, $a2:expr, $a3:expr) => {
__vmcall_impl($hypcall, $a1, $a2, $a3, 0)
};
($hypcall:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr) => {
__vmcall_impl($hypcall, $a1, $a2, $a3, $a4)
};
}
pub fn hyp_io_read(address: u64, size: usize) -> u64 {
vmcall!(PKVM_GHC_IOREAD, address, size as u64)
}
pub fn hyp_io_write(address: u64, size: usize, data: u64) {
vmcall!(PKVM_GHC_IOWRITE, address, size as u64, data);
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct HypIoRegion {
pub paddr: u64,
pub size: usize,
}
impl HypIoRegion {
pub fn read<T: FromBytes>(self, offset: usize) -> T {
assert!(offset + size_of::<T>() <= self.size);
assert!(size_of::<T>() <= HYP_IO_MAX);
let data = hyp_io_read(self.paddr + offset as u64, size_of::<T>());
T::read_from_prefix(data.as_bytes()).unwrap().0
}
pub fn write<T: IntoBytes + Immutable>(self, offset: usize, value: T) {
assert!(offset + size_of::<T>() <= self.size);
assert!(size_of::<T>() <= HYP_IO_MAX);
let mut data = 0;
data.as_mut_bytes()[..size_of::<T>()].copy_from_slice(value.as_bytes());
hyp_io_write(self.paddr + offset as u64, size_of::<T>(), data);
}
}