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}