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,
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, FileOpsCore, LockDepMutex, Locked, Unlocked};
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::user_address::{UserAddress, UserCString, UserRef};
30use starnix_uapi::{ASHMEM_NAME_LEN, ashmem_pin, device_id, errno, error, off_t, uapi};
31use std::sync::Arc;
32
33pub fn ashmem_device_init(locked: &mut Locked<Unlocked>, kernel: &Kernel) {
35 let registry = &kernel.device_registry;
36
37 registry
38 .register_misc_device(locked, kernel, "ashmem".into(), AshmemDevice::new())
39 .expect("can register ashmem");
40}
41
42#[derive(Clone)]
43pub struct AshmemDevice {
44 pub next_id: Arc<AtomicCounter<u32>>,
45}
46
47pub struct Ashmem {
48 memory: OnceCell<Arc<MemoryObject>>,
49 state: LockDepMutex<AshmemState, AshmemStateLock>,
50}
51
52struct AshmemState {
53 size: usize,
54 name: FsString,
55 prot_flags: ProtectionFlags,
56 unpinned: RangeMap<u32, bool>,
57 id: u32,
58}
59
60impl AshmemDevice {
61 pub fn new() -> AshmemDevice {
62 AshmemDevice { next_id: Arc::new(AtomicCounter::new(1)) }
63 }
64}
65
66impl DeviceOps for AshmemDevice {
67 fn open(
68 &self,
69 _locked: &mut Locked<FileOpsCore>,
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 _locked: &mut Locked<FileOpsCore>,
112 _file: &FileObject,
113 _current_task: &CurrentTask,
114 current_offset: off_t,
115 target: SeekTarget,
116 ) -> Result<off_t, Errno> {
117 if !self.is_mapped() {
118 return error!(EBADF);
119 }
120 let eof_offset = self.state.lock().size;
121 default_seek(current_offset, target, || Ok(eof_offset.try_into().unwrap()))
122 }
123
124 fn read(
125 &self,
126 _locked: &mut starnix_sync::Locked<FileOpsCore>,
127 _file: &FileObject,
128 _current_task: &CurrentTask,
129 offset: usize,
130 data: &mut dyn OutputBuffer,
131 ) -> Result<usize, Errno> {
132 let memory = self.memory().map_err(|_| errno!(EBADF))?;
133 let file_length = self.state.lock().size;
134 let actual = {
135 let want_read = data.available();
136 if offset < file_length {
137 let to_read =
138 if file_length < offset + want_read { file_length - offset } else { want_read };
139 let buf =
140 memory.read_to_vec(offset as u64, to_read as u64).map_err(|_| errno!(EIO))?;
141 data.write_all(&buf[..])?;
142 to_read
143 } else {
144 0
145 }
146 };
147 Ok(actual)
148 }
149
150 fn write(
151 &self,
152 _locked: &mut Locked<FileOpsCore>,
153 _file: &FileObject,
154 _current_task: &CurrentTask,
155 _offset: usize,
156 _data: &mut dyn InputBuffer,
157 ) -> Result<usize, Errno> {
158 error!(EINVAL)
159 }
160
161 fn mmap(
162 &self,
163 _locked: &mut Locked<FileOpsCore>,
164 file: &FileObject,
165 current_task: &CurrentTask,
166 addr: DesiredAddress,
167 memory_offset: u64,
168 length: usize,
169 prot_flags: ProtectionFlags,
170 mapping_options: MappingOptions,
171 _filename: NamespaceNode,
172 ) -> Result<UserAddress, Errno> {
173 let state = self.state.lock();
174 let size_paged_aligned = round_up_to_increment(state.size, *PAGE_SIZE as usize)?;
175
176 if !state.prot_flags.contains(prot_flags) {
178 return error!(EINVAL);
179 }
180 if size_paged_aligned < length {
182 return error!(EINVAL);
183 }
184
185 let memory = self
186 .memory
187 .get_or_try_init(|| {
188 if size_paged_aligned == 0 {
189 return error!(EINVAL);
190 }
191 let vmo = zx::Vmo::create(size_paged_aligned as u64).map_err(|_| errno!(ENOMEM))?;
193 let memory = MemoryObject::from(vmo).with_zx_name(b"starnix:ashmem");
194 Ok(Arc::new(memory))
195 })?
196 .clone();
197
198 let mapped_addr = current_task.mm()?.map_memory(
199 addr,
200 memory,
201 memory_offset,
202 length,
203 prot_flags,
204 file.max_access_for_memory_mapping(),
205 mapping_options,
206 MappingName::Ashmem(state.name.clone().into()),
207 )?;
208
209 Ok(mapped_addr)
210 }
211
212 fn ioctl(
213 &self,
214 _locked: &mut Locked<Unlocked>,
215 _file: &FileObject,
216 current_task: &CurrentTask,
217 request: u32,
218 arg: SyscallArg,
219 ) -> Result<SyscallResult, Errno> {
220 match request {
221 #[allow(unreachable_patterns)]
222 ASHMEM_SET_SIZE | starnix_uapi::arch32::ASHMEM_SET_SIZE => {
223 let mut state = self.state.lock();
224
225 if self.is_mapped() {
226 return error!(EINVAL);
227 }
228 state.size = arg.into();
229 Ok(SUCCESS)
230 }
231 ASHMEM_GET_SIZE => Ok(self.state.lock().size.into()),
232 ASHMEM_SET_NAME => {
233 let mut state = self.state.lock();
234
235 if self.is_mapped() {
236 return error!(EINVAL);
237 }
238 let mut name = current_task.read_c_string_to_vec(
239 UserCString::new(current_task, arg),
240 ASHMEM_NAME_LEN as usize,
241 )?;
242 name.push(0); state.name = name.into();
245 Ok(SUCCESS)
246 }
247 ASHMEM_GET_NAME => {
248 let state = self.state.lock();
249 let name = &state.name[..];
250
251 current_task.write_memory(arg.into(), name)?;
252 Ok(SUCCESS)
253 }
254 #[allow(unreachable_patterns)]
255 ASHMEM_SET_PROT_MASK | starnix_uapi::arch32::ASHMEM_SET_PROT_MASK => {
256 let mut state = self.state.lock();
257 let prot_flags =
258 ProtectionFlags::from_access_bits(arg.into()).ok_or_else(|| errno!(EINVAL))?;
259
260 if !state.prot_flags.contains(prot_flags) {
262 return error!(EINVAL);
263 }
264
265 state.prot_flags = prot_flags;
266 Ok(SUCCESS)
267 }
268 ASHMEM_GET_PROT_MASK => Ok(self.state.lock().prot_flags.bits().into()),
269 ASHMEM_PIN | ASHMEM_UNPIN | ASHMEM_GET_PIN_STATUS => {
270 let mut state = self.state.lock();
271
272 if !self.is_mapped() {
273 return error!(EINVAL);
274 }
275
276 let user_ref = UserRef::<ashmem_pin>::new(arg.into());
277 let pin = current_task.read_object(user_ref)?;
278 let (lo, hi) =
279 (pin.offset, pin.offset.checked_add(pin.len).ok_or_else(|| errno!(EFAULT))?);
280
281 if (lo as usize) >= state.size || (hi as usize) > state.size {
283 return error!(EINVAL);
284 }
285
286 if (lo as u64) % *PAGE_SIZE != 0 || (hi as u64) % *PAGE_SIZE != 0 {
288 return error!(EINVAL);
289 }
290
291 match request {
292 ASHMEM_PIN => {
293 for is_purged in state.unpinned.remove(lo..hi).iter() {
294 if *is_purged {
295 return Ok(ASHMEM_WAS_PURGED.into());
296 }
297 }
298
299 return Ok(ASHMEM_NOT_PURGED.into());
300 }
301 ASHMEM_UNPIN => {
302 let _ = state.unpinned.insert(lo..hi, false);
305 return Ok(ASHMEM_IS_UNPINNED.into());
306 }
307 ASHMEM_GET_PIN_STATUS => {
308 let mut intervals = state.unpinned.range(lo..hi);
309 return match intervals.next() {
310 Some(_) => Ok(ASHMEM_IS_UNPINNED.into()),
311 None => Ok(ASHMEM_IS_PINNED.into()),
312 };
313 }
314 _ => unreachable!(),
315 }
316 }
317 ASHMEM_PURGE_ALL_CACHES => {
318 let mut state = self.state.lock();
319 let memory = self.memory.get().ok_or_else(|| errno!(EINVAL))?;
320
321 if state.unpinned.is_empty() {
322 return Ok(ASHMEM_IS_PINNED.into());
323 }
324 let unpinned: Vec<_> = state.unpinned.iter().map(|(k, _)| k.clone()).collect();
325 for range in unpinned.into_iter() {
326 let (lo, hi) = (range.start as u64, range.end as u64);
327 memory.op_range(zx::VmoOp::ZERO, lo, hi - lo).unwrap_or(());
328
329 let _ = state.unpinned.insert(range, true);
332 }
333 return Ok(ASHMEM_IS_UNPINNED.into());
334 }
335 #[allow(unreachable_patterns)]
336 uapi::ASHMEM_GET_FILE_ID | uapi::arch32::ASHMEM_GET_FILE_ID => {
337 let state = self.state.lock();
338 current_task.write_object(arg.into(), &(state.id))?;
339 Ok(SUCCESS)
340 }
341 _ => error!(ENOTTY),
342 }
343 }
344}