Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add flags for CR0, CR4 and XCR0, as well as extra checks for modification of XCR0 #273

Merged
merged 3 commits into from
Jul 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 66 additions & 34 deletions src/registers/control.rs
Original file line number Diff line number Diff line change
@@ -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};
Comment on lines +4 to +5
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this about?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is needed for the doc comments to properly reference these other structures (like PageTableFlags::GLOBAL). Without them, the docs don't properly link to the other sections.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh ok. But there imports are then also needed for local doc builds, e.g. on cargo doc --open. So we should probably use cfg(doc) instead. I opened #287 for this.


use bitflags::bitflags;

Expand All @@ -9,119 +11,149 @@ 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;
/// Enables the native error reporting mechanism for x87 FPU errors.
/// 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 (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;
/// 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.
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 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;
/// 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;
/// 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;
}
}

Expand Down
67 changes: 59 additions & 8 deletions src/registers/xcontrol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +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, PKRU state management is supported by
/// ZSAVE/XRSTOR
/// Enables MPX instructions and using the BND0-BND3 bound registers
/// with `XSAVE`/`XRSTOR` (Intel Only).
const BNDREG = 1 << 3;
/// Enables MPX instructions and using the BNDCFGU and BNDSTATUS registers
/// with `XSAVE`/`XRSTOR` (Intel Only).
const BNDCSR = 1 << 4;
/// Enables AVX-512 instructions and using the K0-K7 mask registers
/// with `XSAVE`/`XRSTOR` (Intel Only).
const OPMASK = 1 << 5;
/// Enables AVX-512 instructions and using the upper halves of the lower ZMM registers
/// with `XSAVE`/`XRSTOR` (Intel Only).
const ZMM_HI256 = 1 << 6;
/// Enables AVX-512 instructions and using the upper ZMM registers
/// with `XSAVE`/`XRSTOR` (Intel Only).
const HI16_ZMM = 1 << 7;
/// 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;
}
}
Expand Down Expand Up @@ -58,6 +82,7 @@ mod x86_64 {
/// Write XCR0 flags.
///
/// Preserves the value of reserved fields.
/// Panics if invalid combinations of [`XCr0Flags`] are set.
///
/// ## Safety
///
Expand All @@ -69,6 +94,32 @@ mod x86_64 {
let reserved = old_value & !(XCr0Flags::all().bits());
let new_value = reserved | flags.bits();

assert!(flags.contains(XCr0Flags::X87), "The X87 flag must be set");
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"
);
}

Self::write_raw(new_value);
}

Expand Down