From 977fca65556c5665930e038cb83651ea4130d809 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Tue, 3 Dec 2024 13:12:34 +0000 Subject: [PATCH] ANDROID: rust: miscdevice: add llseek/read_iter Support for these methods has not been sent upstream. Bug: 370906207 Change-Id: Ibdcfbaf36a6b05a82ed57be5b897e723f6e7201a Signed-off-by: Alice Ryhl --- rust/kernel/miscdevice.rs | 109 +++++++++++++++++++++++++++++++++++++- 1 file changed, 108 insertions(+), 1 deletion(-) diff --git a/rust/kernel/miscdevice.rs b/rust/kernel/miscdevice.rs index 8f0e3b49a5f9..07c07aee3a90 100644 --- a/rust/kernel/miscdevice.rs +++ b/rust/kernel/miscdevice.rs @@ -12,7 +12,7 @@ use crate::{ bindings, device::Device, error::{to_result, Error, Result, VTABLE_DEFAULT_ERROR}, - fs::File, + fs::{File, LocalFile}, mm::virt::VmAreaNew, prelude::*, seq_file::SeqFile, @@ -24,8 +24,13 @@ use core::{ marker::PhantomData, mem::MaybeUninit, pin::Pin, + ptr::NonNull, }; +/// The kernel `loff_t` type. +#[allow(non_camel_case_types)] +pub type loff_t = bindings::loff_t; + /// Options for creating a misc device. #[derive(Copy, Clone)] pub struct MiscDeviceOptions { @@ -140,6 +145,21 @@ pub trait MiscDevice: Sized { kernel::build_error!(VTABLE_DEFAULT_ERROR) } + /// Seeks this miscdevice. + fn llseek( + _device: ::Borrowed<'_>, + _file: &LocalFile, + _offset: loff_t, + _whence: c_int, + ) -> Result { + kernel::build_error(VTABLE_DEFAULT_ERROR) + } + + /// Read from this miscdevice. + fn read_iter(_kiocb: Kiocb<'_, Self::Ptr>, _iov: &mut IovIter) -> Result { + kernel::build_error(VTABLE_DEFAULT_ERROR) + } + /// Handler for ioctls. /// /// The `cmd` argument is usually manipulated using the utilties in [`kernel::ioctl`]. @@ -181,6 +201,48 @@ pub trait MiscDevice: Sized { } } +/// Wrapper for the kernel's `struct kiocb`. +/// +/// The type `T` represents the private data of the file. +pub struct Kiocb<'a, T> { + inner: NonNull, + _phantom: PhantomData<&'a T>, +} + +impl<'a, T: ForeignOwnable> Kiocb<'a, T> { + /// Get the private data in this kiocb. + pub fn private_data(&self) -> ::Borrowed<'a> { + // SAFETY: The `kiocb` lets us access the private data. + let private = unsafe { (*(*self.inner.as_ptr()).ki_filp).private_data }; + // SAFETY: The kiocb has shared access to the private data. + unsafe { ::borrow(private) } + } + + /// Gets the current value of `ki_pos`. + pub fn ki_pos(&self) -> loff_t { + // SAFETY: The `kiocb` can access `ki_pos`. + unsafe { (*self.inner.as_ptr()).ki_pos } + } + + /// Gets a mutable reference to the `ki_pos` field. + pub fn ki_pos_mut(&mut self) -> &mut loff_t { + // SAFETY: The `kiocb` can access `ki_pos`. + unsafe { &mut (*self.inner.as_ptr()).ki_pos } + } +} + +/// Wrapper for the kernel's `struct iov_iter`. +pub struct IovIter { + inner: Opaque, +} + +impl IovIter { + /// Gets a raw pointer to the contents. + pub fn as_raw(&self) -> *mut bindings::iov_iter { + self.inner.get() + } +} + const fn create_vtable() -> &'static bindings::file_operations { const fn maybe_fn(check: bool, func: T) -> Option { if check { @@ -198,6 +260,8 @@ const fn create_vtable() -> &'static bindings::file_operations { open: Some(fops_open::), release: Some(fops_release::), mmap: maybe_fn(T::HAS_MMAP, fops_mmap::), + llseek: maybe_fn(T::HAS_LLSEEK, fops_llseek::), + read_iter: maybe_fn(T::HAS_READ_ITER, fops_read_iter::), unlocked_ioctl: maybe_fn(T::HAS_IOCTL, fops_ioctl::), #[cfg(CONFIG_COMPAT)] compat_ioctl: if T::HAS_COMPAT_IOCTL { @@ -305,6 +369,49 @@ unsafe extern "C" fn fops_mmap( } } +/// # Safety +/// +/// `file` must be a valid file that is associated with a `MiscDeviceRegistration`. +unsafe extern "C" fn fops_llseek( + file: *mut bindings::file, + offset: loff_t, + whence: c_int, +) -> loff_t { + // SAFETY: The release call of a file owns the private data. + let private = unsafe { (*file).private_data }; + // SAFETY: Ioctl calls can borrow the private data of the file. + let device = unsafe { ::borrow(private) }; + // SAFETY: + // * The file is valid for the duration of this call. + // * We are inside an fdget_pos region, so there cannot be any active fdget_pos regions on + // other threads. + let file = unsafe { LocalFile::from_raw_file(file) }; + + match T::llseek(device, file, offset, whence) { + Ok(res) => res as loff_t, + Err(err) => err.to_errno() as loff_t, + } +} + +/// # Safety +/// +/// Arguments must be valid. +unsafe extern "C" fn fops_read_iter( + kiocb: *mut bindings::kiocb, + iter: *mut bindings::iov_iter, +) -> isize { + let kiocb = Kiocb { + inner: unsafe { NonNull::new_unchecked(kiocb) }, + _phantom: PhantomData, + }; + let iov = unsafe { &mut *iter.cast::() }; + + match T::read_iter(kiocb, iov) { + Ok(res) => res as isize, + Err(err) => err.to_errno() as isize, + } +} + /// # Safety /// /// `file` must be a valid file that is associated with a `MiscDeviceRegistration`.