Skip to content

Commit 77aa68b

Browse files
hoshinolinajannau
authored andcommitted
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 <lina@asahilina.net>
1 parent 8bae89f commit 77aa68b

2 files changed

Lines changed: 91 additions & 0 deletions

File tree

rust/helpers/page.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// SPDX-License-Identifier: GPL-2.0
22

3+
#include <asm/io.h>
34
#include <linux/gfp.h>
45
#include <linux/highmem.h>
56

@@ -17,3 +18,28 @@ void rust_helper_kunmap_local(const void *addr)
1718
{
1819
kunmap_local(addr);
1920
}
21+
22+
struct page *rust_helper_phys_to_page(phys_addr_t phys)
23+
{
24+
return phys_to_page(phys);
25+
}
26+
27+
phys_addr_t rust_helper_page_to_phys(struct page *page)
28+
{
29+
return page_to_phys(page);
30+
}
31+
32+
unsigned long rust_helper_phys_to_pfn(phys_addr_t phys)
33+
{
34+
return __phys_to_pfn(phys);
35+
}
36+
37+
struct page *rust_helper_pfn_to_page(unsigned long pfn)
38+
{
39+
return pfn_to_page(pfn);
40+
}
41+
42+
bool rust_helper_pfn_valid(unsigned long pfn)
43+
{
44+
return pfn_valid(pfn);
45+
}

rust/kernel/page.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
//! Kernel page allocation and management.
44
55
use crate::{
6+
addr::*,
67
alloc::{AllocError, Flags},
78
bindings,
89
error::code::*,
910
error::Result,
1011
types::{Opaque, Ownable, Owned},
1112
uaccess::UserSliceReader,
1213
};
14+
use core::mem::ManuallyDrop;
1315
use core::ptr::{self, NonNull};
1416

1517
/// A bitwise shift for the page size.
@@ -248,6 +250,69 @@ impl Page {
248250
reader.read_raw(unsafe { core::slice::from_raw_parts_mut(dst.cast(), len) })
249251
})
250252
}
253+
254+
/// Returns the physical address of this page.
255+
pub fn phys(&self) -> PhysicalAddr {
256+
// SAFETY: `page` is valid due to the type invariants on `Page`.
257+
unsafe { bindings::page_to_phys(self.as_ptr()) }
258+
}
259+
260+
/// Converts a Rust-owned Page into its physical address.
261+
/// The caller is responsible for calling `from_phys()` to avoid
262+
/// leaking memory.
263+
pub fn into_phys(this: Owned<Self>) -> PhysicalAddr {
264+
ManuallyDrop::new(this).phys()
265+
}
266+
267+
/// Converts a physical address to a Rust-owned Page.
268+
///
269+
/// SAFETY:
270+
/// The caller must ensure that the physical address was previously returned
271+
/// by a call to `Page::into_phys()`, and that the physical address is no
272+
/// longer used after this call, nor is `from_phys()` called again on it.
273+
pub unsafe fn from_phys(phys: PhysicalAddr) -> Owned<Self> {
274+
// SAFETY: By the safety requirements, the physical address must be valid and
275+
// have come from `into_phys()`, so phys_to_page() cannot fail and
276+
// must return the original struct page pointer.
277+
unsafe { Owned::from_raw(NonNull::new_unchecked(bindings::phys_to_page(phys)).cast()) }
278+
}
279+
280+
/// Borrows a Page from a physical address, without taking over ownership.
281+
///
282+
/// If the physical address does not have a `struct page` entry or is not
283+
/// part of the System RAM region, returns None.
284+
///
285+
/// SAFETY:
286+
/// The caller must ensure that the physical address, if it is backed by a
287+
/// `struct page`, remains available for the duration of the borrowed
288+
/// lifetime.
289+
pub unsafe fn borrow_phys(phys: &PhysicalAddr) -> Option<&Self> {
290+
// SAFETY: This is always safe, as it is just arithmetic
291+
let pfn = unsafe { bindings::phys_to_pfn(*phys) };
292+
// SAFETY: This function is safe to call with any pfn
293+
if !unsafe { bindings::pfn_valid(pfn) && bindings::page_is_ram(pfn) != 0 } {
294+
None
295+
} else {
296+
// SAFETY: We have just checked that the pfn is valid above, so it must
297+
// have a corresponding struct page. By the safety requirements, we can
298+
// return a borrowed reference to it.
299+
Some(unsafe { &*(bindings::pfn_to_page(pfn) as *mut Self as *const Self) })
300+
}
301+
}
302+
303+
/// Borrows a Page from a physical address, without taking over ownership
304+
/// nor checking for validity.
305+
///
306+
/// SAFETY:
307+
/// The caller must ensure that the physical address is backed by a
308+
/// `struct page` and corresponds to System RAM.
309+
pub unsafe fn borrow_phys_unchecked(phys: &PhysicalAddr) -> &Self {
310+
// SAFETY: This is always safe, as it is just arithmetic
311+
let pfn = unsafe { bindings::phys_to_pfn(*phys) };
312+
// SAFETY: The caller guarantees that the pfn is valid. By the safety
313+
// requirements, we can return a borrowed reference to it.
314+
unsafe { &*(bindings::pfn_to_page(pfn) as *mut Self as *const Self) }
315+
}
251316
}
252317

253318
// SAFETY: See below.

0 commit comments

Comments
 (0)