Skip to content
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
5 changes: 3 additions & 2 deletions src/addr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
14 changes: 11 additions & 3 deletions src/registers/model_specific.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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,
Expand Down
109 changes: 109 additions & 0 deletions src/structures/paging/frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,76 @@ impl<S: PageSize> PhysFrame<S> {
}
}

/// Returns the frame by a physical frame number.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Let's add a panic section to the doc comment.

Let's also mention that the conversion takes the page size into consideration. For example, converting pfn 512 with page size 4K will not return the same address compared to converting pfn 512 with page size 2M.
The way I usually see it used, pfn is based around the smallest page size on the system. For example, AFAIK, on x86-64, Linux always computes the pfn with a 4K page size even though the hardware also supports other page sizes. Your proposal is not to do that and instead consider the page size. I'm fine with that, I just want to make sure that we communicate that to our users.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We just use pfn * page size, the panick seems not needed...

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

That's fair.

Let's still add a section explaining how we interpret PFNs with respect to different page sizes though.

///
/// ```
/// use x86_64::{PhysAddr, structures::paging::{PhysFrame, Size4KiB}};
///
/// assert_eq!(PhysFrame::<Size4KiB>::from_pfn(0x123), PhysFrame::<Size4KiB>::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::<Size4KiB>::try_from_pfn(0x123), Ok(PhysFrame::<Size4KiB>::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<Self, PfnNotValid> {
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)]
Expand All @@ -70,6 +140,27 @@ impl<S: PageSize> PhysFrame<S> {
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::<Size4KiB>::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::<Size4KiB>::containing_address(PhysAddr::new(0xC000_0000)).pfn(), 0xC0000);
/// assert_eq!(PhysFrame::<Size2MiB>::containing_address(PhysAddr::new(0xC000_0000)).pfn(), 0x600);
/// assert_eq!(PhysFrame::<Size1GiB>::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)]
Expand Down Expand Up @@ -256,6 +347,24 @@ impl<S: PageSize> fmt::Debug for PhysFrameRange<S> {
}
}

/// 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)]
Expand Down
Loading