use core::arch::asm;
pub const ALLOW_ACCESS: u32 = 0;
pub const DISABLE_ACCESS: u32 = 0b11111111_11111111_11111111_11111111;
#[inline]
pub fn read() -> u32 {
let ecx: u32 = 0;
let pkru: u32;
unsafe {
asm!("rdpkru", in("ecx") ecx, out("eax") pkru, out("edx") _,
options(nomem, nostack, preserves_flags));
}
return pkru;
}
#[inline]
pub fn write(pkru: u32) {
let ecx: u32 = 0;
let edx: u32 = 0;
unsafe {
asm!("wrpkru", in("eax") pkru, in("ecx") ecx, in("edx") edx,
options(nomem, nostack, preserves_flags));
}
}
pub fn has_cpuid_bit_set() -> bool {
let result = unsafe { std::arch::x86_64::__cpuid(0x07) };
is_intel_cpu() && (result.ecx & 0b1000) != 0
}
pub fn is_intel_cpu() -> bool {
let result = unsafe { std::arch::x86_64::__cpuid(0) };
result.ebx == u32::from_le_bytes(*b"Genu")
&& result.edx == u32::from_le_bytes(*b"ineI")
&& result.ecx == u32::from_le_bytes(*b"ntel")
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mpk::enabled::skip_if_mpk_unavailable;
#[test]
#[ignore = "cannot be run with other tests that munge the PKRU register"]
fn check_read() {
skip_if_mpk_unavailable!();
assert_eq!(read(), DISABLE_ACCESS ^ 1);
}
#[test]
fn check_roundtrip() {
skip_if_mpk_unavailable!();
let pkru = read();
write(ALLOW_ACCESS);
assert_eq!(read(), ALLOW_ACCESS);
write(pkru);
assert_eq!(read(), pkru);
}
}