From 3c4cf32e5987ee45e7456b1009f1c399ad863117 Mon Sep 17 00:00:00 2001 From: Asahi Lina Date: Tue, 10 Dec 2024 04:04:55 +0900 Subject: [PATCH] rust: page: Add physical address conversion functions Add methods to allow code using the Page type to obtain the physical address of a page, convert to and from an (owned) physical address, and borrow a Page from a physical address. Most of these operations are, as you might expect, unsafe. These primitives are useful to implement page table structures in Rust, and to implement arbitrary physical memory access (as needed to walk arbitrary page tables and dereference through them). These mechanisms are, of course, fraught with danger, and are only expected to be used for core memory management code (in e.g. drivers with their own device page table implementations) and for debug features such as crash dumps of device memory. Signed-off-by: Asahi Lina --- rust/helpers/page.c | 26 +++++++++++++++++++++++ rust/kernel/page.rs | 51 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/rust/helpers/page.c b/rust/helpers/page.c index b3f2b8fbf87fc9..8a271cbb618224 100644 --- a/rust/helpers/page.c +++ b/rust/helpers/page.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 +#include #include #include @@ -17,3 +18,28 @@ void rust_helper_kunmap_local(const void *addr) { kunmap_local(addr); } + +struct page *rust_helper_phys_to_page(phys_addr_t phys) +{ + return phys_to_page(phys); +} + +phys_addr_t rust_helper_page_to_phys(struct page *page) +{ + return page_to_phys(page); +} + +unsigned long rust_helper_phys_to_pfn(phys_addr_t phys) +{ + return __phys_to_pfn(phys); +} + +struct page *rust_helper_pfn_to_page(unsigned long pfn) +{ + return pfn_to_page(pfn); +} + +bool rust_helper_pfn_is_valid(unsigned long pfn) +{ + return pfn_valid(pfn); +} diff --git a/rust/kernel/page.rs b/rust/kernel/page.rs index d7ebb9475eda7b..f86771e110a308 100644 --- a/rust/kernel/page.rs +++ b/rust/kernel/page.rs @@ -3,6 +3,7 @@ //! Kernel page allocation and management. use crate::{ + addr::*, alloc::{AllocError, Flags}, bindings, error::code::*, @@ -10,6 +11,7 @@ use crate::{ types::{Opaque, Ownable, Owned}, uaccess::UserSliceReader, }; +use core::mem::ManuallyDrop; use core::ptr::{self, NonNull}; /// A bitwise shift for the page size. @@ -249,6 +251,55 @@ impl Page { 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 `from_phys()` to avoid + /// leaking memory. + pub fn into_phys(this: Owned) -> 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 `from_phys()` called again on it. + pub unsafe fn from_phys(phys: PhysicalAddr) -> Owned { + // 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, 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<'a>(phys: PhysicalAddr) -> Option<&'a 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_is_valid(pfn) } { + 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::phys_to_page(phys) as *mut Self as *const Self) }) + } + } } // SAFETY: See below.