1 // SPDX-License-Identifier: GPL-2.0
2
3 // Copyright (C) 2024 Google LLC.
4
5 //! Provides knobs for Rust ashmem.
6
7 use crate::{ashmem_range, IGNORE_UNSET_PROT_EXEC, IGNORE_UNSET_PROT_READ};
8 use core::{marker::PhantomData, sync::atomic::Ordering};
9 use kernel::{
10 c_str,
11 error::to_result,
12 fs::File,
13 miscdevice::{IovIter, Kiocb, MiscDevice, MiscDeviceOptions, MiscDeviceRegistration},
14 prelude::*,
15 };
16
kstrtobool(kstr: &CStr) -> Result<bool>17 fn kstrtobool(kstr: &CStr) -> Result<bool> {
18 let mut res = false;
19 to_result(unsafe { kernel::bindings::kstrtobool(kstr.as_char_ptr(), &mut res) })?;
20 Ok(res)
21 }
22
23 pub(crate) trait AshmemToggle {
24 const NAME: &'static CStr;
set(enabled: bool) -> Result<()>25 fn set(enabled: bool) -> Result<()>;
get() -> bool26 fn get() -> bool;
27 }
28
29 pub(crate) struct AshmemToggleMisc<T>(PhantomData<T>);
30
31 impl<T: AshmemToggle> AshmemToggleMisc<T> {
new() -> Result<Pin<KBox<MiscDeviceRegistration<AshmemToggleMisc<T>>>>>32 pub(crate) fn new() -> Result<Pin<KBox<MiscDeviceRegistration<AshmemToggleMisc<T>>>>> {
33 KBox::pin_init(
34 MiscDeviceRegistration::register(MiscDeviceOptions { name: T::NAME }),
35 GFP_KERNEL,
36 )
37 }
38 }
39
40 #[vtable]
41 impl<T: AshmemToggle> MiscDevice for AshmemToggleMisc<T> {
42 type Ptr = ();
open(_: &File, _: &MiscDeviceRegistration<Self>) -> Result<()>43 fn open(_: &File, _: &MiscDeviceRegistration<Self>) -> Result<()> {
44 Ok(())
45 }
read_iter(mut kiocb: Kiocb<'_, Self::Ptr>, iov: &mut IovIter) -> Result<usize>46 fn read_iter(mut kiocb: Kiocb<'_, Self::Ptr>, iov: &mut IovIter) -> Result<usize> {
47 if kiocb.ki_pos() != 0 {
48 return Ok(0);
49 }
50
51 let data = match T::get() {
52 false => b"0\n",
53 true => b"1\n",
54 };
55
56 // You better give me a buffer with space for at least two bytes.
57 iov.copy_to_iter(data)?;
58 *kiocb.ki_pos_mut() = 2;
59 Ok(2)
60 }
write_iter(_kiocb: Kiocb<'_, Self::Ptr>, iov: &mut IovIter) -> Result<usize>61 fn write_iter(_kiocb: Kiocb<'_, Self::Ptr>, iov: &mut IovIter) -> Result<usize> {
62 let mut data = [0; 16];
63 let len = iov.copy_from_iter(&mut data[..15]);
64 data[len] = 0;
65 let data = CStr::from_bytes_with_nul(&data[..len + 1])?;
66 T::set(kstrtobool(data)?)?;
67 Ok(len)
68 }
69 }
70
71 pub(crate) struct AshmemToggleShrinker;
72
73 impl AshmemToggle for AshmemToggleShrinker {
74 const NAME: &'static CStr = c_str!("ashmem_unpinning_enable");
set(enabled: bool) -> Result<()>75 fn set(enabled: bool) -> Result<()> {
76 ashmem_range::set_shrinker_enabled(enabled)
77 }
get() -> bool78 fn get() -> bool {
79 ashmem_range::get_shrinker_enabled()
80 }
81 }
82
83 pub(crate) struct AshmemToggleRead;
84
85 impl AshmemToggle for AshmemToggleRead {
86 const NAME: &'static CStr = c_str!("ashmem_ignore_unset_prot_read");
set(enabled: bool) -> Result<()>87 fn set(enabled: bool) -> Result<()> {
88 IGNORE_UNSET_PROT_READ.store(enabled, Ordering::Relaxed);
89 Ok(())
90 }
get() -> bool91 fn get() -> bool {
92 IGNORE_UNSET_PROT_READ.load(Ordering::Relaxed)
93 }
94 }
95
96 pub(crate) struct AshmemToggleExec;
97
98 impl AshmemToggle for AshmemToggleExec {
99 const NAME: &'static CStr = c_str!("ashmem_ignore_unset_prot_exec");
set(enabled: bool) -> Result<()>100 fn set(enabled: bool) -> Result<()> {
101 IGNORE_UNSET_PROT_EXEC.store(enabled, Ordering::Relaxed);
102 Ok(())
103 }
get() -> bool104 fn get() -> bool {
105 IGNORE_UNSET_PROT_EXEC.load(Ordering::Relaxed)
106 }
107 }
108