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