From 99742005448dfee624989f1c71cecdad23ce69bf Mon Sep 17 00:00:00 2001 From: Ethin Probst Date: Sun, 27 Jun 2021 22:04:41 -0500 Subject: [PATCH 1/3] registers: add extra flags for CR0, CR4, and XCR0 Alos, add extra checks when writing to XCR0. Signed-off-by: Ethin Probst Signed-off-by: Joe Richey --- src/registers/control.rs | 10 +++++++++- src/registers/xcontrol.rs | 19 ++++++++++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/registers/control.rs b/src/registers/control.rs index c09dd0d7e..58b2a47a2 100644 --- a/src/registers/control.rs +++ b/src/registers/control.rs @@ -24,6 +24,8 @@ bitflags! { /// /// This flags allows lazily saving x87/MMX/SSE instructions on hardware context switches. const TASK_SWITCHED = 1 << 3; + /// Indicates support of 387DX math coprocessor instructions when set + const EXTENSION_TYPE = 1 << 4; /// Enables the native error reporting mechanism for x87 FPU errors. const NUMERIC_ERROR = 1 << 5; /// Controls whether supervisor-level writes to read-only pages are inhibited. @@ -114,14 +116,20 @@ bitflags! { const PCID = 1 << 17; /// Enables extendet processor state management instructions, including XGETBV and XSAVE. const OSXSAVE = 1 << 18; + /// When set, the `LOADIWKEY` instruction is available; additionally, if system firmware has activated the AES key locker instructions, register EBX of CPUID leaf 0x19, bit 0 (AESKLE) is set and the AES key locker instructions are enabled. See the [Intel Key Locker Specification](https://software.intel.com/content/www/us/en/develop/download/intel-key-locker-specification.html) for information on this feature. + const KEY_LOCKER = 1 << 19; /// Prevents the execution of instructions that reside in pages accessible by user-mode /// software when the processor is in supervisor-mode. const SUPERVISOR_MODE_EXECUTION_PROTECTION = 1 << 20; /// Enables restrictions for supervisor-mode software when reading data from user-mode /// pages. const SUPERVISOR_MODE_ACCESS_PREVENTION = 1 << 21; - /// Enables 4-level paging to associate each linear address with a protection key. + /// Enables 4-level and 5-level paging to associate each linear address with a protection key in user mode. const PROTECTION_KEY = 1 << 22; + /// When set, enables intel control-flow enforcement technology. See chapter 18 of the Intel software developer manuals, volume 1, for more information. + const CONTROL_FLOW_ENFORCEMENT = 1 << 23; + /// When set, allows 4-level and 5-level paging implementations to use the `IA32_PKRS` MSR to specify, for each protection key, whether supervisor-mode linear addresses with a particular protection key can be read or written. + const PROTECTION_KEY_SUPERVISOR = 1 << 24; } } diff --git a/src/registers/xcontrol.rs b/src/registers/xcontrol.rs index 8d2dfb789..66501d914 100644 --- a/src/registers/xcontrol.rs +++ b/src/registers/xcontrol.rs @@ -16,8 +16,18 @@ bitflags! { /// Enables 256-bit SSE /// Must be set to enable AVX const YMM = 1<<2; + /// When set, MPX instructions are enabled and the bound registers BND0-BND3 can be managed by XSAVE. + const BNDREG = 1 << 3; + /// When set, MPX instructions can be executed and XSAVE can manage the BNDCFGU and BNDSTATUS registers. + const BNDCSR = 1 << 4; + /// If set, AVX-512 instructions can be executed and XSAVE can manage the K0-K7 mask registers. + const OPMASK = 1 << 5; + /// If set, AVX-512 instructions can be executed and XSAVE can be used to manage the upper halves of the lower ZMM registers. + const ZMM_HI256 = 1 << 6; + /// If set, AVX-512 instructions can be executed and XSAVE can manage the upper ZMM registers. + const HI16_ZMM = 1 << 7; /// When set, PKRU state management is supported by - /// ZSAVE/XRSTOR + /// XSAVE/XRSTOR const MPK = 1<<9; /// When set the Lightweight Profiling extensions are enabled const LWP = 1<<62; @@ -68,6 +78,13 @@ mod x86_64 { let old_value = Self::read_raw(); let reserved = old_value & !(XCr0Flags::all().bits()); let new_value = reserved | flags.bits(); + assert!(flags.contains(XCr0Flags::X87), "The X87 flag must be set"); + assert!((flags.contains(XCr0Flags::AVX) && flags.contains(XCr0Flags::OPMASK) && flags.contains(XCr0Flags::ZMM_HI256) && flags.contains(XCr0Flags::HI16_ZMM)) || !(flags.contains(XCr0Flags::AVX) && flags.contains(XCr0Flags::OPMASK) && flags.contains(XCr0Flags::ZMM_HI256) && flags.contains(XCr0Flags::HI16_ZMM)), "You must enable AVX to set or unset any of XCR0.opmask, XCR0.ZMM_Hi256, and XCR0.Hi16_ZMM"); + if !flags.contains(XCr0Flags::AVX) && (flags.contains(XCr0Flags::OPMASK) || flags.contains(XCr0Flags::ZMM_HI256) || flags.contains(XCr0Flags::HI16_ZMM)) { + panic!("You must have AVX enabled to set XCR0.opmask, XCR0.ZMM_Hi256, or XCR0.Hi16_ZMM"); + } + assert!((flags.contains(XCr0Flags::BNDREG) && flags.contains(XCr0Flags::BNDCSR)) || !(flags.contains(XCr0Flags::BNDREG) && flags.contains(XCr0Flags::BNDCSR)), "BNDREG and BNDCSR must be set and unset together"); + assert!((flags.contains(XCr0Flags::OPMASK) && flags.contains(XCr0Flags::ZMM_HI256) && flags.contains(XCr0Flags::HI16_ZMM)) || !(flags.contains(XCr0Flags::OPMASK) && flags.contains(XCr0Flags::ZMM_HI256) && flags.contains(XCr0Flags::HI16_ZMM)), "You must set or unset all of XCR0.opmask, XCR0.ZMM_Hi256, and XCR0.Hi16_ZMM"); Self::write_raw(new_value); } From 7dfb388f6610b4ebb2e6a37cad3fad2470eed748 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Wed, 7 Jul 2021 05:29:44 -0700 Subject: [PATCH 2/3] registers: Update documentation for control flags Signed-off-by: Joe Richey --- src/registers/control.rs | 100 +++++++++++++++++++++++--------------- src/registers/xcontrol.rs | 40 ++++++++++----- 2 files changed, 89 insertions(+), 51 deletions(-) diff --git a/src/registers/control.rs b/src/registers/control.rs index 58b2a47a2..a8f306e68 100644 --- a/src/registers/control.rs +++ b/src/registers/control.rs @@ -1,6 +1,8 @@ //! Functions to read and write control registers. pub use super::model_specific::{Efer, EferFlags}; +#[cfg(docsrs)] +use crate::{registers::rflags::RFlags, structures::paging::PageTableFlags}; use bitflags::bitflags; @@ -9,114 +11,127 @@ use bitflags::bitflags; pub struct Cr0; bitflags! { - /// Configuration flags of the Cr0 register. + /// Configuration flags of the [`Cr0`] register. pub struct Cr0Flags: u64 { /// Enables protected mode. const PROTECTED_MODE_ENABLE = 1; /// Enables monitoring of the coprocessor, typical for x87 instructions. /// - /// Controls together with the `TASK_SWITCHED` flag whether a `wait` or `fwait` - /// instruction should cause a device-not-available exception. + /// Controls (together with the [`TASK_SWITCHED`](Cr0Flags::TASK_SWITCHED) + /// flag) whether a `wait` or `fwait` instruction should cause an `#NE` exception. const MONITOR_COPROCESSOR = 1 << 1; - /// Force all x87 and MMX instructions to cause an exception. + /// Force all x87 and MMX instructions to cause an `#NE` exception. const EMULATE_COPROCESSOR = 1 << 2; /// Automatically set to 1 on _hardware_ task switch. /// /// This flags allows lazily saving x87/MMX/SSE instructions on hardware context switches. const TASK_SWITCHED = 1 << 3; - /// Indicates support of 387DX math coprocessor instructions when set + /// Indicates support of 387DX math coprocessor instructions. + /// + /// Always set on all recent x86 processors, cannot be cleared. const EXTENSION_TYPE = 1 << 4; - /// Enables the native error reporting mechanism for x87 FPU errors. + /// Enables the native (internal) error reporting mechanism for x87 FPU errors. const NUMERIC_ERROR = 1 << 5; /// Controls whether supervisor-level writes to read-only pages are inhibited. /// /// When set, it is not possible to write to read-only pages from ring 0. const WRITE_PROTECT = 1 << 16; - /// Enables automatic alignment checking. + /// Enables automatic usermode alignment checking if [`RFlags::ALIGNMENT_CHECK`] is also set. const ALIGNMENT_MASK = 1 << 18; - /// Ignored. Used to control write-back/write-through cache strategy on older CPUs. + /// Ignored, should always be unset. + /// + /// Must be unset if [`CACHE_DISABLE`](Cr0Flags::CACHE_DISABLE) is unset. + /// Older CPUs used this to control write-back/write-through cache strategy. const NOT_WRITE_THROUGH = 1 << 29; - /// Disables internal caches (only for some cases). + /// Disables some processor caches, specifics are model-dependent. const CACHE_DISABLE = 1 << 30; - /// Enables page translation. + /// Enables paging. + /// + /// If this bit is set, [`PROTECTED_MODE_ENABLE`](Cr0Flags::PROTECTED_MODE_ENABLE) must be set. const PAGING = 1 << 31; } } /// Contains the Page Fault Linear Address (PFLA). /// -/// When page fault occurs, the CPU sets this register to the accessed address. +/// When a page fault occurs, the CPU sets this register to the faulting virtual address. #[derive(Debug)] pub struct Cr2; -/// Contains the physical address of the level 4 page table. +/// Contains the physical address of the highest-level page table. #[derive(Debug)] pub struct Cr3; bitflags! { - /// Controls cache settings for the level 4 page table. + /// Controls cache settings for the highest-level page table. + /// + /// Unused if paging is disabled or if [`PCID`](Cr4Flags::PCID) is enabled. pub struct Cr3Flags: u64 { - /// Use a writethrough cache policy for the P4 table (else a writeback policy is used). + /// Use a writethrough cache policy for the table (otherwise a writeback policy is used). const PAGE_LEVEL_WRITETHROUGH = 1 << 3; - /// Disable caching for the P4 table. + /// Disable caching for the table. const PAGE_LEVEL_CACHE_DISABLE = 1 << 4; } } -/// Various control flags modifying the basic operation of the CPU while in protected mode. -/// -/// Note: The documention for the individual fields is taken from the AMD64 and Intel x86_64 -/// manuals. +/// Contains various control flags that enable architectural extensions, and +/// indicate support for specific processor capabilities. #[derive(Debug)] pub struct Cr4; bitflags! { - /// Controls cache settings for the level 4 page table. + /// Configuration flags of the [`Cr4`] register. pub struct Cr4Flags: u64 { /// Enables hardware-supported performance enhancements for software running in /// virtual-8086 mode. const VIRTUAL_8086_MODE_EXTENSIONS = 1; /// Enables support for protected-mode virtual interrupts. const PROTECTED_MODE_VIRTUAL_INTERRUPTS = 1 << 1; - /// When set, only privilege-level 0 can execute the RDTSC or RDTSCP instructions. + /// When set, only privilege-level 0 can execute the `RDTSC` or `RDTSCP` instructions. const TIMESTAMP_DISABLE = 1 << 2; - /// Enables I/O breakpoint capability and enforces treatment of DR4 and DR5 registers + /// Enables I/O breakpoint capability and enforces treatment of `DR4` and `DR5` registers /// as reserved. const DEBUGGING_EXTENSIONS = 1 << 3; - /// Enables the use of 4MB physical frames; ignored in long mode. + /// Enables the use of 4MB physical frames; ignored if + /// [`PHYSICAL_ADDRESS_EXTENSION`](Cr4Flags::PHYSICAL_ADDRESS_EXTENSION) + /// is set (so always ignored in long mode). const PAGE_SIZE_EXTENSION = 1 << 4; - /// Enables physical address extension and 2MB physical frames; required in long mode. + /// Enables physical address extensions and 2MB physical frames. Required in long mode. const PHYSICAL_ADDRESS_EXTENSION = 1 << 5; /// Enables the machine-check exception mechanism. const MACHINE_CHECK_EXCEPTION = 1 << 6; - /// Enables the global-page mechanism, which allows to make page translations global - /// to all processes. + /// Enables the global page feature, allowing some page translations to + /// be marked as global (see [`PageTableFlags::GLOBAL`]). const PAGE_GLOBAL = 1 << 7; - /// Allows software running at any privilege level to use the RDPMC instruction. + /// Allows software running at any privilege level to use the `RDPMC` instruction. const PERFORMANCE_MONITOR_COUNTER = 1 << 8; - /// Enable the use of legacy SSE instructions; allows using FXSAVE/FXRSTOR for saving + /// Enables the use of legacy SSE instructions; allows using `FXSAVE`/`FXRSTOR` for saving /// processor state of 128-bit media instructions. const OSFXSR = 1 << 9; - /// Enables the SIMD floating-point exception (#XF) for handling unmasked 256-bit and + /// Enables the SIMD floating-point exception (`#XF`) for handling unmasked 256-bit and /// 128-bit media floating-point errors. const OSXMMEXCPT_ENABLE = 1 << 10; - /// Prevents the execution of the SGDT, SIDT, SLDT, SMSW, and STR instructions by + /// Prevents the execution of the `SGDT`, `SIDT`, `SLDT`, `SMSW`, and `STR` instructions by /// user-mode software. const USER_MODE_INSTRUCTION_PREVENTION = 1 << 11; - /// Enables 5-level paging on supported CPUs. + /// Enables 5-level paging on supported CPUs (Intel Only). const L5_PAGING = 1 << 12; - /// Enables VMX insturctions. + /// Enables VMX instructions (Intel Only). const VIRTUAL_MACHINE_EXTENSIONS = 1 << 13; - /// Enables SMX instructions. + /// Enables SMX instructions (Intel Only). const SAFER_MODE_EXTENSIONS = 1 << 14; /// Enables software running in 64-bit mode at any privilege level to read and write /// the FS.base and GS.base hidden segment register state. const FSGSBASE = 1 << 16; /// Enables process-context identifiers (PCIDs). const PCID = 1 << 17; - /// Enables extendet processor state management instructions, including XGETBV and XSAVE. + /// Enables extended processor state management instructions, including `XGETBV` and `XSAVE`. const OSXSAVE = 1 << 18; - /// When set, the `LOADIWKEY` instruction is available; additionally, if system firmware has activated the AES key locker instructions, register EBX of CPUID leaf 0x19, bit 0 (AESKLE) is set and the AES key locker instructions are enabled. See the [Intel Key Locker Specification](https://software.intel.com/content/www/us/en/develop/download/intel-key-locker-specification.html) for information on this feature. + /// Enables the Key Locker feature (Intel Only). + /// + /// This enables creation and use of opaque AES key handles; see the + /// [Intel Key Locker Specification](https://software.intel.com/content/www/us/en/develop/download/intel-key-locker-specification.html) + /// for more information. const KEY_LOCKER = 1 << 19; /// Prevents the execution of instructions that reside in pages accessible by user-mode /// software when the processor is in supervisor-mode. @@ -124,11 +139,20 @@ bitflags! { /// Enables restrictions for supervisor-mode software when reading data from user-mode /// pages. const SUPERVISOR_MODE_ACCESS_PREVENTION = 1 << 21; - /// Enables 4-level and 5-level paging to associate each linear address with a protection key in user mode. + /// Enables protection keys for user-mode pages. + /// + /// Also enables access to the PKRU register (via the `RDPKRU`/`WRPKRU` + /// instructions) to set user-mode protection key access controls. const PROTECTION_KEY = 1 << 22; - /// When set, enables intel control-flow enforcement technology. See chapter 18 of the Intel software developer manuals, volume 1, for more information. + /// Enables Control-flow Enforcement Technology (CET) + /// + /// This enables the shadow stack feature, ensuring return addresses read + /// via `RET` and `IRET` have not been corrupted. const CONTROL_FLOW_ENFORCEMENT = 1 << 23; - /// When set, allows 4-level and 5-level paging implementations to use the `IA32_PKRS` MSR to specify, for each protection key, whether supervisor-mode linear addresses with a particular protection key can be read or written. + /// Enables protection keys for supervisor-mode pages (Intel Only). + /// + /// Also enables the `IA32_PKRS` MSR to set supervisor-mode protection + /// key access controls. const PROTECTION_KEY_SUPERVISOR = 1 << 24; } } diff --git a/src/registers/xcontrol.rs b/src/registers/xcontrol.rs index 66501d914..0fdc54705 100644 --- a/src/registers/xcontrol.rs +++ b/src/registers/xcontrol.rs @@ -7,29 +7,43 @@ pub struct XCr0; bitflags! { /// Configuration flags of the XCr0 register. + /// + /// For MPX, [`BNDREG`](XCr0Flags::BNDREG) and [`BNDCSR`](XCr0Flags::BNDCSR) must be set/unset simultaneously. + /// For AVX-512, [`OPMASK`](XCr0Flags::OPMASK), [`ZMM_HI256`](XCr0Flags::ZMM_HI256), and [`HI16_ZMM`](XCr0Flags::HI16_ZMM) must be set/unset simultaneously. pub struct XCr0Flags: u64 { - /// Enables x87 FPU + /// Enables using the x87 FPU state + /// with `XSAVE`/`XRSTOR`. + /// + /// Must be set. const X87 = 1; - /// Enables 128-bit (legacy) SSE - /// Must be set to enable AVX and YMM + /// Enables using MXCSR and the XMM registers + /// with `XSAVE`/`XRSTOR`. + /// + /// Must be set if [`YMM`](XCr0Flags::YMM) is set. const SSE = 1<<1; - /// Enables 256-bit SSE - /// Must be set to enable AVX + /// Enables AVX instructions and using the upper halves of the YMM registers + /// with `XSAVE`/`XRSTOR`. const YMM = 1<<2; - /// When set, MPX instructions are enabled and the bound registers BND0-BND3 can be managed by XSAVE. + /// Enables MPX instructions and using the BND0-BND3 bound registers + /// with `XSAVE`/`XRSTOR` (Intel Only). const BNDREG = 1 << 3; - /// When set, MPX instructions can be executed and XSAVE can manage the BNDCFGU and BNDSTATUS registers. + /// Enables MPX instructions and using the BNDCFGU and BNDSTATUS registers + /// with `XSAVE`/`XRSTOR` (Intel Only). const BNDCSR = 1 << 4; - /// If set, AVX-512 instructions can be executed and XSAVE can manage the K0-K7 mask registers. + /// Enables AVX-512 instructions and using the K0-K7 mask registers + /// with `XSAVE`/`XRSTOR` (Intel Only). const OPMASK = 1 << 5; - /// If set, AVX-512 instructions can be executed and XSAVE can be used to manage the upper halves of the lower ZMM registers. + /// Enables AVX-512 instructions and using the upper halves of the lower ZMM registers + /// with `XSAVE`/`XRSTOR` (Intel Only). const ZMM_HI256 = 1 << 6; - /// If set, AVX-512 instructions can be executed and XSAVE can manage the upper ZMM registers. + /// Enables AVX-512 instructions and using the upper ZMM registers + /// with `XSAVE`/`XRSTOR` (Intel Only). const HI16_ZMM = 1 << 7; - /// When set, PKRU state management is supported by - /// XSAVE/XRSTOR + /// Enables using the PKRU register + /// with `XSAVE`/`XRSTOR`. const MPK = 1<<9; - /// When set the Lightweight Profiling extensions are enabled + /// Enables Lightweight Profiling extensions and managing LWP state + /// with `XSAVE`/`XRSTOR` (AMD Only). const LWP = 1<<62; } } From d369377578543884a883aa1d2a08994cd413f617 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Wed, 7 Jul 2021 05:57:30 -0700 Subject: [PATCH 3/3] XCr0: Update the asserts to check all conditions This avoids getting a #GP when writing. Signed-off-by: Joe Richey --- src/registers/xcontrol.rs | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/registers/xcontrol.rs b/src/registers/xcontrol.rs index 0fdc54705..8c4cb3cfa 100644 --- a/src/registers/xcontrol.rs +++ b/src/registers/xcontrol.rs @@ -82,6 +82,7 @@ mod x86_64 { /// Write XCR0 flags. /// /// Preserves the value of reserved fields. + /// Panics if invalid combinations of [`XCr0Flags`] are set. /// /// ## Safety /// @@ -92,13 +93,32 @@ mod x86_64 { let old_value = Self::read_raw(); let reserved = old_value & !(XCr0Flags::all().bits()); let new_value = reserved | flags.bits(); + assert!(flags.contains(XCr0Flags::X87), "The X87 flag must be set"); - assert!((flags.contains(XCr0Flags::AVX) && flags.contains(XCr0Flags::OPMASK) && flags.contains(XCr0Flags::ZMM_HI256) && flags.contains(XCr0Flags::HI16_ZMM)) || !(flags.contains(XCr0Flags::AVX) && flags.contains(XCr0Flags::OPMASK) && flags.contains(XCr0Flags::ZMM_HI256) && flags.contains(XCr0Flags::HI16_ZMM)), "You must enable AVX to set or unset any of XCR0.opmask, XCR0.ZMM_Hi256, and XCR0.Hi16_ZMM"); - if !flags.contains(XCr0Flags::AVX) && (flags.contains(XCr0Flags::OPMASK) || flags.contains(XCr0Flags::ZMM_HI256) || flags.contains(XCr0Flags::HI16_ZMM)) { - panic!("You must have AVX enabled to set XCR0.opmask, XCR0.ZMM_Hi256, or XCR0.Hi16_ZMM"); + if flags.contains(XCr0Flags::YMM) { + assert!( + flags.contains(XCr0Flags::SSE), + "AVX/YMM cannot be enabled without enabling SSE" + ); + } + let mpx = XCr0Flags::BNDREG | XCr0Flags::BNDCSR; + if flags.intersects(mpx) { + assert!( + flags.contains(mpx), + "MPX flags XCr0.BNDREG and XCr0.BNDCSR must be set and unset together" + ); + } + let avx512 = XCr0Flags::OPMASK | XCr0Flags::ZMM_HI256 | XCr0Flags::HI16_ZMM; + if flags.intersects(avx512) { + assert!( + flags.contains(XCr0Flags::YMM), + "AVX-512 cannot be enabled without enabling AVX/YMM" + ); + assert!( + flags.contains(avx512), + "AVX-512 flags XCR0.opmask, XCR0.ZMM_Hi256, and XCR0.Hi16_ZMM must be set and unset together" + ); } - assert!((flags.contains(XCr0Flags::BNDREG) && flags.contains(XCr0Flags::BNDCSR)) || !(flags.contains(XCr0Flags::BNDREG) && flags.contains(XCr0Flags::BNDCSR)), "BNDREG and BNDCSR must be set and unset together"); - assert!((flags.contains(XCr0Flags::OPMASK) && flags.contains(XCr0Flags::ZMM_HI256) && flags.contains(XCr0Flags::HI16_ZMM)) || !(flags.contains(XCr0Flags::OPMASK) && flags.contains(XCr0Flags::ZMM_HI256) && flags.contains(XCr0Flags::HI16_ZMM)), "You must set or unset all of XCR0.opmask, XCR0.ZMM_Hi256, and XCR0.Hi16_ZMM"); Self::write_raw(new_value); }