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, 15 fs::{ 16 File, 17 Kiocb, // 18 }, 19 iov::{ 20 IovIterDest, 21 IovIterSource, // 22 }, 23 mm::virt::VmaNew, 24 module::this_module, 25 prelude::*, 26 seq_file::SeqFile, 27 types::{ 28 ForeignOwnable, 29 Opaque, // 30 }, // 31 }; 32 use core::marker::PhantomData; 33 34 /// Options for creating a misc device. 35 #[derive(Copy, Clone)] 36 pub struct MiscDeviceOptions { 37 /// The name of the miscdevice. 38 pub name: &'static CStr, 39 } 40 41 impl MiscDeviceOptions { 42 /// Create a raw `struct miscdev` ready for registration. 43 pub const fn into_raw<T: MiscDevice>(self) -> bindings::miscdevice { 44 let mut result: bindings::miscdevice = pin_init::zeroed(); 45 result.minor = bindings::MISC_DYNAMIC_MINOR as ffi::c_int; 46 result.name = crate::str::as_char_ptr_in_const_context(self.name); 47 result.fops = MiscdeviceVTable::<T>::build(); 48 result 49 } 50 } 51 52 /// A registration of a miscdevice. 53 /// 54 /// # Invariants 55 /// 56 /// - `inner` contains a `struct miscdevice` that is registered using 57 /// `misc_register()`. 58 /// - This registration remains valid for the entire lifetime of the 59 /// [`MiscDeviceRegistration`] instance. 60 /// - Deregistration occurs exactly once in [`Drop`] via `misc_deregister()`. 61 /// - `inner` wraps a valid, pinned `miscdevice` created using 62 /// [`MiscDeviceOptions::into_raw`]. 63 #[repr(transparent)] 64 #[pin_data(PinnedDrop)] 65 pub struct MiscDeviceRegistration<T> { 66 #[pin] 67 inner: Opaque<bindings::miscdevice>, 68 _t: PhantomData<T>, 69 } 70 71 // SAFETY: It is allowed to call `misc_deregister` on a different thread from where you called 72 // `misc_register`. 73 unsafe impl<T> Send for MiscDeviceRegistration<T> {} 74 // SAFETY: All `&self` methods on this type are written to ensure that it is safe to call them in 75 // parallel. 76 unsafe impl<T> Sync for MiscDeviceRegistration<T> {} 77 78 impl<T: MiscDevice> MiscDeviceRegistration<T> { 79 /// Register a misc device. 80 pub fn register(opts: MiscDeviceOptions) -> impl PinInit<Self, Error> { 81 try_pin_init!(Self { 82 inner <- Opaque::try_ffi_init(move |slot: *mut bindings::miscdevice| { 83 // SAFETY: The initializer can write to the provided `slot`. 84 unsafe { slot.write(opts.into_raw::<T>()) }; 85 86 // SAFETY: We just wrote the misc device options to the slot. The miscdevice will 87 // get unregistered before `slot` is deallocated because the memory is pinned and 88 // the destructor of this type deallocates the memory. 89 // INVARIANT: If this returns `Ok(())`, then the `slot` will contain a registered 90 // misc device. 91 to_result(unsafe { bindings::misc_register(slot) }) 92 }), 93 _t: PhantomData, 94 }) 95 } 96 97 /// Returns a raw pointer to the misc device. 98 pub fn as_raw(&self) -> *mut bindings::miscdevice { 99 self.inner.get() 100 } 101 102 /// Access the `this_device` field. 103 pub fn device(&self) -> &Device { 104 // SAFETY: This can only be called after a successful register(), which always 105 // initialises `this_device` with a valid device. Furthermore, the signature of this 106 // function tells the borrow-checker that the `&Device` reference must not outlive the 107 // `&MiscDeviceRegistration<T>` used to obtain it, so the last use of the reference must be 108 // before the underlying `struct miscdevice` is destroyed. 109 unsafe { Device::from_raw((*self.as_raw()).this_device) } 110 } 111 } 112 113 #[pinned_drop] 114 impl<T> PinnedDrop for MiscDeviceRegistration<T> { 115 fn drop(self: Pin<&mut Self>) { 116 // SAFETY: We know that the device is registered by the type invariants. 117 unsafe { bindings::misc_deregister(self.inner.get()) }; 118 } 119 } 120 121 /// Trait implemented by the private data of an open misc device. 122 #[vtable] 123 pub trait MiscDevice: Sized { 124 /// What kind of pointer should `Self` be wrapped in. 125 type Ptr: ForeignOwnable + Send + Sync; 126 127 /// Called when the misc device is opened. 128 /// 129 /// The returned pointer will be stored as the private data for the file. 130 fn open(_file: &File, _misc: &MiscDeviceRegistration<Self>) -> Result<Self::Ptr>; 131 132 /// Called when the misc device is released. 133 fn release(device: Self::Ptr, _file: &File) { 134 drop(device); 135 } 136 137 /// Handle for mmap. 138 /// 139 /// This function is invoked when a user space process invokes the `mmap` system call on 140 /// `file`. The function is a callback that is part of the VMA initializer. The kernel will do 141 /// initial setup of the VMA before calling this function. The function can then interact with 142 /// the VMA initialization by calling methods of `vma`. If the function does not return an 143 /// error, the kernel will complete initialization of the VMA according to the properties of 144 /// `vma`. 145 #[optional] 146 fn mmap( 147 _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>, 148 _file: &File, 149 _vma: &VmaNew, 150 ) -> Result; 151 152 /// Read from this miscdevice. 153 #[optional] 154 fn read_iter(_kiocb: Kiocb<'_, Self::Ptr>, _iov: &mut IovIterDest<'_>) -> Result<usize>; 155 156 /// Write to this miscdevice. 157 #[optional] 158 fn write_iter(_kiocb: Kiocb<'_, Self::Ptr>, _iov: &mut IovIterSource<'_>) -> Result<usize>; 159 160 /// Handler for ioctls. 161 /// 162 /// The `cmd` argument is usually manipulated using the utilities in [`kernel::ioctl`]. 163 /// 164 /// [`kernel::ioctl`]: mod@crate::ioctl 165 #[optional] 166 fn ioctl( 167 _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>, 168 _file: &File, 169 _cmd: u32, 170 _arg: usize, 171 ) -> Result<isize>; 172 173 /// Handler for ioctls. 174 /// 175 /// Used for 32-bit userspace on 64-bit platforms. 176 /// 177 /// This method is optional and only needs to be provided if the ioctl relies on structures 178 /// that have different layout on 32-bit and 64-bit userspace. If no implementation is 179 /// provided, then `compat_ptr_ioctl` will be used instead. 180 #[cfg(CONFIG_COMPAT)] 181 #[optional] 182 fn compat_ioctl( 183 _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>, 184 _file: &File, 185 _cmd: u32, 186 _arg: usize, 187 ) -> Result<isize>; 188 189 /// Show info for this fd. 190 #[optional] 191 fn show_fdinfo( 192 _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>, 193 _m: &SeqFile, 194 _file: &File, 195 ); 196 } 197 198 /// A vtable for the file operations of a Rust miscdevice. 199 struct MiscdeviceVTable<T: MiscDevice>(PhantomData<T>); 200 201 impl<T: MiscDevice> MiscdeviceVTable<T> { 202 /// # Safety 203 /// 204 /// `file` and `inode` must be the file and inode for a file that is undergoing initialization. 205 /// The file must be associated with a `MiscDeviceRegistration<T>`. 206 unsafe extern "C" fn open(inode: *mut bindings::inode, raw_file: *mut bindings::file) -> c_int { 207 // SAFETY: The pointers are valid and for a file being opened. 208 let ret = unsafe { bindings::generic_file_open(inode, raw_file) }; 209 if ret != 0 { 210 return ret; 211 } 212 213 // SAFETY: The open call of a file can access the private data. 214 let misc_ptr = unsafe { (*raw_file).private_data }; 215 216 // SAFETY: This is a miscdevice, so `misc_open()` set the private data to a pointer to the 217 // associated `struct miscdevice` before calling into this method. Furthermore, 218 // `misc_open()` ensures that the miscdevice can't be unregistered and freed during this 219 // call to `fops_open`. 220 let misc = unsafe { &*misc_ptr.cast::<MiscDeviceRegistration<T>>() }; 221 222 // SAFETY: 223 // * This underlying file is valid for (much longer than) the duration of `T::open`. 224 // * There is no active fdget_pos region on the file on this thread. 225 let file = unsafe { File::from_raw_file(raw_file) }; 226 227 let ptr = match T::open(file, misc) { 228 Ok(ptr) => ptr, 229 Err(err) => return err.to_errno(), 230 }; 231 232 // This overwrites the private data with the value specified by the user, changing the type 233 // of this file's private data. All future accesses to the private data is performed by 234 // other fops_* methods in this file, which all correctly cast the private data to the new 235 // type. 236 // 237 // SAFETY: The open call of a file can access the private data. 238 unsafe { (*raw_file).private_data = ptr.into_foreign() }; 239 240 0 241 } 242 243 /// # Safety 244 /// 245 /// `file` and `inode` must be the file and inode for a file that is being released. The file 246 /// must be associated with a `MiscDeviceRegistration<T>`. 247 unsafe extern "C" fn release(_inode: *mut bindings::inode, file: *mut bindings::file) -> c_int { 248 // SAFETY: The release call of a file owns the private data. 249 let private = unsafe { (*file).private_data }; 250 // SAFETY: The release call of a file owns the private data. 251 let ptr = unsafe { <T::Ptr as ForeignOwnable>::from_foreign(private) }; 252 253 // SAFETY: 254 // * The file is valid for the duration of this call. 255 // * There is no active fdget_pos region on the file on this thread. 256 T::release(ptr, unsafe { File::from_raw_file(file) }); 257 258 0 259 } 260 261 /// # Safety 262 /// 263 /// `kiocb` must be correspond to a valid file that is associated with a 264 /// `MiscDeviceRegistration<T>`. `iter` must be a valid `struct iov_iter` for writing. 265 unsafe extern "C" fn read_iter( 266 kiocb: *mut bindings::kiocb, 267 iter: *mut bindings::iov_iter, 268 ) -> isize { 269 // SAFETY: The caller provides a valid `struct kiocb` associated with a 270 // `MiscDeviceRegistration<T>` file. 271 let kiocb = unsafe { Kiocb::from_raw(kiocb) }; 272 // SAFETY: This is a valid `struct iov_iter` for writing. 273 let iov = unsafe { IovIterDest::from_raw(iter) }; 274 275 match T::read_iter(kiocb, iov) { 276 Ok(res) => res as isize, 277 Err(err) => err.to_errno() as isize, 278 } 279 } 280 281 /// # Safety 282 /// 283 /// `kiocb` must be correspond to a valid file that is associated with a 284 /// `MiscDeviceRegistration<T>`. `iter` must be a valid `struct iov_iter` for reading. 285 unsafe extern "C" fn write_iter( 286 kiocb: *mut bindings::kiocb, 287 iter: *mut bindings::iov_iter, 288 ) -> isize { 289 // SAFETY: The caller provides a valid `struct kiocb` associated with a 290 // `MiscDeviceRegistration<T>` file. 291 let kiocb = unsafe { Kiocb::from_raw(kiocb) }; 292 // SAFETY: This is a valid `struct iov_iter` for reading. 293 let iov = unsafe { IovIterSource::from_raw(iter) }; 294 295 match T::write_iter(kiocb, iov) { 296 Ok(res) => res as isize, 297 Err(err) => err.to_errno() as isize, 298 } 299 } 300 301 /// # Safety 302 /// 303 /// `file` must be a valid file that is associated with a `MiscDeviceRegistration<T>`. 304 /// `vma` must be a vma that is currently being mmap'ed with this file. 305 unsafe extern "C" fn mmap( 306 file: *mut bindings::file, 307 vma: *mut bindings::vm_area_struct, 308 ) -> c_int { 309 // SAFETY: The mmap call of a file can access the private data. 310 let private = unsafe { (*file).private_data }; 311 // SAFETY: This is a Rust Miscdevice, so we call `into_foreign` in `open` and 312 // `from_foreign` in `release`, and `fops_mmap` is guaranteed to be called between those 313 // two operations. 314 let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private.cast()) }; 315 // SAFETY: The caller provides a vma that is undergoing initial VMA setup. 316 let area = unsafe { VmaNew::from_raw(vma) }; 317 // SAFETY: 318 // * The file is valid for the duration of this call. 319 // * There is no active fdget_pos region on the file on this thread. 320 let file = unsafe { File::from_raw_file(file) }; 321 322 match T::mmap(device, file, area) { 323 Ok(()) => 0, 324 Err(err) => err.to_errno(), 325 } 326 } 327 328 /// # Safety 329 /// 330 /// `file` must be a valid file that is associated with a `MiscDeviceRegistration<T>`. 331 unsafe extern "C" fn ioctl(file: *mut bindings::file, cmd: c_uint, arg: c_ulong) -> c_long { 332 // SAFETY: The ioctl call of a file can access the private data. 333 let private = unsafe { (*file).private_data }; 334 // SAFETY: Ioctl calls can borrow the private data of the file. 335 let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) }; 336 337 // SAFETY: 338 // * The file is valid for the duration of this call. 339 // * There is no active fdget_pos region on the file on this thread. 340 let file = unsafe { File::from_raw_file(file) }; 341 342 match T::ioctl(device, file, cmd, arg) { 343 Ok(ret) => ret as c_long, 344 Err(err) => err.to_errno() as c_long, 345 } 346 } 347 348 /// # Safety 349 /// 350 /// `file` must be a valid file that is associated with a `MiscDeviceRegistration<T>`. 351 #[cfg(CONFIG_COMPAT)] 352 unsafe extern "C" fn compat_ioctl( 353 file: *mut bindings::file, 354 cmd: c_uint, 355 arg: c_ulong, 356 ) -> c_long { 357 // SAFETY: The compat ioctl call of a file can access the private data. 358 let private = unsafe { (*file).private_data }; 359 // SAFETY: Ioctl calls can borrow the private data of the file. 360 let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) }; 361 362 // SAFETY: 363 // * The file is valid for the duration of this call. 364 // * There is no active fdget_pos region on the file on this thread. 365 let file = unsafe { File::from_raw_file(file) }; 366 367 match T::compat_ioctl(device, file, cmd, arg) { 368 Ok(ret) => ret as c_long, 369 Err(err) => err.to_errno() as c_long, 370 } 371 } 372 373 /// # Safety 374 /// 375 /// - `file` must be a valid file that is associated with a `MiscDeviceRegistration<T>`. 376 /// - `seq_file` must be a valid `struct seq_file` that we can write to. 377 unsafe extern "C" fn show_fdinfo(seq_file: *mut bindings::seq_file, file: *mut bindings::file) { 378 // SAFETY: The release call of a file owns the private data. 379 let private = unsafe { (*file).private_data }; 380 // SAFETY: Ioctl calls can borrow the private data of the file. 381 let device = unsafe { <T::Ptr as ForeignOwnable>::borrow(private) }; 382 // SAFETY: 383 // * The file is valid for the duration of this call. 384 // * There is no active fdget_pos region on the file on this thread. 385 let file = unsafe { File::from_raw_file(file) }; 386 // SAFETY: The caller ensures that the pointer is valid and exclusive for the duration in 387 // which this method is called. 388 let m = unsafe { SeqFile::from_raw(seq_file) }; 389 390 T::show_fdinfo(device, m, file); 391 } 392 393 const VTABLE: bindings::file_operations = bindings::file_operations { 394 open: Some(Self::open), 395 release: Some(Self::release), 396 mmap: if T::HAS_MMAP { Some(Self::mmap) } else { None }, 397 read_iter: if T::HAS_READ_ITER { 398 Some(Self::read_iter) 399 } else { 400 None 401 }, 402 write_iter: if T::HAS_WRITE_ITER { 403 Some(Self::write_iter) 404 } else { 405 None 406 }, 407 unlocked_ioctl: if T::HAS_IOCTL { 408 Some(Self::ioctl) 409 } else { 410 None 411 }, 412 #[cfg(CONFIG_COMPAT)] 413 compat_ioctl: if T::HAS_COMPAT_IOCTL { 414 Some(Self::compat_ioctl) 415 } else if T::HAS_IOCTL { 416 bindings::compat_ptr_ioctl 417 } else { 418 None 419 }, 420 show_fdinfo: if T::HAS_SHOW_FDINFO { 421 Some(Self::show_fdinfo) 422 } else { 423 None 424 }, 425 owner: this_module::<T::OwnerModule>().as_ptr(), 426 ..pin_init::zeroed() 427 }; 428 429 const fn build() -> &'static bindings::file_operations { 430 &Self::VTABLE 431 } 432 } 433