diff --git a/zircon-object/src/task/job.rs b/zircon-object/src/task/job.rs index 4afd8249..318ed24c 100644 --- a/zircon-object/src/task/job.rs +++ b/zircon-object/src/task/job.rs @@ -184,6 +184,22 @@ impl Job { pub fn get_exceptionate(&self) -> Arc { self.exceptionate.clone() } + + pub fn enumerate_process(&self, mut f: impl FnMut(KoID) -> bool) { + self.inner + .lock() + .processes + .iter() + .find(|child| !f(child.id())); + } + + pub fn enumerate_children(&self, mut f: impl FnMut(KoID) -> bool) { + self.inner + .lock() + .children + .iter() + .find(|child| !f(child.id())); + } } impl JobInner { diff --git a/zircon-object/src/task/process.rs b/zircon-object/src/task/process.rs index 906d0bad..5ca0063e 100644 --- a/zircon-object/src/task/process.rs +++ b/zircon-object/src/task/process.rs @@ -310,6 +310,14 @@ impl Process { Ok(handle.object) } + pub fn get_dyn_object_and_rights( + &self, + handle_value: HandleValue, + ) -> ZxResult<(Arc, Rights)> { + let handle = self.get_handle(handle_value)?; + Ok((handle.object, handle.rights)) + } + /// Get the kernel object corresponding to this `handle_value` pub fn get_object(&self, handle_value: HandleValue) -> ZxResult> { let handle = self.get_handle(handle_value)?; @@ -402,6 +410,14 @@ impl Process { pub fn get_exceptionate(&self) -> Arc { self.exceptionate.clone() } + + pub fn enumerate_thread(&self, mut f: impl FnMut(KoID) -> bool) { + self.inner + .lock() + .threads + .iter() + .find(|child| !f(child.id())); + } } impl ProcessInner { diff --git a/zircon-object/src/vm/vmar.rs b/zircon-object/src/vm/vmar.rs index 55914eba..e045c4f7 100644 --- a/zircon-object/src/vm/vmar.rs +++ b/zircon-object/src/vm/vmar.rs @@ -1,7 +1,7 @@ use core::sync::atomic::*; use { - super::*, crate::object::*, alloc::sync::Arc, alloc::vec::Vec, bitflags::bitflags, - kernel_hal::PageTable, spin::Mutex, + super::*, crate::object::*, alloc::collections::VecDeque, alloc::sync::Arc, alloc::vec::Vec, + bitflags::bitflags, kernel_hal::PageTable, spin::Mutex, }; bitflags! { @@ -449,6 +449,31 @@ impl VmAddressRegion { Err(ZxError::NOT_FOUND) } + pub fn get_task_stats(&self) -> ZxInfoTaskStats { + let mut task_stats = ZxInfoTaskStats::default(); + let mut list = VecDeque::new(); + self.inner + .lock() + .as_ref() + .unwrap() + .children + .iter() + .for_each(|child| { + list.push_back(child.clone()); + }); + while let Some(vmar) = list.pop_front() { + let vmar_inner = vmar.inner.lock(); + let inner = vmar_inner.as_ref().unwrap(); + inner.children.iter().for_each(|child| { + list.push_back(child.clone()); + }); + inner.mappings.iter().for_each(|map| { + map.fill_in_task_status(&mut task_stats); + }); + } + task_stats + } + #[cfg(test)] fn count(&self) -> usize { let mut guard = self.inner.lock(); @@ -487,6 +512,15 @@ struct VmMappingInner { vmo_offset: usize, } +#[repr(C)] +#[derive(Default)] +pub struct ZxInfoTaskStats { + mapped_bytes: u64, + private_bytes: u64, + shared_bytes: u64, + scaled_shared_bytes: u64, +} + impl core::fmt::Debug for VmMapping { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { let inner = self.inner.lock(); @@ -548,6 +582,21 @@ impl VmMapping { .unmap_from(&mut page_table, inner.addr, inner.vmo_offset, inner.size); } + fn fill_in_task_status(&self, task_stats: &mut ZxInfoTaskStats) { + let inner = self.inner.lock(); + let start_idx = inner.vmo_offset / PAGE_SIZE; + let end_idx = start_idx + inner.size / PAGE_SIZE; + task_stats.mapped_bytes += self.vmo.len() as u64; + let committed_pages = self.vmo.committed_pages_in_range(start_idx, end_idx); + let share_count = self.vmo.share_count(); + if share_count == 1 { + task_stats.private_bytes += (committed_pages * PAGE_SIZE) as u64; + } else { + task_stats.shared_bytes += (committed_pages * PAGE_SIZE) as u64; + task_stats.scaled_shared_bytes += (committed_pages * PAGE_SIZE / share_count) as u64; + } + } + /// Cut and unmap regions in `[begin, end)`. /// /// If it will be split, return another one. diff --git a/zircon-object/src/vm/vmo/mod.rs b/zircon-object/src/vm/vmo/mod.rs index 184a0e60..969e31c7 100644 --- a/zircon-object/src/vm/vmo/mod.rs +++ b/zircon-object/src/vm/vmo/mod.rs @@ -67,6 +67,10 @@ pub trait VMObjectTrait: Sync + Send { fn get_cache_policy(&self) -> CachePolicy; fn set_cache_policy(&self, policy: CachePolicy) -> ZxResult; + + fn share_count(&self) -> usize; + + fn committed_pages_in_range(&self, start_idx: usize, end_idx: usize) -> usize; } pub struct VmObject { diff --git a/zircon-object/src/vm/vmo/paged.rs b/zircon-object/src/vm/vmo/paged.rs index 2e022bff..8f9b9ce4 100644 --- a/zircon-object/src/vm/vmo/paged.rs +++ b/zircon-object/src/vm/vmo/paged.rs @@ -316,6 +316,16 @@ impl VMObjectTrait for VMObjectPaged { // fn is_contiguous(&self) -> bool { // false // } + + fn committed_pages_in_range(&self, start_idx: usize, end_idx: usize) -> usize { + self.inner + .lock() + .committed_pages_in_range(start_idx, end_idx) + } + + fn share_count(&self) -> usize { + self.inner.lock().mappings.len() + } } enum CommitResult { @@ -522,9 +532,21 @@ impl VMObjectPagedInner { } /// Count committed pages of the VMO. - fn committed_pages(&self) -> usize { + fn committed_pages_in_range(&self, start_idx: usize, end_idx: usize) -> usize { + assert!( + start_idx < self.size / PAGE_SIZE, + "start_idx {:#x}, self.size {:#x}", + start_idx, + self.size + ); + assert!( + end_idx <= self.size / PAGE_SIZE, + "end_idx {:#x}, self.size {:#x}", + end_idx, + self.size + ); let mut count = 0; - for i in 0..self.size / PAGE_SIZE { + for i in start_idx..end_idx { if self.frames.contains_key(&i) { count += 1; continue; @@ -658,7 +680,8 @@ impl VMObjectPagedInner { info.num_children = if self.type_.is_hidden() { 2 } else { 0 }; info.num_mappings = self.mappings.len() as u64; // FIXME remove weak ptr info.share_count = self.mappings.len() as u64; // FIXME share_count should be the count of unique aspace - info.committed_bytes = (self.committed_pages() * PAGE_SIZE) as u64; + info.committed_bytes = + (self.committed_pages_in_range(0, self.size / PAGE_SIZE) * PAGE_SIZE) as u64; // TODO cache_policy should be set up. } diff --git a/zircon-object/src/vm/vmo/physical.rs b/zircon-object/src/vm/vmo/physical.rs index a0c7d894..add956de 100644 --- a/zircon-object/src/vm/vmo/physical.rs +++ b/zircon-object/src/vm/vmo/physical.rs @@ -120,6 +120,14 @@ impl VMObjectTrait for VMObjectPhysical { Ok(()) } } + + fn share_count(&self) -> usize { + unimplemented!() + } + + fn committed_pages_in_range(&self, _start_idx: usize, _end_idx: usize) -> usize { + unimplemented!() + } } #[cfg(test)] diff --git a/zircon-syscall/src/lib.rs b/zircon-syscall/src/lib.rs index 0f45a4df..59487cc0 100644 --- a/zircon-syscall/src/lib.rs +++ b/zircon-syscall/src/lib.rs @@ -236,6 +236,9 @@ impl Syscall<'_> { let _ = self.sys_handle_close(a3 as _); self.sys_thread_exit() }), + Sys::OBJECT_GET_CHILD => { + self.sys_object_get_child(a0 as _, a1 as _, a2 as _, a3.into()) + } _ => { error!("syscall unimplemented: {:?}", sys_type); Err(ZxError::NOT_SUPPORTED) diff --git a/zircon-syscall/src/object.rs b/zircon-syscall/src/object.rs index 004b7d06..acd4be54 100644 --- a/zircon-syscall/src/object.rs +++ b/zircon-syscall/src/object.rs @@ -168,14 +168,15 @@ impl Syscall<'_> { Ok(()) } + #[allow(unsafe_code)] pub fn sys_object_get_info( &self, handle: HandleValue, topic: u32, buffer: usize, buffer_size: usize, - _actual: UserOutPtr, - _avail: UserOutPtr, + mut actual: UserOutPtr, + mut avail: UserOutPtr, ) -> ZxResult { let topic = Topic::try_from(topic).map_err(|_| ZxError::INVALID_ARGS)?; info!( @@ -225,8 +226,71 @@ impl Syscall<'_> { kmem.vmo_bytes = vmo_page_bytes() as u64; UserOutPtr::::from(buffer).write(kmem)?; } + Topic::JobProcess => { + let job = proc.get_object_with_rights::(handle, Rights::ENUMERATE)?; + let (mut count, mut avail_count) = (0usize, 0usize); + let ptr = UserOutPtr::::from(buffer).as_ptr(); + let item_size = core::mem::size_of::(); + job.enumerate_process(|id| { + if count < buffer_size / item_size { + unsafe { + ptr.add(count).write(id); + } + count += 1; + } + avail_count += 1; + true + }); + actual.write(count)?; + avail.write(avail_count)?; + } + Topic::TaskStats => { + assert_eq!(core::mem::size_of::(), buffer_size); + let vmar = proc + .get_object_with_rights::(handle, Rights::INSPECT)? + .vmar(); + //let mut task_stats = ZxInfoTaskStats::default(); + let task_stats = vmar.get_task_stats(); + UserOutPtr::::from(buffer).write(task_stats)?; + } + Topic::ProcessThreads => { + let (mut count, mut avail_count) = (0usize, 0usize); + let ptr = UserOutPtr::::from(buffer).as_ptr(); + let item_size = core::mem::size_of::(); + proc.get_object_with_rights::(handle, Rights::ENUMERATE)? + .enumerate_thread(|id| { + if count < buffer_size / item_size { + unsafe { + ptr.add(count).write(id); + } + count += 1; + } + avail_count += 1; + true + }); + actual.write(count)?; + avail.write(avail_count)?; + } + Topic::JobChildren => { + let (mut count, mut avail_count) = (0usize, 0usize); + let ptr = UserOutPtr::::from(buffer).as_ptr(); + let item_size = core::mem::size_of::(); + proc.get_object_with_rights::(handle, Rights::ENUMERATE)? + .enumerate_children(|id| { + if count < buffer_size / item_size { + unsafe { + ptr.add(count).write(id); + } + count += 1; + } + avail_count += 1; + true + }); + actual.write(count)?; + avail.write(avail_count)?; + } _ => { - warn!("not supported info topic: {:?}", topic); + error!("not supported info topic: {:?}", topic); return Err(ZxError::NOT_SUPPORTED); } } @@ -324,6 +388,34 @@ impl Syscall<'_> { user_items.write_array(&items)?; Ok(()) } + + pub fn sys_object_get_child( + &self, + handle: HandleValue, + koid: KoID, + rights: u32, + mut out: UserOutPtr, + ) -> ZxResult { + info!( + "object.get_child: handle={:#x}, koid={:#x}, rights={:#x}", + handle, koid, rights + ); + let mut rights = Rights::from_bits(rights).ok_or(ZxError::INVALID_ARGS)?; + let proc = self.thread.proc(); + let (task, parent_rights) = proc.get_dyn_object_and_rights(handle)?; + if !parent_rights.contains(Rights::ENUMERATE) { + return Err(ZxError::ACCESS_DENIED); + } + if rights == Rights::SAME_RIGHTS { + rights = parent_rights; + } else if (rights & parent_rights) != rights { + return Err(ZxError::ACCESS_DENIED); + } + let child = task.get_child(koid)?; + let child_handle = proc.add_handle(Handle::new(child, rights)); + out.write(child_handle)?; + Ok(()) + } } numeric_enum! {