Skip to content

Commit

Permalink
Merge pull request #273 from rust-osdev/flags
Browse files Browse the repository at this point in the history
Add flags for CR0, CR4 and XCR0, as well as extra checks for modification of XCR0
  • Loading branch information
josephlr authored Jul 17, 2021
2 parents 1a0b149 + d369377 commit 759cb32
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 42 deletions.
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};

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

0 comments on commit 759cb32

Please sign in to comment.