starnix_modules_userfaultfd/
userfault_file.rs1use linux_uapi::{
6 UFFDIO_CONTINUE, UFFDIO_COPY, UFFDIO_WAKE, UFFDIO_WRITEPROTECT, UFFDIO_ZEROPAGE, uffdio_copy,
7 uffdio_zeropage,
8};
9use starnix_core::mm::{
10 FaultCopyMode, FaultRegisterMode, FaultZeroMode, MemoryAccessorExt, UserFault,
11 UserFaultFeatures,
12};
13use starnix_core::task::{CurrentTask, EventHandler, WaitCanceler, Waiter};
14use starnix_core::vfs::{
15 Anon, FileHandle, FileObject, FileObjectState, FileOps, InputBuffer, OutputBuffer,
16 fileops_impl_nonseekable, fileops_impl_noop_sync,
17};
18use starnix_logging::track_stub;
19
20use starnix_uapi::errors::Errno;
21use starnix_uapi::open_flags::OpenFlags;
22use starnix_uapi::user_address::UserRef;
23use starnix_uapi::vfs::FdEvents;
24use starnix_uapi::{
25 _UFFDIO_API, _UFFDIO_REGISTER, _UFFDIO_UNREGISTER, UFFDIO, UFFDIO_API, UFFDIO_MOVE,
26 UFFDIO_POISON, UFFDIO_REGISTER, UFFDIO_UNREGISTER, errno, error, uapi, uffdio_api,
27 uffdio_range, uffdio_register,
28};
29use static_assertions::const_assert_eq;
30use std::sync::Arc;
31
32uapi::check_arch_independent_layout! {
33 uffdio_api {
34 api,
35 features,
36 ioctls,
37 }
38
39 uffdio_range {
40 start,
41 len,
42 }
43
44 uffdio_register {
45 range,
46 mode,
47 ioctls,
48 }
49
50 uffdio_copy {
51 dst,
52 src,
53 len,
54 mode,
55 copy,
56 }
57
58 uffdio_zeropage {
59 range,
60 mode,
61 zeropage,
62 }
63
64 uffdio_writeprotect {
65 range,
66 mode,
67 }
68
69 uffdio_continue {
70 range,
71 mode,
72 mapped,
73 }
74
75 uffdio_poison {
76 range,
77 mode,
78 updated,
79 }
80
81 uffdio_move {
82 dst,
83 src,
84 len,
85 mode,
86 move_,
87 }
88}
89
90pub struct UserFaultFile {
91 inner: Arc<UserFault>,
92}
93
94const_assert_eq!(UFFDIO, 0xAA);
96
97impl UserFaultFile {
98 pub fn new(
99 current_task: &CurrentTask,
100 open_flags: OpenFlags,
101 _user_mode_only: bool,
102 ) -> Result<FileHandle, Errno> {
103 let mm = current_task.mm()?;
104 let inner = Arc::new(UserFault::new(Arc::downgrade(&mm)));
105 mm.register_uffd(&inner);
106 Anon::new_file(current_task, Box::new(Self { inner }), open_flags, "[userfaultfd]")
107 }
108
109 fn api_handshake(
110 &self,
111 _current_task: &CurrentTask,
112 request: uffdio_api,
113 ) -> Result<uffdio_api, Errno> {
114 if self.inner.is_initialized() {
115 return error!(EPERM, "userfault object already initialized");
116 }
117
118 if request.api != UFFDIO as u64 {
119 return error!(EINVAL, format!("unsupported API version {}", request.api));
120 }
121
122 let requested_features =
123 UserFaultFeatures::from_bits(request.features.try_into().map_err(|_| errno!(EINVAL))?)
124 .ok_or_else(|| errno!(EINVAL))?;
125 let requested_unsupported = requested_features.difference(UserFaultFeatures::ALL_SUPPORTED);
126 if !requested_unsupported.is_empty() {
127 return error!(EINVAL);
128 }
129
130 self.inner.initialize(requested_features);
132
133 Ok(uffdio_api {
134 api: request.api,
135 features: UserFaultFeatures::ALL_SUPPORTED.bits() as u64,
136 ioctls: (1 << _UFFDIO_API) | (1 << _UFFDIO_REGISTER) | (1 << _UFFDIO_UNREGISTER),
137 })
138 }
139}
140
141impl FileOps for UserFaultFile {
142 fileops_impl_nonseekable!();
143 fileops_impl_noop_sync!();
144 fn read(
145 &self,
146 _file: &FileObject,
147 _current_task: &CurrentTask,
148 _offset: usize,
149 _data: &mut dyn OutputBuffer,
150 ) -> Result<usize, Errno> {
151 track_stub!(TODO("https://fxbug.dev/391599171"), "event-based uffd operations");
152 error!(ENOTSUP)
153 }
154
155 fn write(
156 &self,
157 _file: &FileObject,
158 _current_task: &CurrentTask,
159 _offset: usize,
160 _data: &mut dyn InputBuffer,
161 ) -> Result<usize, Errno> {
162 error!(EINVAL)
163 }
164
165 fn query_events(
166 &self,
167 _file: &FileObject,
168 _current_task: &CurrentTask,
169 ) -> Result<FdEvents, Errno> {
170 track_stub!(TODO("https://fxbug.dev/391599171"), "event-based uffd operations");
171 error!(ENOTSUP)
172 }
173
174 fn wait_async(
175 &self,
176 _file: &FileObject,
177 _current_task: &CurrentTask,
178 _waiter: &Waiter,
179 _events: FdEvents,
180 _handler: EventHandler,
181 ) -> Option<WaitCanceler> {
182 track_stub!(TODO("https://fxbug.dev/391599171"), "event-based uffd operations");
183 None
184 }
185
186 fn ioctl(
187 &self,
188 _file: &FileObject,
189 current_task: &CurrentTask,
190 request: u32,
191 arg: starnix_syscalls::SyscallArg,
192 ) -> Result<starnix_syscalls::SyscallResult, Errno> {
193 match request {
194 UFFDIO_API => {
195 let arg: UserRef<uffdio_api> = arg.into();
196 let request = current_task.read_object(arg)?;
197 match self.api_handshake(current_task, request) {
198 Ok(reply) => {
199 current_task.write_object(arg, &reply)?;
200 Ok(0.into())
201 }
202 Err(e) => {
203 current_task.write_object(arg, &uffdio_api::default())?;
204 Err(e)
205 }
206 }
207 }
208
209 UFFDIO_REGISTER => {
210 let arg: UserRef<uffdio_register> = arg.into();
211 let mut request = current_task.read_object(arg)?;
212
213 request.ioctls = self
214 .inner
215 .op_register(
216 request.range.start.into(),
217 request.range.len,
218 FaultRegisterMode::from_bits_truncate(
219 request.mode.try_into().map_err(|_| errno!(EINVAL))?,
220 ),
221 )?
222 .bits();
223 current_task.write_object(arg, &request)?;
224 Ok(0.into())
225 }
226
227 UFFDIO_UNREGISTER => {
228 let arg: UserRef<uffdio_range> = arg.into();
229 let request = current_task.read_object(arg)?;
230 self.inner.op_unregister(request.start.into(), request.len)?;
231 Ok(0.into())
232 }
233
234 UFFDIO_ZEROPAGE => {
235 let arg: UserRef<uffdio_zeropage> = arg.into();
236 let mut request = current_task.read_object(arg)?;
237 let ioctl_res = self.inner.op_zero(
238 request.range.start.into(),
239 request.range.len,
240 FaultZeroMode::from_bits_truncate(
241 request.mode.try_into().map_err(|_| errno!(EINVAL))?,
242 ),
243 );
244 request.zeropage = match ioctl_res {
245 Ok(bytes) => bytes as i64,
246 Err(ref e) => -1 * (e.code.error_code() as i64),
247 };
248 current_task.write_object(arg, &request)?;
249 match ioctl_res {
252 Ok(bytes) if bytes == request.range.len as usize => Ok(0.into()),
253 Err(e) => Err(e),
254 _ => error!(EAGAIN),
255 }
256 }
257
258 UFFDIO_COPY => {
259 let arg: UserRef<uffdio_copy> = arg.into();
260 let mut request = current_task.read_object(arg)?;
261 let mm = current_task.mm()?;
262 let ioctl_res = self.inner.op_copy(
263 &mm,
264 request.src.into(),
265 request.dst.into(),
266 request.len,
267 FaultCopyMode::from_bits_truncate(
268 request.mode.try_into().map_err(|_| errno!(EINVAL))?,
269 ),
270 );
271 request.copy = match ioctl_res {
272 Ok(bytes) => bytes as i64,
273 Err(ref e) => -1 * (e.code.error_code() as i64),
274 };
275 current_task.write_object(arg, &request)?;
276 match ioctl_res {
279 Ok(bytes) if bytes == request.len as usize => Ok(0.into()),
280 Err(e) => Err(e),
281 _ => error!(EAGAIN),
282 }
283 }
284
285 UFFDIO_MOVE => {
286 track_stub!(TODO("https://fxbug.dev/297375964"), "basic uffd ioctls", request);
287 error!(ENOSYS)
288 }
289
290 UFFDIO_WAKE | UFFDIO_WRITEPROTECT | UFFDIO_CONTINUE | UFFDIO_POISON => {
291 track_stub!(
292 TODO("https://fxbug.dev/322893681"),
293 "full set of uffd ioctls",
294 request
295 );
296 error!(ENOSYS)
297 }
298
299 unknown => error!(EINVAL, format!("unknown ioctl request {unknown}")),
300 }
301 }
302
303 fn close(self: Box<Self>, _file: &FileObjectState, _current_task: &CurrentTask) {
305 self.inner.cleanup();
306 }
307}