1use linux_uapi::{
6 ASHMEM_GET_NAME, ASHMEM_GET_PIN_STATUS, ASHMEM_GET_PROT_MASK, ASHMEM_GET_SIZE,
7 ASHMEM_IS_PINNED, ASHMEM_IS_UNPINNED, ASHMEM_NOT_PURGED, ASHMEM_PIN, ASHMEM_PURGE_ALL_CACHES,
8 ASHMEM_SET_NAME, ASHMEM_SET_PROT_MASK, ASHMEM_SET_SIZE, ASHMEM_UNPIN, ASHMEM_WAS_PURGED,
9};
10use once_cell::sync::OnceCell;
11use range_map::RangeMap;
12use starnix_core::device::DeviceOps;
13use starnix_core::mm::memory::MemoryObject;
14use starnix_core::mm::{
15 DesiredAddress, MappingName, MappingOptions, MemoryAccessor, MemoryAccessorExt, PAGE_SIZE,
16 ProtectionFlags, VMEX_RESOURCE,
17};
18use starnix_core::task::{CurrentTask, Kernel};
19use starnix_core::vfs::{
20 FileObject, FileOps, FsString, InputBuffer, NamespaceNode, OutputBuffer, SeekTarget,
21 default_seek, fileops_impl_noop_sync,
22};
23use starnix_lifecycle::AtomicCounter;
24use starnix_sync::{AshmemStateLock, LockDepMutex};
25use starnix_syscalls::{SUCCESS, SyscallArg, SyscallResult};
26use starnix_uapi::errors::Errno;
27use starnix_uapi::math::round_up_to_increment;
28use starnix_uapi::open_flags::OpenFlags;
29use starnix_uapi::personality::PersonalityFlags;
30use starnix_uapi::user_address::{UserAddress, UserCString, UserRef};
31use starnix_uapi::{ASHMEM_NAME_LEN, ashmem_pin, device_id, errno, error, off_t, uapi};
32use std::sync::Arc;
33
34pub fn ashmem_device_init(kernel: &Kernel) {
36 let registry = &kernel.device_registry;
37
38 registry
39 .register_misc_device(kernel, "ashmem".into(), AshmemDevice::new())
40 .expect("can register ashmem");
41}
42
43#[derive(Clone)]
44pub struct AshmemDevice {
45 pub next_id: Arc<AtomicCounter<u32>>,
46}
47
48pub struct Ashmem {
49 memory: OnceCell<Arc<MemoryObject>>,
50 state: LockDepMutex<AshmemState, AshmemStateLock>,
51}
52
53struct AshmemState {
54 size: usize,
55 name: FsString,
56 prot_flags: ProtectionFlags,
57 unpinned: RangeMap<u32, bool>,
58 id: u32,
59}
60
61impl AshmemDevice {
62 pub fn new() -> AshmemDevice {
63 AshmemDevice { next_id: Arc::new(AtomicCounter::new(1)) }
64 }
65}
66
67impl DeviceOps for AshmemDevice {
68 fn open(
69 &self,
70 _current_task: &CurrentTask,
71 _id: device_id::DeviceId,
72 _node: &NamespaceNode,
73 _flags: OpenFlags,
74 ) -> Result<Box<dyn FileOps>, Errno> {
75 let ashmem = Ashmem::new(self.next_id.next());
76 Ok(Box::new(ashmem))
77 }
78}
79
80impl Ashmem {
81 fn new(id: u32) -> Ashmem {
82 let state = AshmemState {
83 size: 0,
84 name: b"dev/ashmem\0".into(),
85 prot_flags: ProtectionFlags::ACCESS_FLAGS,
86 unpinned: RangeMap::<u32, bool>::default(),
87 id: id,
88 };
89
90 Ashmem { memory: OnceCell::new(), state: state.into() }
91 }
92
93 fn memory(&self) -> Result<&Arc<MemoryObject>, Errno> {
94 self.memory.get().ok_or_else(|| errno!(EINVAL))
95 }
96
97 fn is_mapped(&self) -> bool {
98 self.memory.get().is_some()
99 }
100}
101
102impl FileOps for Ashmem {
103 fileops_impl_noop_sync!();
104
105 fn is_seekable(&self) -> bool {
106 true
107 }
108
109 fn seek(
110 &self,
111 _file: &FileObject,
112 _current_task: &CurrentTask,
113 current_offset: off_t,
114 target: SeekTarget,
115 ) -> Result<off_t, Errno> {
116 if !self.is_mapped() {
117 return error!(EBADF);
118 }
119 let eof_offset = self.state.lock().size;
120 default_seek(current_offset, target, || Ok(eof_offset.try_into().unwrap()))
121 }
122
123 fn read(
124 &self,
125 _file: &FileObject,
126 _current_task: &CurrentTask,
127 offset: usize,
128 data: &mut dyn OutputBuffer,
129 ) -> Result<usize, Errno> {
130 let memory = self.memory().map_err(|_| errno!(EBADF))?;
131 let file_length = self.state.lock().size;
132 let actual = {
133 let want_read = data.available();
134 if offset < file_length {
135 let to_read =
136 if file_length < offset + want_read { file_length - offset } else { want_read };
137 let buf =
138 memory.read_to_vec(offset as u64, to_read as u64).map_err(|_| errno!(EIO))?;
139 data.write_all(&buf[..])?;
140 to_read
141 } else {
142 0
143 }
144 };
145 Ok(actual)
146 }
147
148 fn write(
149 &self,
150 _file: &FileObject,
151 _current_task: &CurrentTask,
152 _offset: usize,
153 _data: &mut dyn InputBuffer,
154 ) -> Result<usize, Errno> {
155 error!(EINVAL)
156 }
157
158 fn mmap(
159 &self,
160 _file: &FileObject,
161 current_task: &CurrentTask,
162 addr: DesiredAddress,
163 memory_offset: u64,
164 length: usize,
165 prot_flags: ProtectionFlags,
166 mapping_options: MappingOptions,
167 ) -> Result<UserAddress, Errno> {
168 let state = self.state.lock();
169 let size_paged_aligned = round_up_to_increment(state.size, *PAGE_SIZE as usize)?;
170
171 if !state.prot_flags.contains(prot_flags) {
173 return error!(EINVAL);
174 }
175 if size_paged_aligned < length {
177 return error!(EINVAL);
178 }
179
180 let memory = self.memory.get_or_try_init(|| {
181 if size_paged_aligned == 0 {
182 return error!(EINVAL);
183 }
184 let vmo = zx::Vmo::create(size_paged_aligned as u64)
186 .map_err(|_| errno!(ENOMEM))?
187 .replace_as_executable(&VMEX_RESOURCE)
190 .map_err(|_| errno!(ENOMEM))?;
191 let memory = MemoryObject::from(vmo).with_zx_name(b"starnix:ashmem");
192 Ok(Arc::new(memory))
193 })?;
194 let memory = if state.prot_flags.contains(ProtectionFlags::EXEC) {
197 memory.clone()
198 } else {
199 let rights = memory.get_rights() - zx::Rights::EXECUTE;
200 Arc::new(memory.duplicate_handle(rights).map_err(|_| errno!(ENOMEM))?)
201 };
202
203 let mapped_addr = current_task.mm()?.map_memory(
204 addr,
205 memory,
206 memory_offset,
207 length,
208 prot_flags,
209 mapping_options,
210 MappingName::Ashmem(state.name.clone().into()),
211 )?;
212
213 Ok(mapped_addr)
214 }
215
216 fn ioctl(
217 &self,
218 _file: &FileObject,
219 current_task: &CurrentTask,
220 request: u32,
221 arg: SyscallArg,
222 ) -> Result<SyscallResult, Errno> {
223 match request {
224 #[allow(unreachable_patterns)]
225 ASHMEM_SET_SIZE | starnix_uapi::arch32::ASHMEM_SET_SIZE => {
226 let mut state = self.state.lock();
227
228 if self.is_mapped() {
229 return error!(EINVAL);
230 }
231 state.size = arg.into();
232 Ok(SUCCESS)
233 }
234 ASHMEM_GET_SIZE => Ok(self.state.lock().size.into()),
235 ASHMEM_SET_NAME => {
236 let mut state = self.state.lock();
237
238 if self.is_mapped() {
239 return error!(EINVAL);
240 }
241 let mut name = current_task.read_c_string_to_vec(
242 UserCString::new(current_task, arg),
243 ASHMEM_NAME_LEN as usize,
244 )?;
245 name.push(0); state.name = name.into();
248 Ok(SUCCESS)
249 }
250 ASHMEM_GET_NAME => {
251 let state = self.state.lock();
252 let name = &state.name[..];
253
254 current_task.write_memory(arg.into(), name)?;
255 Ok(SUCCESS)
256 }
257 #[allow(unreachable_patterns)]
258 ASHMEM_SET_PROT_MASK | starnix_uapi::arch32::ASHMEM_SET_PROT_MASK => {
259 let mut prot_flags =
260 ProtectionFlags::from_access_bits(arg.into()).ok_or_else(|| errno!(EINVAL))?;
261 let read_implies_exec = current_task
262 .thread_group()
263 .read()
264 .personality
265 .contains(PersonalityFlags::READ_IMPLIES_EXEC);
266 let mut state = self.state.lock();
267
268 if !state.prot_flags.contains(prot_flags) {
270 return error!(EINVAL);
271 }
272
273 if read_implies_exec && prot_flags.contains(ProtectionFlags::READ) {
277 prot_flags |= ProtectionFlags::EXEC;
278 }
279
280 state.prot_flags = prot_flags;
281 Ok(SUCCESS)
282 }
283 ASHMEM_GET_PROT_MASK => Ok(self.state.lock().prot_flags.bits().into()),
284 ASHMEM_PIN | ASHMEM_UNPIN | ASHMEM_GET_PIN_STATUS => {
285 let mut state = self.state.lock();
286
287 if !self.is_mapped() {
288 return error!(EINVAL);
289 }
290
291 let user_ref = UserRef::<ashmem_pin>::new(arg.into());
292 let pin = current_task.read_object(user_ref)?;
293 let (lo, hi) =
294 (pin.offset, pin.offset.checked_add(pin.len).ok_or_else(|| errno!(EFAULT))?);
295
296 if (lo as usize) >= state.size || (hi as usize) > state.size {
298 return error!(EINVAL);
299 }
300
301 if (lo as u64) % *PAGE_SIZE != 0 || (hi as u64) % *PAGE_SIZE != 0 {
303 return error!(EINVAL);
304 }
305
306 match request {
307 ASHMEM_PIN => {
308 for is_purged in state.unpinned.remove(lo..hi).iter() {
309 if *is_purged {
310 return Ok(ASHMEM_WAS_PURGED.into());
311 }
312 }
313
314 return Ok(ASHMEM_NOT_PURGED.into());
315 }
316 ASHMEM_UNPIN => {
317 let _ = state.unpinned.insert(lo..hi, false);
320 return Ok(ASHMEM_IS_UNPINNED.into());
321 }
322 ASHMEM_GET_PIN_STATUS => {
323 let mut intervals = state.unpinned.range(lo..hi);
324 return match intervals.next() {
325 Some(_) => Ok(ASHMEM_IS_UNPINNED.into()),
326 None => Ok(ASHMEM_IS_PINNED.into()),
327 };
328 }
329 _ => unreachable!(),
330 }
331 }
332 ASHMEM_PURGE_ALL_CACHES => {
333 let mut state = self.state.lock();
334 let memory = self.memory.get().ok_or_else(|| errno!(EINVAL))?;
335
336 if state.unpinned.is_empty() {
337 return Ok(ASHMEM_IS_PINNED.into());
338 }
339 let unpinned: Vec<_> = state.unpinned.iter().map(|(k, _)| k.clone()).collect();
340 for range in unpinned.into_iter() {
341 let (lo, hi) = (range.start as u64, range.end as u64);
342 memory.op_range(zx::VmoOp::ZERO, lo, hi - lo).unwrap_or(());
343
344 let _ = state.unpinned.insert(range, true);
347 }
348 return Ok(ASHMEM_IS_UNPINNED.into());
349 }
350 #[allow(unreachable_patterns)]
351 uapi::ASHMEM_GET_FILE_ID | uapi::arch32::ASHMEM_GET_FILE_ID => {
352 let state = self.state.lock();
353 current_task.write_object(arg.into(), &(state.id))?;
354 Ok(SUCCESS)
355 }
356 _ => error!(ENOTTY),
357 }
358 }
359}