• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0
2 
3 // Copyright (C) 2024 Google LLC.
4 
5 //! Miscdevice support.
6 //!
7 //! C headers: [`include/linux/miscdevice.h`](srctree/include/linux/miscdevice.h).
8 //!
9 //! Reference: <https://www.kernel.org/doc/html/latest/driver-api/misc_devices.html>
10 
11 use crate::{
12     bindings,
13     device::Device,
14     error::{to_result, Error, Result, VTABLE_DEFAULT_ERROR},
15     ffi::{c_int, c_long, c_uint, c_ulong, c_void},
16     fs::{File, LocalFile},
17     mm::virt::VmaNew,
18     prelude::*,
19     seq_file::SeqFile,
20     str::CStr,
21     types::{AsBytes, ForeignOwnable, Opaque},
22 };
23 use core::{marker::PhantomData, mem::MaybeUninit, pin::Pin, ptr::NonNull};
24 
25 /// The kernel `loff_t` type.
26 #[allow(non_camel_case_types)]
27 pub type loff_t = bindings::loff_t;
28 
29 /// Options for creating a misc device.
30 #[derive(Copy, Clone)]
31 pub struct MiscDeviceOptions {
32     /// The name of the miscdevice.
33     pub name: &'static CStr,
34 }
35 
36 impl MiscDeviceOptions {
37     /// Create a raw `struct miscdev` ready for registration.
into_raw<T: MiscDevice>(self) -> bindings::miscdevice38     pub const fn into_raw<T: MiscDevice>(self) -> bindings::miscdevice {
39         // SAFETY: All zeros is valid for this C type.
40         let mut result: bindings::miscdevice = unsafe { MaybeUninit::zeroed().assume_init() };
41         result.minor = bindings::MISC_DYNAMIC_MINOR as _;
42         result.name = self.name.as_char_ptr();
43         result.fops = MiscdeviceVTable::<T>::build();
44         result
45     }
46 }
47 
48 /// A registration of a miscdevice.
49 ///
50 /// # Invariants
51 ///
52 /// `inner` is a registered misc device.
53 #[repr(transparent)]
54 #[pin_data(PinnedDrop)]
55 pub struct MiscDeviceRegistration<T> {
56     #[pin]
57     inner: Opaque<bindings::miscdevice>,
58     _t: PhantomData<T>,
59 }
60 
61 // SAFETY: It is allowed to call `misc_deregister` on a different thread from where you called
62 // `misc_register`.
63 unsafe impl<T> Send for MiscDeviceRegistration<T> {}
64 // SAFETY: All `&self` methods on this type are written to ensure that it is safe to call them in
65 // parallel.
66 unsafe impl<T> Sync for MiscDeviceRegistration<T> {}
67 
68 impl<T: MiscDevice> MiscDeviceRegistration<T> {
69     /// Register a misc device.
register(opts: MiscDeviceOptions) -> impl PinInit<Self, Error>70     pub fn register(opts: MiscDeviceOptions) -> impl PinInit<Self, Error> {
71         try_pin_init!(Self {
72             inner <- Opaque::try_ffi_init(move |slot: *mut bindings::miscdevice| {
73                 // SAFETY: The initializer can write to the provided `slot`.
74                 unsafe { slot.write(opts.into_raw::<T>()) };
75 
76                 // SAFETY: We just wrote the misc device options to the slot. The miscdevice will
77                 // get unregistered before `slot` is deallocated because the memory is pinned and
78                 // the destructor of this type deallocates the memory.
79                 // INVARIANT: If this returns `Ok(())`, then the `slot` will contain a registered
80                 // misc device.
81                 to_result(unsafe { bindings::misc_register(slot) })
82             }),
83             _t: PhantomData,
84         })
85     }
86 
87     /// Returns a raw pointer to the misc device.
as_raw(&self) -> *mut bindings::miscdevice88     pub fn as_raw(&self) -> *mut bindings::miscdevice {
89         self.inner.get()
90     }
91 
92     /// Access the `this_device` field.
device(&self) -> &Device93     pub fn device(&self) -> &Device {
94         // SAFETY: This can only be called after a successful register(), which always
95         // initialises `this_device` with a valid device. Furthermore, the signature of this
96         // function tells the borrow-checker that the `&Device` reference must not outlive the
97         // `&MiscDeviceRegistration<T>` used to obtain it, so the last use of the reference must be
98         // before the underlying `struct miscdevice` is destroyed.
99         unsafe { Device::as_ref((*self.as_raw()).this_device) }
100     }
101 }
102 
103 #[pinned_drop]
104 impl<T> PinnedDrop for MiscDeviceRegistration<T> {
drop(self: Pin<&mut Self>)105     fn drop(self: Pin<&mut Self>) {
106         // SAFETY: We know that the device is registered by the type invariants.
107         unsafe { bindings::misc_deregister(self.inner.get()) };
108     }
109 }
110 
111 /// Trait implemented by the private data of an open misc device.
112 #[vtable]
113 pub trait MiscDevice: Sized {
114     /// What kind of pointer should `Self` be wrapped in.
115     type Ptr: ForeignOwnable + Send + Sync;
116 
117     /// Called when the misc device is opened.
118     ///
119     /// The returned pointer will be stored as the private data for the file.
open(_file: &File, _misc: &MiscDeviceRegistration<Self>) -> Result<Self::Ptr>120     fn open(_file: &File, _misc: &MiscDeviceRegistration<Self>) -> Result<Self::Ptr>;
121 
122     /// Called when the misc device is released.
release(device: Self::Ptr, _file: &File)123     fn release(device: Self::Ptr, _file: &File) {
124         drop(device);
125     }
126 
127     /// Handle for mmap.
128     ///
129     /// This function is invoked when a user space process invokes the `mmap` system call on
130     /// `file`. The function is a callback that is part of the VMA initializer. The kernel will do
131     /// initial setup of the VMA before calling this function. The function can then interact with
132     /// the VMA initialization by calling methods of `vma`. If the function does not return an
133     /// error, the kernel will complete initialization of the VMA according to the properties of
134     /// `vma`.
mmap( _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>, _file: &File, _vma: &VmaNew, ) -> Result135     fn mmap(
136         _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>,
137         _file: &File,
138         _vma: &VmaNew,
139     ) -> Result {
140         kernel::build_error(VTABLE_DEFAULT_ERROR)
141     }
142 
143     /// Seeks this miscdevice.
llseek( _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>, _file: &LocalFile, _offset: loff_t, _whence: c_int, ) -> Result<loff_t>144     fn llseek(
145         _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>,
146         _file: &LocalFile,
147         _offset: loff_t,
148         _whence: c_int,
149     ) -> Result<loff_t> {
150         kernel::build_error(VTABLE_DEFAULT_ERROR)
151     }
152 
153     /// Read from this miscdevice.
read_iter(_kiocb: Kiocb<'_, Self::Ptr>, _iov: &mut IovIter) -> Result<usize>154     fn read_iter(_kiocb: Kiocb<'_, Self::Ptr>, _iov: &mut IovIter) -> Result<usize> {
155         kernel::build_error(VTABLE_DEFAULT_ERROR)
156     }
157 
158     /// Write to this miscdevice.
write_iter(_kiocb: Kiocb<'_, Self::Ptr>, _iov: &mut IovIter) -> Result<usize>159     fn write_iter(_kiocb: Kiocb<'_, Self::Ptr>, _iov: &mut IovIter) -> Result<usize> {
160         kernel::build_error(VTABLE_DEFAULT_ERROR)
161     }
162 
163     /// Handler for ioctls.
164     ///
165     /// The `cmd` argument is usually manipulated using the utilties in [`kernel::ioctl`].
166     ///
167     /// [`kernel::ioctl`]: mod@crate::ioctl
ioctl( _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>, _file: &File, _cmd: u32, _arg: usize, ) -> Result<isize>168     fn ioctl(
169         _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>,
170         _file: &File,
171         _cmd: u32,
172         _arg: usize,
173     ) -> Result<isize> {
174         kernel::build_error!(VTABLE_DEFAULT_ERROR)
175     }
176 
177     /// Handler for ioctls.
178     ///
179     /// Used for 32-bit userspace on 64-bit platforms.
180     ///
181     /// This method is optional and only needs to be provided if the ioctl relies on structures
182     /// that have different layout on 32-bit and 64-bit userspace. If no implementation is
183     /// provided, then `compat_ptr_ioctl` will be used instead.
184     #[cfg(CONFIG_COMPAT)]
compat_ioctl( _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>, _file: &File, _cmd: u32, _arg: usize, ) -> Result<isize>185     fn compat_ioctl(
186         _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>,
187         _file: &File,
188         _cmd: u32,
189         _arg: usize,
190     ) -> Result<isize> {
191         kernel::build_error!(VTABLE_DEFAULT_ERROR)
192     }
193 
194     /// Show info for this fd.
show_fdinfo( _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>, _m: &SeqFile, _file: &File, )195     fn show_fdinfo(
196         _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>,
197         _m: &SeqFile,
198         _file: &File,
199     ) {
200         kernel::build_error!(VTABLE_DEFAULT_ERROR)
201     }
202 }
203 
204 /// Wrapper for the kernel's `struct kiocb`.
205 ///
206 /// The type `T` represents the private data of the file.
207 pub struct Kiocb<'a, T> {
208     inner: NonNull<bindings::kiocb>,
209     _phantom: PhantomData<&'a T>,
210 }
211 
212 impl<'a, T: ForeignOwnable> Kiocb<'a, T> {
213     /// Get the private data in this kiocb.
private_data(&self) -> <T as ForeignOwnable>::Borrowed<'a>214     pub fn private_data(&self) -> <T as ForeignOwnable>::Borrowed<'a> {
215         // SAFETY: The `kiocb` lets us access the private data.
216         let private = unsafe { (*(*self.inner.as_ptr()).ki_filp).private_data };
217         // SAFETY: The kiocb has shared access to the private data.
218         unsafe { <T as ForeignOwnable>::borrow(private) }
219     }
220 
221     /// Gets the current value of `ki_pos`.
ki_pos(&self) -> loff_t222     pub fn ki_pos(&self) -> loff_t {
223         // SAFETY: The `kiocb` can access `ki_pos`.
224         unsafe { (*self.inner.as_ptr()).ki_pos }
225     }
226 
227     /// Gets a mutable reference to the `ki_pos` field.
ki_pos_mut(&mut self) -> &mut loff_t228     pub fn ki_pos_mut(&mut self) -> &mut loff_t {
229         // SAFETY: The `kiocb` can access `ki_pos`.
230         unsafe { &mut (*self.inner.as_ptr()).ki_pos }
231     }
232 }
233 
234 /// Wrapper for the kernel's `struct iov_iter`.
235 pub struct IovIter {
236     inner: Opaque<bindings::iov_iter>,
237 }
238 
239 impl IovIter {
240     /// Gets a raw pointer to the contents.
as_raw(&self) -> *mut bindings::iov_iter241     pub fn as_raw(&self) -> *mut bindings::iov_iter {
242         self.inner.get()
243     }
244 
245     /// Copy bytes from this iterator.
copy_from_iter(&mut self, buf: &mut [u8]) -> usize246     pub fn copy_from_iter(&mut self, buf: &mut [u8]) -> usize {
247         // SAFETY: The local variable `out` is valid for writing `size_of::<T>()` bytes.
248         unsafe {
249             bindings::_copy_from_iter(
250                 buf.as_mut_ptr().cast::<c_void>(),
251                 buf.len(),
252                 self.inner.get(),
253             )
254         }
255     }
256 
257     /// Copy bytes to this iterator.
copy_to_iter<T: AsBytes>(&mut self, value: &T) -> Result<()>258     pub fn copy_to_iter<T: AsBytes>(&mut self, value: &T) -> Result<()> {
259         let len = size_of::<T>();
260         // SAFETY: The reference points to a value of type `T`, so it is valid for reading
261         // `size_of::<T>()` bytes.
262         let res = unsafe {
263             bindings::_copy_to_iter((value as *const T).cast::<c_void>(), len, self.inner.get())
264         };
265         if res == len {
266             Ok(())
267         } else {
268             Err(EFAULT)
269         }
270     }
271 }
272 
273 /// A vtable for the file operations of a Rust miscdevice.
274 struct MiscdeviceVTable<T: MiscDevice>(PhantomData<T>);
275 
276 impl<T: MiscDevice> MiscdeviceVTable<T> {
277     /// # Safety
278     ///
279     /// `file` and `inode` must be the file and inode for a file that is undergoing initialization.
280     /// The file must be associated with a `MiscDeviceRegistration<T>`.
open(inode: *mut bindings::inode, raw_file: *mut bindings::file) -> c_int281     unsafe extern "C" fn open(inode: *mut bindings::inode, raw_file: *mut bindings::file) -> c_int {
282         // SAFETY: The pointers are valid and for a file being opened.
283         let ret = unsafe { bindings::generic_file_open(inode, raw_file) };
284         if ret != 0 {
285             return ret;
286         }
287 
288         // SAFETY: The open call of a file can access the private data.
289         let misc_ptr = unsafe { (*raw_file).private_data };
290 
291         // SAFETY: This is a miscdevice, so `misc_open()` set the private data to a pointer to the
292         // associated `struct miscdevice` before calling into this method. Furthermore,
293         // `misc_open()` ensures that the miscdevice can't be unregistered and freed during this
294         // call to `fops_open`.
295         let misc = unsafe { &*misc_ptr.cast::<MiscDeviceRegistration<T>>() };
296 
297         // SAFETY:
298         // * This underlying file is valid for (much longer than) the duration of `T::open`.
299         // * There is no active fdget_pos region on the file on this thread.
300         let file = unsafe { File::from_raw_file(raw_file) };
301 
302         let ptr = match T::open(file, misc) {
303             Ok(ptr) => ptr,
304             Err(err) => return err.to_errno(),
305         };
306 
307         // This overwrites the private data with the value specified by the user, changing the type
308         // of this file's private data. All future accesses to the private data is performed by
309         // other fops_* methods in this file, which all correctly cast the private data to the new
310         // type.
311         //
312         // SAFETY: The open call of a file can access the private data.
313         unsafe { (*raw_file).private_data = ptr.into_foreign().cast_mut() };
314 
315         0
316     }
317 
318     /// # Safety
319     ///
320     /// `file` and `inode` must be the file and inode for a file that is being released. The file
321     /// must be associated with a `MiscDeviceRegistration<T>`.
release(_inode: *mut bindings::inode, file: *mut bindings::file) -> c_int322     unsafe extern "C" fn release(_inode: *mut bindings::inode, file: *mut bindings::file) -> c_int {
323         // SAFETY: The release call of a file owns the private data.
324         let private = unsafe { (*file).private_data };
325         // SAFETY: The release call of a file owns the private data.
326         let ptr = unsafe { <T::Ptr as ForeignOwnable>::from_foreign(private) };
327 
328         // SAFETY:
329         // * The file is valid for the duration of this call.
330         // * There is no active fdget_pos region on the file on this thread.
331         T::release(ptr, unsafe { File::from_raw_file(file) });
332 
333         0
334     }
335 
336     /// # Safety
337     ///
338     /// `file` must be a valid file that is associated with a `MiscDeviceRegistration<T>`.
339     /// `vma` must be a vma that is currently being mmap'ed with this file.
mmap( file: *mut bindings::file, vma: *mut bindings::vm_area_struct, ) -> c_int340     unsafe extern "C" fn mmap(
341         file: *mut bindings::file,
342         vma: *mut bindings::vm_area_struct,
343     ) -> c_int {
344         // SAFETY: The mmap call of a file can access the private data.
345         let private = unsafe { (*file).private_data };
346         // SAFETY: This is a Rust Miscdevice, so we call `into_foreign` in `open` and
347         // `from_foreign` in `release`, and `fops_mmap` is guaranteed to be called between those
348         // two operations.
349         let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
350         // SAFETY: The caller provides a vma that is undergoing initial VMA setup.
351         let area = unsafe { VmaNew::from_raw(vma) };
352         // SAFETY:
353         // * The file is valid for the duration of this call.
354         // * There is no active fdget_pos region on the file on this thread.
355         let file = unsafe { File::from_raw_file(file) };
356 
357         match T::mmap(device, file, area) {
358             Ok(()) => 0,
359             Err(err) => err.to_errno(),
360         }
361     }
362 
363     /// # Safety
364     ///
365     /// `file` must be a valid file that is associated with a `MiscDeviceRegistration<T>`.
llseek( file: *mut bindings::file, offset: loff_t, whence: c_int, ) -> loff_t366     unsafe extern "C" fn llseek(
367         file: *mut bindings::file,
368         offset: loff_t,
369         whence: c_int,
370     ) -> loff_t {
371         // SAFETY: The release call of a file owns the private data.
372         let private = unsafe { (*file).private_data };
373         // SAFETY: Ioctl calls can borrow the private data of the file.
374         let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
375         // SAFETY:
376         // * The file is valid for the duration of this call.
377         // * We are inside an fdget_pos region, so there cannot be any active fdget_pos regions on
378         //   other threads.
379         let file = unsafe { LocalFile::from_raw_file(file) };
380 
381         match T::llseek(device, file, offset, whence) {
382             Ok(res) => res as loff_t,
383             Err(err) => err.to_errno() as loff_t,
384         }
385     }
386 
387     /// # Safety
388     ///
389     /// Arguments must be valid.
read_iter( kiocb: *mut bindings::kiocb, iter: *mut bindings::iov_iter, ) -> isize390     unsafe extern "C" fn read_iter(
391         kiocb: *mut bindings::kiocb,
392         iter: *mut bindings::iov_iter,
393     ) -> isize {
394         let kiocb = Kiocb {
395             inner: unsafe { NonNull::new_unchecked(kiocb) },
396             _phantom: PhantomData,
397         };
398         let iov = unsafe { &mut *iter.cast::<IovIter>() };
399 
400         match T::read_iter(kiocb, iov) {
401             Ok(res) => res as isize,
402             Err(err) => err.to_errno() as isize,
403         }
404     }
405 
406     /// # Safety
407     ///
408     /// Arguments must be valid.
write_iter( kiocb: *mut bindings::kiocb, iter: *mut bindings::iov_iter, ) -> isize409     unsafe extern "C" fn write_iter(
410         kiocb: *mut bindings::kiocb,
411         iter: *mut bindings::iov_iter,
412     ) -> isize {
413         let kiocb = Kiocb {
414             inner: unsafe { NonNull::new_unchecked(kiocb) },
415             _phantom: PhantomData,
416         };
417         let iov = unsafe { &mut *iter.cast::<IovIter>() };
418 
419         match T::write_iter(kiocb, iov) {
420             Ok(res) => res as isize,
421             Err(err) => err.to_errno() as isize,
422         }
423     }
424 
425     /// # Safety
426     ///
427     /// `file` must be a valid file that is associated with a `MiscDeviceRegistration<T>`.
ioctl(file: *mut bindings::file, cmd: c_uint, arg: c_ulong) -> c_long428     unsafe extern "C" fn ioctl(file: *mut bindings::file, cmd: c_uint, arg: c_ulong) -> c_long {
429         // SAFETY: The ioctl call of a file can access the private data.
430         let private = unsafe { (*file).private_data };
431         // SAFETY: Ioctl calls can borrow the private data of the file.
432         let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
433 
434         // SAFETY:
435         // * The file is valid for the duration of this call.
436         // * There is no active fdget_pos region on the file on this thread.
437         let file = unsafe { File::from_raw_file(file) };
438 
439         match T::ioctl(device, file, cmd, arg) {
440             Ok(ret) => ret as c_long,
441             Err(err) => err.to_errno() as c_long,
442         }
443     }
444 
445     /// # Safety
446     ///
447     /// `file` must be a valid file that is associated with a `MiscDeviceRegistration<T>`.
448     #[cfg(CONFIG_COMPAT)]
compat_ioctl( file: *mut bindings::file, cmd: c_uint, arg: c_ulong, ) -> c_long449     unsafe extern "C" fn compat_ioctl(
450         file: *mut bindings::file,
451         cmd: c_uint,
452         arg: c_ulong,
453     ) -> c_long {
454         // SAFETY: The compat ioctl call of a file can access the private data.
455         let private = unsafe { (*file).private_data };
456         // SAFETY: Ioctl calls can borrow the private data of the file.
457         let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
458 
459         // SAFETY:
460         // * The file is valid for the duration of this call.
461         // * There is no active fdget_pos region on the file on this thread.
462         let file = unsafe { File::from_raw_file(file) };
463 
464         match T::compat_ioctl(device, file, cmd, arg) {
465             Ok(ret) => ret as c_long,
466             Err(err) => err.to_errno() as c_long,
467         }
468     }
469 
470     /// # Safety
471     ///
472     /// - `file` must be a valid file that is associated with a `MiscDeviceRegistration<T>`.
473     /// - `seq_file` must be a valid `struct seq_file` that we can write to.
show_fdinfo(seq_file: *mut bindings::seq_file, file: *mut bindings::file)474     unsafe extern "C" fn show_fdinfo(seq_file: *mut bindings::seq_file, file: *mut bindings::file) {
475         // SAFETY: The release call of a file owns the private data.
476         let private = unsafe { (*file).private_data };
477         // SAFETY: Ioctl calls can borrow the private data of the file.
478         let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) };
479         // SAFETY:
480         // * The file is valid for the duration of this call.
481         // * There is no active fdget_pos region on the file on this thread.
482         let file = unsafe { File::from_raw_file(file) };
483         // SAFETY: The caller ensures that the pointer is valid and exclusive for the duration in
484         // which this method is called.
485         let m = unsafe { SeqFile::from_raw(seq_file) };
486 
487         T::show_fdinfo(device, m, file);
488     }
489 
490     const VTABLE: bindings::file_operations = bindings::file_operations {
491         open: Some(Self::open),
492         release: Some(Self::release),
493         mmap: if T::HAS_MMAP { Some(Self::mmap) } else { None },
494         llseek: if T::HAS_LLSEEK {
495             Some(Self::llseek)
496         } else {
497             None
498         },
499         read_iter: if T::HAS_READ_ITER {
500             Some(Self::read_iter)
501         } else {
502             None
503         },
504         write_iter: if T::HAS_WRITE_ITER {
505             Some(Self::write_iter)
506         } else {
507             None
508         },
509         unlocked_ioctl: if T::HAS_IOCTL {
510             Some(Self::ioctl)
511         } else {
512             None
513         },
514         #[cfg(CONFIG_COMPAT)]
515         compat_ioctl: if T::HAS_COMPAT_IOCTL {
516             Some(Self::compat_ioctl)
517         } else if T::HAS_IOCTL {
518             Some(bindings::compat_ptr_ioctl)
519         } else {
520             None
521         },
522         show_fdinfo: if T::HAS_SHOW_FDINFO {
523             Some(Self::show_fdinfo)
524         } else {
525             None
526         },
527         // SAFETY: All zeros is a valid value for `bindings::file_operations`.
528         ..unsafe { MaybeUninit::zeroed().assume_init() }
529     };
530 
build() -> &'static bindings::file_operations531     const fn build() -> &'static bindings::file_operations {
532         &Self::VTABLE
533     }
534 }
535