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