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}