Skip to main content

vm/
page.rs

1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use super::page_state::VmPageState;
6use core::ffi::c_void;
7use core::ptr::NonNull;
8use kernel::types::PAddr;
9
10unsafe extern "C" {
11    fn cpp_get_count(state: VmPageState) -> u64;
12    fn cpp_add_to_initial_count(state: VmPageState, n: u64);
13    fn cpp_vm_page_is_loaned(page: *mut c_void) -> bool;
14    fn cpp_vm_page_is_loan_cancelled(page: *mut c_void) -> bool;
15    fn cpp_vm_page_set_is_loaned(page: *mut c_void);
16    fn cpp_vm_page_clear_is_loaned(page: *mut c_void);
17    fn cpp_vm_page_set_is_loan_cancelled(page: *mut c_void);
18    fn cpp_vm_page_clear_is_loan_cancelled(page: *mut c_void);
19    fn cpp_vm_page_dump(page: *mut c_void);
20    fn cpp_vm_page_paddr(page: *mut c_void) -> PAddr;
21    fn cpp_vm_page_state(page: *mut c_void) -> VmPageState;
22    fn cpp_vm_page_set_state(page: *mut c_void, new_state: VmPageState);
23}
24
25/// Type-safe wrapper around a raw pointer to a kernel page.
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub struct VmPagePtr(NonNull<c_void>);
28
29impl VmPagePtr {
30    /// Creates a `VmPagePtr` from a raw pointer.
31    ///
32    /// # Safety
33    ///
34    /// The caller must ensure that `ptr` is a valid pointer to a kernel page.
35    pub const unsafe fn from_raw(ptr: *mut c_void) -> Option<Self> {
36        match NonNull::new(ptr) {
37            Some(nn) => Some(Self(nn)),
38            None => None,
39        }
40    }
41
42    /// Returns the raw pointer.
43    pub fn as_raw(self) -> *mut c_void {
44        self.0.as_ptr()
45    }
46
47    /// Returns whether this page is in the FREE state. When in the FREE state the page is assumed
48    /// to be owned by the relevant PmmNode, and hence unless its lock is held this query must be
49    /// assumed to be racy.
50    ///
51    /// # Safety
52    ///
53    /// The caller must ensure that it either still has ownership of the page or knows it is safe to
54    /// inspect the state.
55    pub unsafe fn is_free(self) -> bool {
56        // SAFETY: The caller guarantees via function safety preconditions that it is safe to
57        // inspect the page state.
58        unsafe { self.state() == VmPageState::Free }
59    }
60
61    /// Returns whether this page is in the FREE_LOANED state. Similar to the FREE state the page is
62    /// assumed to be owned by the relevant PmmNode, however this distinguishes whether the page is
63    /// part of the general purpose free list, versus the more narrowly usable set of loaned pages.
64    ///
65    /// # Safety
66    ///
67    /// The caller must ensure that it either still has ownership of the page or knows it is safe to
68    /// inspect the state.
69    pub unsafe fn is_free_loaned(self) -> bool {
70        // SAFETY: The caller guarantees via function safety preconditions that it is safe to
71        // inspect the page state.
72        unsafe { self.state() == VmPageState::FreeLoaned }
73    }
74
75    /// If true, this page is "loaned" in the sense of being loaned from a contiguous VMO (via
76    /// decommit) to Zircon.  If the original contiguous VMO is deleted, this page will no longer be
77    /// loaned.  A loaned page cannot be pinned.  Instead a different physical page (non-loaned) is
78    /// used for the pin.  A loaned page can be (re-)committed back into its original contiguous
79    /// VMO, which causes the data in the loaned page to be moved into a different physical page
80    /// (which itself can be non-loaned or loaned).  A loaned page cannot be used to allocate a new
81    /// contiguous VMO. Maybe queried by anyone who either owns the page, or has sufficient
82    /// knowledge that the loaned state cannot be being altered in parallel.
83    ///
84    /// # Safety
85    ///
86    /// The caller must ensure that it either still has ownership of the page or knows it is safe to
87    /// inspect the state.
88    pub unsafe fn is_loaned(self) -> bool {
89        // SAFETY: The caller guarantees via function safety preconditions that it is safe to
90        // inspect the loaned state.
91        unsafe { cpp_vm_page_is_loaned(self.as_raw()) }
92    }
93
94    /// If true, the original contiguous VMO wants the page back.  Such pages won't be reused until
95    /// the page is no longer loaned, either via commit of the page back into the contiguous VMO
96    /// that loaned the page, or via deletion of the contiguous VMO that loaned the page. Such pages
97    /// are not in the free_loaned_list_ in pmm, which is how reuse is prevented. Should only be
98    /// called by the PmmNode under its lock.
99    ///
100    /// # Safety
101    ///
102    /// The caller must ensure that it either still has ownership of the page or knows it is safe to
103    /// inspect the state.
104    pub unsafe fn is_loan_cancelled(self) -> bool {
105        // SAFETY: The caller guarantees via function safety preconditions that it is safe to
106        // inspect the loaned state.
107        unsafe { cpp_vm_page_is_loan_cancelled(self.as_raw()) }
108    }
109
110    /// Sets the loaned flag on the page.
111    ///
112    /// # Safety
113    ///
114    /// The caller must ensure that it owns the page and holds the loaned pages lock of the PmmNode
115    pub unsafe fn set_is_loaned(self) {
116        // SAFETY: The caller guarantees ownership of the page and holds the necessary PmmNode lock.
117        unsafe { cpp_vm_page_set_is_loaned(self.as_raw()) }
118    }
119    /// Clears the loaned flag on the page.
120    ///
121    /// # Safety
122    ///
123    /// The caller must ensure that it owns the page and holds the loaned pages lock of the PmmNode
124    pub unsafe fn clear_is_loaned(self) {
125        // SAFETY: The caller guarantees ownership of the page and holds the necessary PmmNode lock.
126        unsafe { cpp_vm_page_clear_is_loaned(self.as_raw()) }
127    }
128
129    /// Sets the loan_cancelled flag on the page. May be done even if not the owner of the page.
130    ///
131    /// # Safety
132    ///
133    /// The caller must ensure that it holds the loaned pages lock of the PmmNode
134    pub unsafe fn set_is_loan_cancelled(self) {
135        // SAFETY: The caller guarantees holding the necessary PmmNode lock.
136        unsafe { cpp_vm_page_set_is_loan_cancelled(self.as_raw()) }
137    }
138    /// Clears the loan_cancelled flag on the page. May be done even if not the owner of the page.
139    ///
140    /// # Safety
141    ///
142    /// The caller must ensure that it holds the loaned pages lock of the PmmNode
143    pub unsafe fn clear_is_loan_cancelled(self) {
144        // SAFETY: The caller guarantees holding the necessary PmmNode lock.
145        unsafe { cpp_vm_page_clear_is_loan_cancelled(self.as_raw()) }
146    }
147
148    /// Dumps information about the page to the debuglog.
149    ///
150    /// # Safety
151    ///
152    /// The caller must ensure that it either still has ownership of the page or knows it is safe to
153    /// access the pages state.
154    pub unsafe fn dump(self) {
155        // SAFETY: The caller guarantees via function safety preconditions that it is safe to access
156        // the page state.
157        unsafe { cpp_vm_page_dump(self.as_raw()) }
158    }
159
160    /// Return the physical address of the page.
161    ///
162    /// # Safety
163    ///
164    /// The caller must ensure that it either still has ownership of the page or knows it is safe to
165    /// inspect the state.
166    pub unsafe fn paddr(self) -> PAddr {
167        // SAFETY: The caller guarantees via function safety preconditions that it is safe to
168        // inspect the page state.
169        unsafe { cpp_vm_page_paddr(self.as_raw()) }
170    }
171
172    /// Return the current VmPageState of this page.
173    ///
174    /// # Safety
175    ///
176    /// The caller must ensure that it either still has ownership of the page or knows it is safe to
177    /// inspect the state.
178    pub unsafe fn state(self) -> VmPageState {
179        // SAFETY: The caller guarantees via function safety preconditions that it is safe to
180        // inspect the page state.
181        unsafe { cpp_vm_page_state(self.as_raw()) }
182    }
183
184    /// Sets the VmPageState of this page.
185    ///
186    /// # Safety
187    ///
188    /// The caller must ensure that it owns the page or holds the necessary locks to modify its
189    /// state.
190    pub unsafe fn set_state(self, new_state: VmPageState) {
191        // SAFETY: The caller guarantees ownership of the page or holding the necessary locks to
192        // modify its state.
193        unsafe { cpp_vm_page_set_state(self.as_raw(), new_state) }
194    }
195}
196
197// Return the approximate number of pages in state |state|.
198//
199// When called concurrently with |set_state|, the count may be off by a small amount.
200#[inline]
201pub fn get_count(state: VmPageState) -> u64 {
202    // SAFETY: cpp_get_count is a thread-safe FFI call that disables preemption and reads atomic
203    // per-CPU counters.
204    unsafe { cpp_get_count(state) }
205}
206
207// Add |n| to the count of pages in state |state|.
208//
209// Should be used when first constructing pages.
210#[inline]
211pub fn add_to_initial_count(state: VmPageState, n: u64) {
212    // SAFETY: cpp_add_to_initial_count is a thread-safe FFI call that disables preemption and
213    // modifies atomic per-CPU counters during initialization.
214    unsafe {
215        cpp_add_to_initial_count(state, n);
216    }
217}