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