diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/fuchsia-zircon/src/vmo.rs | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/fuchsia-zircon/src/vmo.rs')
-rw-r--r-- | third_party/rust/fuchsia-zircon/src/vmo.rs | 256 |
1 files changed, 256 insertions, 0 deletions
diff --git a/third_party/rust/fuchsia-zircon/src/vmo.rs b/third_party/rust/fuchsia-zircon/src/vmo.rs new file mode 100644 index 0000000000..68c86b5429 --- /dev/null +++ b/third_party/rust/fuchsia-zircon/src/vmo.rs @@ -0,0 +1,256 @@ +// Copyright 2017 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +//! Type-safe bindings for Zircon vmo objects. + +use {AsHandleRef, Cookied, HandleBased, Handle, HandleRef, Status}; +use {sys, ok}; +use std::{mem, ptr}; + +/// An object representing a Zircon +/// [virtual memory object](https://fuchsia.googlesource.com/zircon/+/master/docs/objects/vm_object.md). +/// +/// As essentially a subtype of `Handle`, it can be freely interconverted. +#[derive(Debug, Eq, PartialEq)] +pub struct Vmo(Handle); +impl_handle_based!(Vmo); +impl Cookied for Vmo {} + +impl Vmo { + /// Create a virtual memory object. + /// + /// Wraps the + /// `zx_vmo_create` + /// syscall. See the + /// [Shared Memory: Virtual Memory Objects (VMOs)](https://fuchsia.googlesource.com/zircon/+/master/docs/concepts.md#Shared-Memory_Virtual-Memory-Objects-VMOs) + /// for more information. + pub fn create(size: u64) -> Result<Vmo, Status> { + let mut handle = 0; + let opts = 0; + let status = unsafe { sys::zx_vmo_create(size, opts, &mut handle) }; + ok(status)?; + unsafe { + Ok(Vmo::from(Handle::from_raw(handle))) + } + } + + /// Read from a virtual memory object. + /// + /// Wraps the `zx_vmo_read` syscall. + pub fn read(&self, data: &mut [u8], offset: u64) -> Result<usize, Status> { + unsafe { + let mut actual = 0; + let status = sys::zx_vmo_read(self.raw_handle(), data.as_mut_ptr(), + offset, data.len(), &mut actual); + ok(status).map(|()| actual) + } + } + + /// Write to a virtual memory object. + /// + /// Wraps the `zx_vmo_write` syscall. + pub fn write(&self, data: &[u8], offset: u64) -> Result<usize, Status> { + unsafe { + let mut actual = 0; + let status = sys::zx_vmo_write(self.raw_handle(), data.as_ptr(), + offset, data.len(), &mut actual); + ok(status).map(|()| actual) + } + } + + /// Get the size of a virtual memory object. + /// + /// Wraps the `zx_vmo_get_size` syscall. + pub fn get_size(&self) -> Result<u64, Status> { + let mut size = 0; + let status = unsafe { sys::zx_vmo_get_size(self.raw_handle(), &mut size) }; + ok(status).map(|()| size) + } + + /// Attempt to change the size of a virtual memory object. + /// + /// Wraps the `zx_vmo_set_size` syscall. + pub fn set_size(&self, size: u64) -> Result<(), Status> { + let status = unsafe { sys::zx_vmo_set_size(self.raw_handle(), size) }; + ok(status) + } + + /// Perform an operation on a range of a virtual memory object. + /// + /// Wraps the + /// [zx_vmo_op_range](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/vmo_op_range.md) + /// syscall. + pub fn op_range(&self, op: VmoOp, offset: u64, size: u64) -> Result<(), Status> { + let status = unsafe { + sys::zx_vmo_op_range(self.raw_handle(), op.into_raw(), offset, size, ptr::null_mut(), 0) + }; + ok(status) + } + + /// Look up a list of physical addresses corresponding to the pages held by the VMO from + /// `offset` to `offset`+`size`, and store them in `buffer`. + /// + /// Wraps the + /// [zx_vmo_op_range](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/vmo_op_range.md) + /// syscall with ZX_VMO_OP_LOOKUP. + pub fn lookup(&self, offset: u64, size: u64, buffer: &mut [sys::zx_paddr_t]) + -> Result<(), Status> + { + let status = unsafe { + sys::zx_vmo_op_range(self.raw_handle(), VmoOp::LOOKUP.into_raw(), offset, size, + buffer.as_mut_ptr() as *mut u8, buffer.len() * mem::size_of::<sys::zx_paddr_t>()) + }; + ok(status) + } + + /// Create a new virtual memory object that clones a range of this one. + /// + /// Wraps the + /// [zx_vmo_clone](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/vmo_clone.md) + /// syscall. + pub fn clone(&self, offset: u64, size: u64) -> Result<Vmo, Status> { + let mut out = 0; + let opts = sys::ZX_VMO_CLONE_COPY_ON_WRITE; + let status = unsafe { + sys::zx_vmo_clone(self.raw_handle(), opts, offset, size, &mut out) + }; + ok(status)?; + unsafe { Ok(Vmo::from(Handle::from_raw(out))) } + } +} + +/// VM Object opcodes +#[repr(C)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub struct VmoOp(u32); +impl VmoOp { + pub fn from_raw(raw: u32) -> VmoOp { + VmoOp(raw) + } + pub fn into_raw(self) -> u32 { + self.0 + } +} + +assoc_consts!(VmoOp, [ + COMMIT = sys::ZX_VMO_OP_COMMIT; + DECOMMIT = sys::ZX_VMO_OP_DECOMMIT; + LOCK = sys::ZX_VMO_OP_LOCK; + UNLOCK = sys::ZX_VMO_OP_UNLOCK; + LOOKUP = sys::ZX_VMO_OP_LOOKUP; + CACHE_SYNC = sys::ZX_VMO_OP_CACHE_SYNC; + CACHE_INVALIDATE = sys::ZX_VMO_OP_CACHE_INVALIDATE; + CACHE_CLEAN = sys::ZX_VMO_OP_CACHE_CLEAN; + CACHE_CLEAN_INVALIDATE = sys::ZX_VMO_OP_CACHE_CLEAN_INVALIDATE; +]); + + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn vmo_get_size() { + let size = 16 * 1024 * 1024; + let vmo = Vmo::create(size).unwrap(); + assert_eq!(size, vmo.get_size().unwrap()); + } + + #[test] + fn vmo_set_size() { + let start_size = 12; + let vmo = Vmo::create(start_size).unwrap(); + assert_eq!(start_size, vmo.get_size().unwrap()); + + // Change the size and make sure the new size is reported + let new_size = 23; + assert!(vmo.set_size(new_size).is_ok()); + assert_eq!(new_size, vmo.get_size().unwrap()); + } + + #[test] + fn vmo_read_write() { + let mut vec1 = vec![0; 16]; + let vmo = Vmo::create(vec1.len() as u64).unwrap(); + assert_eq!(vmo.write(b"abcdef", 0), Ok(6)); + assert_eq!(16, vmo.read(&mut vec1, 0).unwrap()); + assert_eq!(b"abcdef", &vec1[0..6]); + assert_eq!(vmo.write(b"123", 2), Ok(3)); + assert_eq!(16, vmo.read(&mut vec1, 0).unwrap()); + assert_eq!(b"ab123f", &vec1[0..6]); + assert_eq!(15, vmo.read(&mut vec1, 1).unwrap()); + assert_eq!(b"b123f", &vec1[0..5]); + } + + #[test] + fn vmo_op_range_unsupported() { + let vmo = Vmo::create(12).unwrap(); + assert_eq!(vmo.op_range(VmoOp::LOCK, 0, 1), Err(Status::NOT_SUPPORTED)); + assert_eq!(vmo.op_range(VmoOp::UNLOCK, 0, 1), Err(Status::NOT_SUPPORTED)); + } + + #[test] + fn vmo_lookup() { + let vmo = Vmo::create(12).unwrap(); + let mut buffer = vec![0; 2]; + + // Lookup will fail as it is not committed yet. + assert_eq!(vmo.lookup(0, 12, &mut buffer), Err(Status::NO_MEMORY)); + + // COMMIT and try again. + assert_eq!(vmo.op_range(VmoOp::COMMIT, 0, 12), Ok(())); + assert_eq!(vmo.lookup(0, 12, &mut buffer), Ok(())); + assert_ne!(buffer[0], 0); + assert_eq!(buffer[1], 0); + + // If we decommit then lookup should go back to failing. + assert_eq!(vmo.op_range(VmoOp::DECOMMIT, 0, 12), Ok(())); + assert_eq!(vmo.lookup(0, 12, &mut buffer), Err(Status::NO_MEMORY)); + } + + #[test] + fn vmo_cache() { + let vmo = Vmo::create(12).unwrap(); + + // Cache operations should all succeed. + assert_eq!(vmo.op_range(VmoOp::CACHE_SYNC, 0, 12), Ok(())); + assert_eq!(vmo.op_range(VmoOp::CACHE_INVALIDATE, 0, 12), Ok(())); + assert_eq!(vmo.op_range(VmoOp::CACHE_CLEAN, 0, 12), Ok(())); + assert_eq!(vmo.op_range(VmoOp::CACHE_CLEAN_INVALIDATE, 0, 12), Ok(())); + } + + #[test] + fn vmo_clone() { + let original = Vmo::create(12).unwrap(); + assert_eq!(original.write(b"one", 0), Ok(3)); + + // Clone the VMO, and make sure it contains what we expect. + let clone = original.clone(0, 10).unwrap(); + let mut read_buffer = vec![0; 16]; + assert_eq!(clone.read(&mut read_buffer, 0), Ok(10)); + assert_eq!(&read_buffer[0..3], b"one"); + + // Writing to the original will affect the clone too, surprisingly. + assert_eq!(original.write(b"two", 0), Ok(3)); + assert_eq!(original.read(&mut read_buffer, 0), Ok(12)); + assert_eq!(&read_buffer[0..3], b"two"); + assert_eq!(clone.read(&mut read_buffer, 0), Ok(10)); + assert_eq!(&read_buffer[0..3], b"two"); + + // However, writing to the clone will not affect the original + assert_eq!(clone.write(b"three", 0), Ok(5)); + assert_eq!(original.read(&mut read_buffer, 0), Ok(12)); + assert_eq!(&read_buffer[0..3], b"two"); + assert_eq!(clone.read(&mut read_buffer, 0), Ok(10)); + assert_eq!(&read_buffer[0..5], b"three"); + + // And now that the copy-on-write has happened, writing to the original will not affect the + // clone. How bizarre. + assert_eq!(original.write(b"four", 0), Ok(4)); + assert_eq!(original.read(&mut read_buffer, 0), Ok(12)); + assert_eq!(&read_buffer[0..4], b"four"); + assert_eq!(clone.read(&mut read_buffer, 0), Ok(10)); + assert_eq!(&read_buffer[0..5], b"three"); + } +} |