Re: [PATCH 5/6] rust: page: Add physical address conversion functions

From: Andreas Hindborg
Date: Wed Feb 19 2025 - 04:15:45 EST


"Asahi Lina" <lina@xxxxxxxxxxxxx> writes:

[...]

> /// A bitwise shift for the page size.
> @@ -249,6 +251,69 @@ pub unsafe fn copy_from_user_slice_raw(
> reader.read_raw(unsafe { core::slice::from_raw_parts_mut(dst.cast(), len) })
> })
> }
> +
> + /// Returns the physical address of this page.
> + pub fn phys(&self) -> PhysicalAddr {
> + // SAFETY: `page` is valid due to the type invariants on `Page`.
> + unsafe { bindings::page_to_phys(self.as_ptr()) }
> + }
> +
> + /// Converts a Rust-owned Page into its physical address.
> + ///
> + /// The caller is responsible for calling [`Page::from_phys()`] to avoid leaking memory.
> + pub fn into_phys(this: Owned<Self>) -> PhysicalAddr {
> + ManuallyDrop::new(this).phys()
> + }
> +
> + /// Converts a physical address to a Rust-owned Page.
> + ///
> + /// # Safety
> + /// The caller must ensure that the physical address was previously returned by a call to
> + /// [`Page::into_phys()`], and that the physical address is no longer used after this call,
> + /// nor is [`Page::from_phys()`] called again on it.

Do we really need the `PhysicalAddr` to come from a call to
`Page::into_phys`? Could we relax this and say that we don't care how
you came about the `PhysicalAddr` as long as you can guarantee that
ownership is correct? That would make interop with C easer in some cases.

> + pub unsafe fn from_phys(phys: PhysicalAddr) -> Owned<Self> {
> + // SAFETY: By the safety requirements, the physical address must be valid and
> + // have come from `into_phys()`, so phys_to_page() cannot fail and
> + // must return the original struct page pointer.
> + unsafe { Owned::from_raw(NonNull::new_unchecked(bindings::phys_to_page(phys)).cast()) }
> + }
> +
> + /// Borrows a Page from a physical address, without taking over ownership.
> + ///
> + /// If the physical address does not have a `struct page` entry or is not
> + /// part of a System RAM region, returns None.
> + ///
> + /// # Safety
> + /// The caller must ensure that the physical address, if it is backed by a `struct page`,
> + /// remains available for the duration of the borrowed lifetime.
> + pub unsafe fn borrow_phys(phys: &PhysicalAddr) -> Option<&Self> {
> + // SAFETY: This is always safe, as it is just arithmetic
> + let pfn = unsafe { bindings::phys_to_pfn(*phys) };
> + // SAFETY: This function is safe to call with any pfn
> + if !unsafe { bindings::pfn_valid(pfn) && bindings::page_is_ram(pfn) != 0 } {
> + None
> + } else {
> + // SAFETY: We have just checked that the pfn is valid above, so it must
> + // have a corresponding struct page. By the safety requirements, we can
> + // return a borrowed reference to it.
> + Some(unsafe { &*(bindings::pfn_to_page(pfn) as *mut Self as *const Self) })

I think you can maybe go
`bindings::pfn_to_page(pfn).cast::<Self>().cast_const()` here.

> + }
> + }
> +
> + /// Borrows a Page from a physical address, without taking over ownership
> + /// nor checking for validity.
> + ///
> + /// # Safety
> + /// The caller must ensure that the physical address is backed by a `struct page` and
> + /// corresponds to System RAM. This is true when the address was returned by
> + /// [`Page::into_phys()`].
> + pub unsafe fn borrow_phys_unchecked(phys: &PhysicalAddr) -> &Self {
> + // SAFETY: This is always safe, as it is just arithmetic
> + let pfn = unsafe { bindings::phys_to_pfn(*phys) };
> + // SAFETY: The caller guarantees that the pfn is valid. By the safety
> + // requirements, we can return a borrowed reference to it.
> + unsafe { &*(bindings::pfn_to_page(pfn) as *mut Self as *const Self) }

Same applies here.


Best regards,
Andreas Hindborg