diff --git a/src/addr.rs b/src/addr.rs index d9cc4fcf..e3645034 100644 --- a/src/addr.rs +++ b/src/addr.rs @@ -539,8 +539,9 @@ impl PhysAddr { /// ## Panics /// /// This function panics if a bit in the range 52 to 64 is set. - // If the `memory_encryption` feature has been enabled and an encryption bit has been - // configured, this also panics if the encryption bit is manually set in the address. + /// + /// If the `memory_encryption` feature has been enabled and an encryption bit has been + /// configured, this also panics if the encryption bit is manually set in the address. #[inline] #[const_fn(cfg(not(feature = "memory_encryption")))] pub const fn new(addr: u64) -> Self { diff --git a/src/registers/model_specific.rs b/src/registers/model_specific.rs index a694615d..79340959 100644 --- a/src/registers/model_specific.rs +++ b/src/registers/model_specific.rs @@ -428,9 +428,12 @@ mod x86_64 { /// 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. + /// + /// - sysret: For SYSRETQ (64-bit), the CS selector is set to this + /// field + 16. For SYSRET (32-bit), the CS selector is set to this + /// field. SS.Sel is set to this field + 8. Because SYSRETQ/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. @@ -451,11 +454,16 @@ mod x86_64 { } /// 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. + /// + /// Note that `cs_sysret` should contain the segment to be used for + /// SYSRETQ (64-bit), not SYSRET (32-bit). #[inline] pub fn write( cs_sysret: SegmentSelector, diff --git a/src/structures/paging/frame.rs b/src/structures/paging/frame.rs index 95015969..7ad1a717 100644 --- a/src/structures/paging/frame.rs +++ b/src/structures/paging/frame.rs @@ -46,6 +46,76 @@ impl PhysFrame { } } + /// Returns the frame by a physical frame number. + /// + /// ``` + /// use x86_64::{PhysAddr, structures::paging::{PhysFrame, Size4KiB}}; + /// + /// assert_eq!(PhysFrame::::from_pfn(0x123), PhysFrame::::containing_address(PhysAddr::new(0x123000))); + /// ``` + /// + /// # Panics + /// + /// This function will panic if the resulting address is not valid. + #[inline] + #[rustversion::attr( + since(1.61), + dep_const_fn::const_fn(cfg(not(feature = "memory_encryption"))) + )] + pub fn from_pfn(pfn: u64) -> Self { + match Self::try_from_pfn(pfn) { + Ok(frame) => frame, + Err(_) => panic!("PFNs must not have any bits in the range 40 to 64 set"), + } + } + + /// Returns the frame by a physical frame number. + /// + /// ``` + /// use x86_64::{PhysAddr, structures::paging::{PhysFrame, Size4KiB}}; + /// + /// assert_eq!(PhysFrame::::try_from_pfn(0x123), Ok(PhysFrame::::containing_address(PhysAddr::new(0x123000)))); + /// ``` + /// + /// # Error + /// + /// This function will return an error if the resulting address is not valid. + #[inline] + #[rustversion::attr( + since(1.61), + dep_const_fn::const_fn(cfg(not(feature = "memory_encryption"))) + )] + pub fn try_from_pfn(pfn: u64) -> Result { + let addr_raw = if let Some(addr_raw) = pfn.checked_mul(S::SIZE) { + addr_raw + } else { + return Err(PfnNotValid(pfn)); + }; + let addr = if let Ok(addr) = PhysAddr::try_new(addr_raw) { + addr + } else { + return Err(PfnNotValid(pfn)); + }; + Ok(PhysFrame { + start_address: addr, + size: PhantomData, + }) + } + + /// Returns the frame by a physical frame number. + /// + /// # Safety + /// + /// The resulting address must be valid. + #[inline] + #[rustversion::attr(since(1.61), const)] + pub unsafe fn from_pfn_unchecked(pfn: u64) -> Self { + PhysFrame { + start_address: unsafe { PhysAddr::new_unsafe(pfn * S::SIZE) }, + size: PhantomData, + } + } + /// Returns the frame that contains the given physical address. #[inline] #[rustversion::attr(since(1.61), const)] @@ -70,6 +140,27 @@ impl PhysFrame { S::SIZE } + /// Returns the PFN of the current frame. + /// + /// The PFN is defined to be the address divided by the page size. + /// + /// ``` + /// use x86_64::{PhysAddr, structures::paging::{PhysFrame, Size1GiB, Size2MiB, Size4KiB}}; + /// + /// assert_eq!(PhysFrame::::containing_address(PhysAddr::new(0x123000)).pfn(), 0x123); + /// + /// // Note that this means that the PFN for the same address will be + /// // different for different page sizes. + /// assert_eq!(PhysFrame::::containing_address(PhysAddr::new(0xC000_0000)).pfn(), 0xC0000); + /// assert_eq!(PhysFrame::::containing_address(PhysAddr::new(0xC000_0000)).pfn(), 0x600); + /// assert_eq!(PhysFrame::::containing_address(PhysAddr::new(0xC000_0000)).pfn(), 0x3); + /// ``` + #[inline] + #[rustversion::attr(since(1.61), const)] + pub fn pfn(self) -> u64 { + self.start_address.as_u64() / S::SIZE + } + /// Returns a range of frames, exclusive `end`. #[inline] #[rustversion::attr(since(1.61), const)] @@ -256,6 +347,24 @@ impl fmt::Debug for PhysFrameRange { } } +/// A passed `u64` was not a valid physical address. +/// +/// This means that bits 40 to 64 were not all null. +/// +/// Contains the invalid PFN. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(transparent)] +pub struct PfnNotValid(pub u64); + +// Implementation of display +impl fmt::Display for PfnNotValid { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_tuple("PhysAddrNotValid") + .field(&format_args!("{:#x}", self.0)) + .finish() + } +} + /// An range of physical memory frames, inclusive the upper bound. #[derive(Clone, Copy, PartialEq, Eq, Hash)] #[repr(C)]