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