Skip to content

Commit 0b31a16

Browse files
committed
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 eef7a95 commit 0b31a16

File tree

2 files changed

+77
-0
lines changed

2 files changed

+77
-0
lines changed

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_is_valid(unsigned long pfn)
43+
{
44+
return pfn_valid(pfn);
45+
}

rust/kernel/page.rs

Lines changed: 51 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.
@@ -249,6 +251,55 @@ impl Page {
249251
reader.read_raw(unsafe { core::slice::from_raw_parts_mut(dst.cast(), len) })
250252
})
251253
}
254+
255+
/// Returns the physical address of this page.
256+
pub fn phys(&self) -> PhysicalAddr {
257+
// SAFETY: `page` is valid due to the type invariants on `Page`.
258+
unsafe { bindings::page_to_phys(self.as_ptr()) }
259+
}
260+
261+
/// Converts a Rust-owned Page into its physical address.
262+
/// The caller is responsible for calling `from_phys()` to avoid
263+
/// leaking memory.
264+
pub fn into_phys(this: Owned<Self>) -> PhysicalAddr {
265+
ManuallyDrop::new(this).phys()
266+
}
267+
268+
/// Converts a physical address to a Rust-owned Page.
269+
///
270+
/// SAFETY:
271+
/// The caller must ensure that the physical address was previously returned
272+
/// by a call to `Page::into_phys()`, and that the physical address is no
273+
/// longer used after this call, nor is `from_phys()` called again on it.
274+
pub unsafe fn from_phys(phys: PhysicalAddr) -> Owned<Self> {
275+
// SAFETY: By the safety requirements, the physical address must be valid and
276+
// have come from `into_phys()`, so phys_to_page() cannot fail and
277+
// must return the original struct page pointer.
278+
unsafe { Owned::from_raw(NonNull::new_unchecked(bindings::phys_to_page(phys)).cast()) }
279+
}
280+
281+
/// Borrows a Page from a physical address, without taking over ownership.
282+
///
283+
/// If the physical address does not have a `struct page` entry, returns
284+
/// None.
285+
///
286+
/// SAFETY:
287+
/// The caller must ensure that the physical address, if it is backed by a
288+
/// `struct page`, remains available for the duration of the borrowed
289+
/// lifetime.
290+
pub unsafe fn borrow_phys(phys: &PhysicalAddr) -> Option<&Self> {
291+
// SAFETY: This is always safe, as it is just arithmetic
292+
let pfn = unsafe { bindings::phys_to_pfn(*phys) };
293+
// SAFETY: This function is safe to call with any pfn
294+
if !unsafe { bindings::pfn_is_valid(pfn) } {
295+
None
296+
} else {
297+
// SAFETY: We have just checked that the pfn is valid above, so it must
298+
// have a corresponding struct page. By the safety requirements, we can
299+
// return a borrowed reference to it.
300+
Some(unsafe { &*(bindings::pfn_to_page(pfn) as *mut Self as *const Self) })
301+
}
302+
}
252303
}
253304

254305
// SAFETY: See below.

0 commit comments

Comments
 (0)