Skip to content

Commit 59ac07c

Browse files
authored
Merge pull request #527 from mkroening/xcr0-update
feat: add `update()` to `Cr3`, `Dr7`, `SFMask`, `UCet`, `SCet`, `mxcsr`, `rflags`, and `XCr0`
2 parents a1ffaad + 87a7aff commit 59ac07c

File tree

6 files changed

+162
-1
lines changed

6 files changed

+162
-1
lines changed

src/registers/control.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,63 @@ mod x86_64 {
361361
asm!("mov cr3, {}", in(reg) value, options(nostack, preserves_flags));
362362
}
363363
}
364+
365+
/// Update the P4 table address in the CR3 register.
366+
///
367+
/// ## Safety
368+
///
369+
/// Changing the level 4 page table is unsafe, because it's possible to violate memory safety by
370+
/// changing the page mapping.
371+
#[inline]
372+
pub unsafe fn update<F>(f: F)
373+
where
374+
F: FnOnce(&mut PhysFrame, &mut Cr3Flags),
375+
{
376+
let (mut frame, mut flags) = Self::read();
377+
f(&mut frame, &mut flags);
378+
unsafe {
379+
Self::write(frame, flags);
380+
}
381+
}
382+
383+
/// Updates the P4 table address in the CR3 register.
384+
///
385+
/// ## Safety
386+
///
387+
/// Changing the level 4 page table is unsafe, because it's possible to violate memory safety by
388+
/// changing the page mapping.
389+
/// [`Cr4Flags::PCID`] must be set before calling this method.
390+
#[inline]
391+
pub unsafe fn update_pcid<F>(f: F)
392+
where
393+
F: FnOnce(&mut PhysFrame, &mut Pcid),
394+
{
395+
let (mut frame, mut pcid) = Self::read_pcid();
396+
f(&mut frame, &mut pcid);
397+
unsafe {
398+
Self::write_pcid(frame, pcid);
399+
}
400+
}
401+
402+
/// Updates the P4 table address in the CR3 register without flushing existing TLB entries for
403+
/// the PCID.
404+
///
405+
/// ## Safety
406+
///
407+
/// Changing the level 4 page table is unsafe, because it's possible to violate memory safety by
408+
/// changing the page mapping.
409+
/// [`Cr4Flags::PCID`] must be set before calling this method.
410+
#[inline]
411+
pub unsafe fn update_pcid_no_flush<F>(f: F)
412+
where
413+
F: FnOnce(&mut PhysFrame, &mut Pcid),
414+
{
415+
let (mut frame, mut pcid) = Self::read_pcid();
416+
f(&mut frame, &mut pcid);
417+
unsafe {
418+
Self::write_pcid_no_flush(frame, pcid);
419+
}
420+
}
364421
}
365422

366423
impl Cr4 {

src/registers/debug.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,5 +499,18 @@ mod x86_64 {
499499
asm!("mov dr7, {}", in(reg) value, options(nomem, nostack, preserves_flags));
500500
}
501501
}
502+
503+
/// Update the DR7 value.
504+
///
505+
/// Preserves the value of reserved fields.
506+
#[inline]
507+
pub fn update<F>(f: F)
508+
where
509+
F: FnOnce(&mut Dr7Value),
510+
{
511+
let mut value = Self::read();
512+
f(&mut value);
513+
Self::write(value);
514+
}
502515
}
503516
}

src/registers/model_specific.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,24 @@ mod x86_64 {
525525
let mut msr = Self::MSR;
526526
unsafe { msr.write(value.bits()) };
527527
}
528+
529+
/// Update the SFMask register.
530+
///
531+
/// The SFMASK register is used to specify which RFLAGS bits
532+
/// are cleared during a SYSCALL. In long mode, SFMASK is used
533+
/// to specify which RFLAGS bits are cleared when SYSCALL is
534+
/// executed. If a bit in SFMASK is set to 1, the corresponding
535+
/// bit in RFLAGS is cleared to 0. If a bit in SFMASK is cleared
536+
/// to 0, the corresponding rFLAGS bit is not modified.
537+
#[inline]
538+
pub fn update<F>(f: F)
539+
where
540+
F: FnOnce(&mut RFlags),
541+
{
542+
let mut flags = Self::read();
543+
f(&mut flags);
544+
Self::write(flags);
545+
}
528546
}
529547

530548
impl UCet {
@@ -560,6 +578,17 @@ mod x86_64 {
560578
pub fn write(flags: CetFlags, legacy_bitmap: Page) {
561579
Self::write_raw(flags.bits() | legacy_bitmap.start_address().as_u64());
562580
}
581+
582+
/// Updates IA32_U_CET.
583+
#[inline]
584+
pub fn update<F>(f: F)
585+
where
586+
F: FnOnce(&mut CetFlags, &mut Page),
587+
{
588+
let (mut flags, mut legacy_bitmap) = Self::read();
589+
f(&mut flags, &mut legacy_bitmap);
590+
Self::write(flags, legacy_bitmap);
591+
}
563592
}
564593

565594
impl SCet {
@@ -595,5 +624,16 @@ mod x86_64 {
595624
pub fn write(flags: CetFlags, legacy_bitmap: Page) {
596625
Self::write_raw(flags.bits() | legacy_bitmap.start_address().as_u64());
597626
}
627+
628+
/// Updates IA32_S_CET.
629+
#[inline]
630+
pub fn update<F>(f: F)
631+
where
632+
F: FnOnce(&mut CetFlags, &mut Page),
633+
{
634+
let (mut flags, mut legacy_bitmap) = Self::read();
635+
f(&mut flags, &mut legacy_bitmap);
636+
Self::write(flags, legacy_bitmap);
637+
}
598638
}
599639
}

src/registers/mxcsr.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,17 @@ mod x86_64 {
8383
}
8484
}
8585

86+
/// Update MXCSR.
87+
#[inline]
88+
pub fn update<F>(f: F)
89+
where
90+
F: FnOnce(&mut MxCsr),
91+
{
92+
let mut mxcsr = self::read();
93+
f(&mut mxcsr);
94+
self::write(mxcsr);
95+
}
96+
8697
#[cfg(test)]
8798
mod test {
8899
use crate::registers::mxcsr::*;

src/registers/rflags.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,25 @@ mod x86_64 {
126126
}
127127
}
128128

129+
/// Updates the RFLAGS register, preserves reserved bits.
130+
///
131+
/// ## Safety
132+
///
133+
/// Unsafe because undefined becavior can occur if certain flags are modified. For example,
134+
/// the `DF` flag must be unset in all Rust code. Also, modifying `CF`, `PF`, or any other
135+
/// flags also used by Rust/LLVM can result in undefined behavior too.
136+
#[inline]
137+
pub unsafe fn update<F>(f: F)
138+
where
139+
F: FnOnce(&mut RFlags),
140+
{
141+
let mut flags = self::read();
142+
f(&mut flags);
143+
unsafe {
144+
self::write(flags);
145+
}
146+
}
147+
129148
#[cfg(test)]
130149
mod test {
131150
use crate::registers::rflags::read;

src/registers/xcontrol.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ mod x86_64 {
8585
/// ## Safety
8686
///
8787
/// This function is unsafe because it's possible to
88-
/// enable features that are not supported by the architecture
88+
/// enable features that are not supported by the architecture.
8989
#[inline]
9090
pub unsafe fn write(flags: XCr0Flags) {
9191
let old_value = Self::read_raw();
@@ -145,5 +145,26 @@ mod x86_64 {
145145
);
146146
}
147147
}
148+
149+
/// Update XCR0 flags.
150+
///
151+
/// Preserves the value of reserved fields.
152+
/// Panics if invalid combinations of [`XCr0Flags`] are set.
153+
///
154+
/// ## Safety
155+
///
156+
/// This function is unsafe because it's possible to
157+
/// enable features that are not supported by the architecture.
158+
#[inline]
159+
pub unsafe fn update<F>(f: F)
160+
where
161+
F: FnOnce(&mut XCr0Flags),
162+
{
163+
let mut flags = Self::read();
164+
f(&mut flags);
165+
unsafe {
166+
Self::write(flags);
167+
}
168+
}
148169
}
149170
}

0 commit comments

Comments
 (0)