From 786d732257aa8a0b310813afe317acace9e0a63c Mon Sep 17 00:00:00 2001 From: Vinay Chandra Dommeti Date: Thu, 23 Jan 2020 21:00:55 -0800 Subject: [PATCH 1/3] Add User Mode registers --- src/registers/model_specific.rs | 154 +++++++++++++++++++++++++++++++- 1 file changed, 153 insertions(+), 1 deletion(-) diff --git a/src/registers/model_specific.rs b/src/registers/model_specific.rs index 4f2b9c325..7fa4c30e1 100644 --- a/src/registers/model_specific.rs +++ b/src/registers/model_specific.rs @@ -1,6 +1,10 @@ -//! Functions to read and write control registers. +//! Functions to read and write model specific registers. +use crate::registers::rflags::RFlags; +use crate::structures::gdt::SegmentSelector; +use bit_field::BitField; use bitflags::bitflags; +use core::convert::TryInto; /// A model specific register. #[derive(Debug)] @@ -29,6 +33,18 @@ pub struct GsBase; #[derive(Debug)] pub struct KernelGsBase; +/// Syscall Register: STAR +#[derive(Debug)] +pub struct Star; + +/// Syscall Register: LSTAR +#[derive(Debug)] +pub struct LStar; + +/// Syscall Register: SFMASK +#[derive(Debug)] +pub struct SFMask; + impl Efer { /// The underlying model specific register. pub const MSR: Msr = Msr(0xC0000080); @@ -49,6 +65,21 @@ impl KernelGsBase { pub const MSR: Msr = Msr(0xC000_0102); } +impl Star { + /// The underlying model specific register. + pub const MSR: Msr = Msr(0xC000_0081); +} + +impl LStar { + /// The underlying model specific register. + pub const MSR: Msr = Msr(0xC000_0082); +} + +impl SFMask { + /// The underlying model specific register. + pub const MSR: Msr = Msr(0xC000_0084); +} + bitflags! { /// Flags of the Extended Feature Enable Register. pub struct EferFlags: u64 { @@ -172,4 +203,125 @@ mod x86_64 { unsafe { Self::MSR.write(address.as_u64()) }; } } + + impl Star { + /// Read the Ring 0 and Ring 3 segment bases. + /// The remaining fields are ignored because they are + /// not valid for long mode. + /// + /// # Returns + /// - Field 1 (SYSRET): The CS selector is set to this field + 16. SS.Sel is set to + /// this field + 8. Because SYSRET always returns to CPL 3, the + /// RPL bits 1:0 should be initialized to 11b. + /// - Field 2 (SYSCALL): This field is copied directly into CS.Sel. SS.Sel is set to + /// this field + 8. Because SYSCALL always switches to CPL 0, the RPL bits + /// 33:32 should be initialized to 00b. + pub fn read_raw() -> (u64, u64) { + let msr_value = unsafe { Self::MSR.read() }; + let sysret = msr_value.get_bits(48..63); + let syscall = msr_value.get_bits(32..47); + (sysret, syscall) + } + + /// Read the Ring 0 and Ring 3 segment bases. + /// Returns + /// - CS Selector SYSRET + /// - SS Selector SYSRET + /// - CS Selector SYSCALL + /// - SS Selector SYSCALL + pub fn read() -> ( + SegmentSelector, + SegmentSelector, + SegmentSelector, + SegmentSelector, + ) { + let raw = Self::read_raw(); + return ( + SegmentSelector((raw.0 + 16 - 3).try_into().unwrap()), + SegmentSelector((raw.0 + 8 - 3).try_into().unwrap()), + SegmentSelector((raw.1).try_into().unwrap()), + SegmentSelector((raw.1 + 8).try_into().unwrap()), + ); + } + + /// Write the Ring 0 and Ring 3 segment bases. + /// The remaining fields are ignored because they are + /// not valid for long mode. + /// + /// # Parameters + /// - sysret: The CS selector is set to this field + 16. SS.Sel is set to + /// this field + 8. Because SYSRET always returns to CPL 3, the + /// RPL bits 1:0 should be initialized to 11b. + /// - syscall: This field is copied directly into CS.Sel. SS.Sel is set to + /// this field + 8. Because SYSCALL always switches to CPL 0, the RPL bits + /// 33:32 should be initialized to 00b. + pub fn write_raw(sysret: u64, syscall: u64) { + let mut msr_value = 0u64; + msr_value.set_bits(48..63, sysret); + msr_value.set_bits(32..47, syscall); + unsafe { + Self::MSR.write(msr_value); + } + } + + /// Write the Ring 0 and Ring 3 segment bases. + /// The remaining fields are ignored because they are + /// not valid for long mode. + pub fn write( + cs_sysret: SegmentSelector, + ss_sysret: SegmentSelector, + cs_syscall: SegmentSelector, + ss_syscall: SegmentSelector, + ) -> Result<(), &'static str> { + if cs_sysret.0 - 16 != ss_sysret.0 - 8 { + return Err("Sysret CS and SS is not offset by 8."); + } + + if cs_syscall.0 != ss_syscall.0 - 8 { + return Err("Syscall CS and SS is not offset by 8."); + } + + Self::write_raw(((ss_sysret.0 - 8) | 0b11).into(), cs_syscall.0.into()); + + Ok(()) + } + } + + impl LStar { + /// Read the current LStar register. + /// This holds the target RIP of a syscall. + pub fn read() -> VirtAddr { + VirtAddr::new(unsafe { Self::MSR.read() }) + } + + /// Write a given virtual address to the LStar register. + /// This holds the target RIP of a syscall. + pub fn write(address: VirtAddr) { + unsafe { Self::MSR.write(address.as_u64()) }; + } + } + + impl SFMask { + /// Read to the SFMask register. + /// The SFMASK register is used to specify which RFLAGS bits + /// are cleared during a SYSCALL. In long mode, SFMASK is used + /// to specify which RFLAGS bits are cleared when SYSCALL is + /// executed. If a bit in SFMASK is set to 1, the corresponding + /// bit in RFLAGS is cleared to 0. If a bit in SFMASK is cleared + /// to 0, the corresponding rFLAGS bit is not modified. + pub fn read() -> RFlags { + RFlags::from_bits(unsafe { Self::MSR.read() }).unwrap() + } + + /// Write to the SFMask register. + /// The SFMASK register is used to specify which RFLAGS bits + /// are cleared during a SYSCALL. In long mode, SFMASK is used + /// to specify which RFLAGS bits are cleared when SYSCALL is + /// executed. If a bit in SFMASK is set to 1, the corresponding + /// bit in RFLAGS is cleared to 0. If a bit in SFMASK is cleared + /// to 0, the corresponding rFLAGS bit is not modified. + pub fn write(value: RFlags) { + unsafe { Self::MSR.write(value.bits()) }; + } + } } From 60f8b65f98272dacae99fd1f0f5f9b488506db6d Mon Sep 17 00:00:00 2001 From: Vinay Chandra Dommeti Date: Sat, 25 Jan 2020 22:15:22 -0800 Subject: [PATCH 2/3] Comments --- src/registers/model_specific.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/registers/model_specific.rs b/src/registers/model_specific.rs index 7fa4c30e1..53ec5c917 100644 --- a/src/registers/model_specific.rs +++ b/src/registers/model_specific.rs @@ -216,11 +216,11 @@ mod x86_64 { /// - Field 2 (SYSCALL): This field is copied directly into CS.Sel. SS.Sel is set to /// this field + 8. Because SYSCALL always switches to CPL 0, the RPL bits /// 33:32 should be initialized to 00b. - pub fn read_raw() -> (u64, u64) { + pub fn read_raw() -> (u16, u16) { let msr_value = unsafe { Self::MSR.read() }; - let sysret = msr_value.get_bits(48..63); - let syscall = msr_value.get_bits(32..47); - (sysret, syscall) + let sysret = msr_value.get_bits(48..64); + let syscall = msr_value.get_bits(32..48); + (sysret.try_into().unwrap(), syscall.try_into().unwrap()) } /// Read the Ring 0 and Ring 3 segment bases. @@ -255,10 +255,10 @@ mod x86_64 { /// - syscall: This field is copied directly into CS.Sel. SS.Sel is set to /// this field + 8. Because SYSCALL always switches to CPL 0, the RPL bits /// 33:32 should be initialized to 00b. - pub fn write_raw(sysret: u64, syscall: u64) { + pub fn write_raw(sysret: u16, syscall: u16) { let mut msr_value = 0u64; - msr_value.set_bits(48..63, sysret); - msr_value.set_bits(32..47, syscall); + msr_value.set_bits(48..64, sysret.into()); + msr_value.set_bits(32..48, syscall.into()); unsafe { Self::MSR.write(msr_value); } From 2748ff763e2a1e2c6999dd097aa0162002b771a8 Mon Sep 17 00:00:00 2001 From: Vinay Chandra Dommeti Date: Sat, 8 Feb 2020 08:46:25 -0800 Subject: [PATCH 3/3] Comments --- src/registers/model_specific.rs | 28 +++++++++++++++++++++------- src/structures/gdt.rs | 5 +++++ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/registers/model_specific.rs b/src/registers/model_specific.rs index 53ec5c917..20bdcfd85 100644 --- a/src/registers/model_specific.rs +++ b/src/registers/model_specific.rs @@ -2,6 +2,7 @@ use crate::registers::rflags::RFlags; use crate::structures::gdt::SegmentSelector; +use crate::PrivilegeLevel; use bit_field::BitField; use bitflags::bitflags; use core::convert::TryInto; @@ -237,8 +238,8 @@ mod x86_64 { ) { let raw = Self::read_raw(); return ( - SegmentSelector((raw.0 + 16 - 3).try_into().unwrap()), - SegmentSelector((raw.0 + 8 - 3).try_into().unwrap()), + SegmentSelector((raw.0 + 16).try_into().unwrap()), + SegmentSelector((raw.0 + 8).try_into().unwrap()), SegmentSelector((raw.1).try_into().unwrap()), SegmentSelector((raw.1 + 8).try_into().unwrap()), ); @@ -255,18 +256,23 @@ mod x86_64 { /// - syscall: This field is copied directly into CS.Sel. SS.Sel is set to /// this field + 8. Because SYSCALL always switches to CPL 0, the RPL bits /// 33:32 should be initialized to 00b. - pub fn write_raw(sysret: u16, syscall: u16) { + /// + /// # Unsafety + /// Unsafe because this can cause system instability if passed in the + /// wrong values for the fields. + pub unsafe fn write_raw(sysret: u16, syscall: u16) { let mut msr_value = 0u64; msr_value.set_bits(48..64, sysret.into()); msr_value.set_bits(32..48, syscall.into()); - unsafe { - Self::MSR.write(msr_value); - } + Self::MSR.write(msr_value); } /// Write the Ring 0 and Ring 3 segment bases. /// The remaining fields are ignored because they are /// not valid for long mode. + /// This function will fail if the segment selectors are + /// not in the correct offset of each other or if the + /// segment selectors do not have correct privileges. pub fn write( cs_sysret: SegmentSelector, ss_sysret: SegmentSelector, @@ -281,7 +287,15 @@ mod x86_64 { return Err("Syscall CS and SS is not offset by 8."); } - Self::write_raw(((ss_sysret.0 - 8) | 0b11).into(), cs_syscall.0.into()); + if ss_sysret.rpl() != PrivilegeLevel::Ring3 { + return Err("Sysret's segment must be a Ring3 segment."); + } + + if ss_syscall.rpl() != PrivilegeLevel::Ring0 { + return Err("Syscall's segment must be a Ring0 segment."); + } + + unsafe { Self::write_raw((ss_sysret.0 - 8).into(), cs_syscall.0.into()) }; Ok(()) } diff --git a/src/structures/gdt.rs b/src/structures/gdt.rs index bade2d1da..3369f2b4d 100644 --- a/src/structures/gdt.rs +++ b/src/structures/gdt.rs @@ -34,6 +34,11 @@ impl SegmentSelector { pub fn rpl(&self) -> PrivilegeLevel { PrivilegeLevel::from_u16(self.0.get_bits(0..2)) } + + /// Set the privilege level for this Segment selector. + pub fn set_rpl(&mut self, rpl: PrivilegeLevel) { + self.0.set_bits(0..2, rpl as u16); + } } impl fmt::Debug for SegmentSelector {