diff --git a/drivers/staging/android/ashmem_rust.rs b/drivers/staging/android/ashmem_rust.rs index 77de00d9e26a..40b46bf44a72 100644 --- a/drivers/staging/android/ashmem_rust.rs +++ b/drivers/staging/android/ashmem_rust.rs @@ -12,13 +12,18 @@ use core::pin::Pin; use kernel::{ - c_str, + bindings, c_str, error::Result, fs::File, + ioctl::_IOC_SIZE, miscdevice::{MiscDevice, MiscDeviceOptions, MiscDeviceRegistration}, prelude::*, + sync::{new_mutex, Mutex}, + uaccess::{UserSlice, UserSliceReader, UserSliceWriter}, }; +const ASHMEM_NAME_LEN: usize = bindings::ASHMEM_NAME_LEN as usize; + module! { type: AshmemModule, name: "ashmem_rust", @@ -48,13 +53,81 @@ impl kernel::Module for AshmemModule { /// Represents an open ashmem file. #[pin_data] -struct Ashmem {} +struct Ashmem { + #[pin] + inner: Mutex, +} + +struct AshmemInner { + /// If set, then this holds the ashmem name without the dev/ashmem/ prefix. No zero terminator. + name: Option>, +} #[vtable] impl MiscDevice for Ashmem { type Ptr = Pin>; fn open(_: &File, _: &MiscDeviceRegistration) -> Result>> { - Box::try_pin_init(Ashmem {}, GFP_KERNEL) + Box::try_pin_init( + try_pin_init! { + Ashmem { + inner <- new_mutex!(AshmemInner { + name: None, + }), + } + }, + GFP_KERNEL, + ) + } + + fn ioctl(me: Pin<&Ashmem>, _file: &File, cmd: u32, arg: usize) -> Result { + let size = _IOC_SIZE(cmd); + match cmd { + bindings::ASHMEM_SET_NAME => me.set_name(UserSlice::new(arg, size).reader()), + bindings::ASHMEM_GET_NAME => me.get_name(UserSlice::new(arg, size).writer()), + _ => Err(EINVAL), + } + } +} + +impl Ashmem { + fn set_name(&self, mut reader: UserSliceReader) -> Result { + let mut local_name = [0u8; ASHMEM_NAME_LEN]; + reader.read_slice(&mut local_name)?; + + // Find the zero terminator. If the zero terminator is missing, the string is truncated to + // `ASHMEM_NAME_LEN-1` so that `get_name` can return it and has enough space to add a zero + // terminator. + let len = local_name + .iter() + .position(|&c| c == 0) + .unwrap_or(local_name.len() - 1); + + let mut v = Vec::with_capacity(len, GFP_KERNEL)?; + v.extend_from_slice(&local_name[..len], GFP_KERNEL)?; + + let mut asma = self.inner.lock(); + // TODO: fail if `mmap` is already called + asma.name = Some(v); + Ok(0) + } + + fn get_name(&self, mut writer: UserSliceWriter) -> Result { + let mut local_name = [0u8; ASHMEM_NAME_LEN]; + let asma = self.inner.lock(); + let name = asma.name.as_deref().unwrap_or(b"dev/ashmem"); + let len = name.len(); + let len_with_nul = len + 1; + if local_name.len() <= len_with_nul { + // This shouldn't happen in practice since `set_name` will refuse to store a string + // that is too long. + return Err(EINVAL); + } + local_name[..len].copy_from_slice(name); + local_name[len_with_nul] = 0; + drop(asma); + + writer.write_slice(&local_name[..len_with_nul])?; + Ok(0) } } diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 2675215ec32e..cc0387de3b72 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -45,3 +45,7 @@ const gfp_t RUST_CONST_HELPER_GFP_NOWAIT = GFP_NOWAIT; const gfp_t RUST_CONST_HELPER___GFP_ZERO = __GFP_ZERO; const gfp_t RUST_CONST_HELPER___GFP_HIGHMEM = ___GFP_HIGHMEM; const blk_features_t RUST_CONST_HELPER_BLK_FEAT_ROTATIONAL = BLK_FEAT_ROTATIONAL; + +#ifdef CONFIG_ASHMEM_RUST +#include "../../drivers/staging/android/ashmem.h" +#endif