wasmtime/runtime/vm/mpk/
pkru.rs

1//! Control access to the x86 `PKRU` register.
2//!
3//! As documented in the Intel Software Development Manual, vol 3a, section 2.7,
4//! the 32 bits of the `PKRU` register laid out as follows (note the
5//! little-endianness):
6//!
7//! ```text
8//! ┌───┬───┬───┬───┬───┬───┐
9//! │...│AD2│WD1│AD1│WD0│AD0│
10//! └───┴───┴───┴───┴───┴───┘
11//! ```
12//!
13//! - `ADn = 1` means "access disable key `n`"--no reads or writes allowed to
14//!   pages marked with key `n`.
15//! - `WDn = 1` means "write disable key `n`"--only reads are prevented to pages
16//!   marked with key `n`
17//! - it is unclear what it means to have both `ADn` and `WDn` set
18//!
19//! Note that this only handles the user-mode `PKRU` register; there is an
20//! equivalent supervisor-mode MSR, `IA32_PKRS`.
21
22use core::arch::asm;
23
24/// This `PKRU` register mask allows access to any pages marked with any
25/// key--in other words, reading and writing is permitted to all pages.
26pub const ALLOW_ACCESS: u32 = 0;
27
28/// This `PKRU` register mask disables access to any page marked with any
29/// key--in other words, no reading or writing to all pages.
30pub const DISABLE_ACCESS: u32 = 0b11111111_11111111_11111111_11111111;
31
32/// Read the value of the `PKRU` register.
33#[inline]
34pub fn read() -> u32 {
35    // ECX must be 0 to prevent a general protection exception (#GP).
36    let ecx: u32 = 0;
37    let pkru: u32;
38    unsafe {
39        asm!("rdpkru", in("ecx") ecx, out("eax") pkru, out("edx") _,
40            options(nomem, nostack, preserves_flags));
41    }
42    return pkru;
43}
44
45/// Write a value to the `PKRU` register.
46#[inline]
47pub fn write(pkru: u32) {
48    // Both ECX and EDX must be 0 to prevent a general protection exception
49    // (#GP).
50    let ecx: u32 = 0;
51    let edx: u32 = 0;
52    unsafe {
53        asm!("wrpkru", in("eax") pkru, in("ecx") ecx, in("edx") edx,
54            options(nomem, nostack, preserves_flags));
55    }
56}
57
58/// Check the `ECX.PKU` flag (bit 3, zero-based) of the `07h` `CPUID` leaf; see
59/// the Intel Software Development Manual, vol 3a, section 2.7. This flag is
60/// only set on Intel CPUs, so this function also checks the `CPUID` vendor
61/// string.
62pub fn has_cpuid_bit_set() -> bool {
63    let result = unsafe { core::arch::x86_64::__cpuid(0x07) };
64    is_intel_cpu() && (result.ecx & 0b1000) != 0
65}
66
67/// Check the `CPUID` vendor string for `GenuineIntel`; see the Intel Software
68/// Development Manual, vol 2a, `CPUID` description.
69pub fn is_intel_cpu() -> bool {
70    // To read the CPU vendor string, we pass 0 in EAX and read 12 ASCII bytes
71    // from EBX, EDX, and ECX (in that order).
72    let result = unsafe { core::arch::x86_64::__cpuid(0) };
73    // Then we check if the vendor string matches "GenuineIntel".
74    result.ebx == u32::from_le_bytes(*b"Genu")
75        && result.edx == u32::from_le_bytes(*b"ineI")
76        && result.ecx == u32::from_le_bytes(*b"ntel")
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82    use crate::runtime::vm::mpk::enabled::skip_if_mpk_unavailable;
83
84    #[test]
85    #[ignore = "cannot be run with other tests that munge the PKRU register"]
86    fn check_read() {
87        skip_if_mpk_unavailable!();
88        assert_eq!(read(), DISABLE_ACCESS ^ 1);
89        // By default, the Linux kernel only allows a process to access key 0,
90        // the default kernel key.
91    }
92
93    #[test]
94    fn check_roundtrip() {
95        skip_if_mpk_unavailable!();
96        let pkru = read();
97        // Allow access to pages marked with any key.
98        write(ALLOW_ACCESS);
99        assert_eq!(read(), ALLOW_ACCESS);
100        // Restore the original value.
101        write(pkru);
102        assert_eq!(read(), pkru);
103    }
104}