diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:47:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:47:29 +0000 |
commit | 0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d (patch) | |
tree | a31f07c9bcca9d56ce61e9a1ffd30ef350d513aa /third_party/rust/fuchsia-zircon/src | |
parent | Initial commit. (diff) | |
download | firefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.tar.xz firefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.zip |
Adding upstream version 115.8.0esr.upstream/115.8.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/fuchsia-zircon/src')
18 files changed, 2716 insertions, 0 deletions
diff --git a/third_party/rust/fuchsia-zircon/src/channel.rs b/third_party/rust/fuchsia-zircon/src/channel.rs new file mode 100644 index 0000000000..44ffc6cd9c --- /dev/null +++ b/third_party/rust/fuchsia-zircon/src/channel.rs @@ -0,0 +1,418 @@ +// 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 channel objects. + +use {AsHandleRef, HandleBased, Handle, HandleRef, Peered, Status, Time, usize_into_u32, size_to_u32_sat}; +use {sys, ok}; +use std::mem; + +/// An object representing a Zircon +/// [channel](https://fuchsia.googlesource.com/zircon/+/master/docs/objects/channel.md). +/// +/// As essentially a subtype of `Handle`, it can be freely interconverted. +#[derive(Debug, Eq, PartialEq, Hash)] +pub struct Channel(Handle); +impl_handle_based!(Channel); +impl Peered for Channel {} + +impl Channel { + /// Create a channel, resulting an a pair of `Channel` objects representing both + /// sides of the channel. Messages written into one maybe read from the opposite. + /// + /// Wraps the + /// [zx_channel_create](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/channel_create.md) + /// syscall. + pub fn create() -> Result<(Channel, Channel), Status> { + unsafe { + let mut handle0 = 0; + let mut handle1 = 0; + let opts = 0; + ok(sys::zx_channel_create(opts, &mut handle0, &mut handle1))?; + Ok(( + Self::from(Handle::from_raw(handle0)), + Self::from(Handle::from_raw(handle1)) + )) + } + } + + /// Read a message from a channel. Wraps the + /// [zx_channel_read](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/channel_read.md) + /// syscall. + /// + /// If the `MessageBuf` lacks the capacity to hold the pending message, + /// returns an `Err` with the number of bytes and number of handles needed. + /// Otherwise returns an `Ok` with the result as usual. + pub fn read_raw(&self, buf: &mut MessageBuf) + -> Result<Result<(), Status>, (usize, usize)> + { + let opts = 0; + unsafe { + buf.clear(); + let raw_handle = self.raw_handle(); + let mut num_bytes: u32 = size_to_u32_sat(buf.bytes.capacity()); + let mut num_handles: u32 = size_to_u32_sat(buf.handles.capacity()); + let status = ok(sys::zx_channel_read(raw_handle, opts, + buf.bytes.as_mut_ptr(), buf.handles.as_mut_ptr() as *mut _, + num_bytes, num_handles, &mut num_bytes, &mut num_handles)); + if status == Err(Status::BUFFER_TOO_SMALL) { + Err((num_bytes as usize, num_handles as usize)) + } else { + Ok(status.map(|()| { + buf.bytes.set_len(num_bytes as usize); + buf.handles.set_len(num_handles as usize); + })) + } + } + } + + /// Read a message from a channel. + /// + /// Note that this method can cause internal reallocations in the `MessageBuf` + /// if it is lacks capacity to hold the full message. If such reallocations + /// are not desirable, use `read_raw` instead. + pub fn read(&self, buf: &mut MessageBuf) -> Result<(), Status> { + loop { + match self.read_raw(buf) { + Ok(result) => return result, + Err((num_bytes, num_handles)) => { + buf.ensure_capacity_bytes(num_bytes); + buf.ensure_capacity_handles(num_handles); + } + } + } + } + + /// Write a message to a channel. Wraps the + /// [zx_channel_write](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/channel_write.md) + /// syscall. + pub fn write(&self, bytes: &[u8], handles: &mut Vec<Handle>) + -> Result<(), Status> + { + let opts = 0; + let n_bytes = try!(usize_into_u32(bytes.len()).map_err(|_| Status::OUT_OF_RANGE)); + let n_handles = try!(usize_into_u32(handles.len()).map_err(|_| Status::OUT_OF_RANGE)); + unsafe { + let status = sys::zx_channel_write(self.raw_handle(), opts, bytes.as_ptr(), n_bytes, + handles.as_ptr() as *const sys::zx_handle_t, n_handles); + ok(status)?; + // Handles were successfully transferred, forget them on sender side + handles.set_len(0); + Ok(()) + } + } + + /// Send a message consisting of the given bytes and handles to a channel and await a reply. The + /// bytes should start with a four byte 'txid' which is used to identify the matching reply. + /// + /// Wraps the + /// [zx_channel_call](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/channel_call.md) + /// syscall. + /// + /// Note that unlike [`read`][read], the caller must ensure that the MessageBuf has enough + /// capacity for the bytes and handles which will be received, as replies which are too large + /// are discarded. + /// + /// On failure returns the both the main and read status. + /// + /// [read]: struct.Channel.html#method.read + pub fn call(&self, timeout: Time, bytes: &[u8], handles: &mut Vec<Handle>, + buf: &mut MessageBuf) -> Result<(), (Status, Status)> + { + let write_num_bytes = try!(usize_into_u32(bytes.len()).map_err( + |_| (Status::OUT_OF_RANGE, Status::OK))); + let write_num_handles = try!(usize_into_u32(handles.len()).map_err( + |_| (Status::OUT_OF_RANGE, Status::OK))); + buf.clear(); + let read_num_bytes: u32 = size_to_u32_sat(buf.bytes.capacity()); + let read_num_handles: u32 = size_to_u32_sat(buf.handles.capacity()); + let args = sys::zx_channel_call_args_t { + wr_bytes: bytes.as_ptr(), + wr_handles: handles.as_ptr() as *const sys::zx_handle_t, + rd_bytes: buf.bytes.as_mut_ptr(), + rd_handles: buf.handles.as_mut_ptr() as *mut _, + wr_num_bytes: write_num_bytes, + wr_num_handles: write_num_handles, + rd_num_bytes: read_num_bytes, + rd_num_handles: read_num_handles, + }; + let mut actual_read_bytes: u32 = 0; + let mut actual_read_handles: u32 = 0; + let mut read_status = Status::OK.into_raw(); + let options = 0; + let status = unsafe { + Status::from_raw( + sys::zx_channel_call( + self.raw_handle(), options, timeout.nanos(), &args, &mut actual_read_bytes, + &mut actual_read_handles, &mut read_status)) + }; + + match status { + Status::OK | + Status::TIMED_OUT | + Status::CALL_FAILED => { + // Handles were successfully transferred, + // even if we didn't get a response, so forget + // them on the sender side. + unsafe { handles.set_len(0); } + } + _ => {} + } + + unsafe { + buf.bytes.set_len(actual_read_bytes as usize); + buf.handles.set_len(actual_read_handles as usize); + } + if Status::OK == status { + Ok(()) + } else { + Err((status, Status::from_raw(read_status))) + } + } +} + +#[test] +pub fn test_handle_repr() { + assert_eq!(::std::mem::size_of::<sys::zx_handle_t>(), 4); + assert_eq!(::std::mem::size_of::<Handle>(), 4); + assert_eq!(::std::mem::align_of::<sys::zx_handle_t>(), ::std::mem::align_of::<Handle>()); + + // This test asserts that repr(transparent) still works for Handle -> zx_handle_t + + let n: Vec<sys::zx_handle_t> = vec![0, 100, 2<<32-1]; + let v: Vec<Handle> = n.iter().map(|h| unsafe { Handle::from_raw(*h) } ).collect(); + + for (handle, raw) in v.iter().zip(n.iter()) { + unsafe { + assert_eq!(*(handle as *const _ as *const [u8; 4]), *(raw as *const _ as *const [u8; 4])); + } + } + + for h in v.into_iter() { + ::std::mem::forget(h); + } +} + +impl AsRef<Channel> for Channel { + fn as_ref(&self) -> &Self { + &self + } +} + +/// A buffer for _receiving_ messages from a channel. +/// +/// A `MessageBuf` is essentially a byte buffer and a vector of +/// handles, but move semantics for "taking" handles requires special handling. +/// +/// Note that for sending messages to a channel, the caller manages the buffers, +/// using a plain byte slice and `Vec<Handle>`. +#[derive(Default)] +#[derive(Debug)] +pub struct MessageBuf { + bytes: Vec<u8>, + handles: Vec<Handle>, +} + +impl MessageBuf { + /// Create a new, empty, message buffer. + pub fn new() -> Self { + Default::default() + } + + /// Create a new non-empty message buffer. + pub fn new_with(v: Vec<u8>, h: Vec<Handle>) -> Self { + Self{ + bytes: v, + handles: h, + } + } + + /// Ensure that the buffer has the capacity to hold at least `n_bytes` bytes. + pub fn ensure_capacity_bytes(&mut self, n_bytes: usize) { + ensure_capacity(&mut self.bytes, n_bytes); + } + + /// Ensure that the buffer has the capacity to hold at least `n_handles` handles. + pub fn ensure_capacity_handles(&mut self, n_handles: usize) { + ensure_capacity(&mut self.handles, n_handles); + } + + /// Ensure that at least n_bytes bytes are initialized (0 fill). + pub fn ensure_initialized_bytes(&mut self, n_bytes: usize) { + if n_bytes <= self.bytes.len() { + return; + } + self.bytes.resize(n_bytes, 0); + } + + /// Get a reference to the bytes of the message buffer, as a `&[u8]` slice. + pub fn bytes(&self) -> &[u8] { + self.bytes.as_slice() + } + + /// The number of handles in the message buffer. Note this counts the number + /// available when the message was received; `take_handle` does not affect + /// the count. + pub fn n_handles(&self) -> usize { + self.handles.len() + } + + /// Take the handle at the specified index from the message buffer. If the + /// method is called again with the same index, it will return `None`, as + /// will happen if the index exceeds the number of handles available. + pub fn take_handle(&mut self, index: usize) -> Option<Handle> { + self.handles.get_mut(index).and_then(|handle| + if handle.is_invalid() { + None + } else { + Some(mem::replace(handle, Handle::invalid())) + } + ) + } + + /// Clear the bytes and handles contained in the buf. This will drop any + /// contained handles, resulting in their resources being freed. + pub fn clear(&mut self) { + self.bytes.clear(); + self.handles.clear(); + } +} + +fn ensure_capacity<T>(vec: &mut Vec<T>, size: usize) { + let len = vec.len(); + if size > len { + vec.reserve(size - len); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use {DurationNum, Rights, Signals, Vmo}; + use std::thread; + + #[test] + fn channel_basic() { + let (p1, p2) = Channel::create().unwrap(); + + let mut empty = vec![]; + assert!(p1.write(b"hello", &mut empty).is_ok()); + + let mut buf = MessageBuf::new(); + assert!(p2.read(&mut buf).is_ok()); + assert_eq!(buf.bytes(), b"hello"); + } + + #[test] + fn channel_read_raw_too_small() { + let (p1, p2) = Channel::create().unwrap(); + + let mut empty = vec![]; + assert!(p1.write(b"hello", &mut empty).is_ok()); + + let mut buf = MessageBuf::new(); + let result = p2.read_raw(&mut buf); + assert_eq!(result, Err((5, 0))); + assert_eq!(buf.bytes(), b""); + } + + #[test] + fn channel_send_handle() { + let hello_length: usize = 5; + + // Create a pair of channels and a virtual memory object. + let (p1, p2) = Channel::create().unwrap(); + let vmo = Vmo::create(hello_length as u64).unwrap(); + + // Duplicate VMO handle and send it down the channel. + let duplicate_vmo_handle = vmo.duplicate_handle(Rights::SAME_RIGHTS).unwrap().into(); + let mut handles_to_send: Vec<Handle> = vec![duplicate_vmo_handle]; + assert!(p1.write(b"", &mut handles_to_send).is_ok()); + // Handle should be removed from vector. + assert!(handles_to_send.is_empty()); + + // Read the handle from the receiving channel. + let mut buf = MessageBuf::new(); + assert!(p2.read(&mut buf).is_ok()); + assert_eq!(buf.n_handles(), 1); + // Take the handle from the buffer. + let received_handle = buf.take_handle(0).unwrap(); + // Should not affect number of handles. + assert_eq!(buf.n_handles(), 1); + // Trying to take it again should fail. + assert!(buf.take_handle(0).is_none()); + + // Now to test that we got the right handle, try writing something to it... + let received_vmo = Vmo::from(received_handle); + assert_eq!(received_vmo.write(b"hello", 0).unwrap(), hello_length); + + // ... and reading it back from the original VMO. + let mut read_vec = vec![0; hello_length]; + assert_eq!(vmo.read(&mut read_vec, 0).unwrap(), hello_length); + assert_eq!(read_vec, b"hello"); + } + + #[test] + fn channel_call_timeout() { + let ten_ms = 10.millis(); + + // Create a pair of channels and a virtual memory object. + let (p1, p2) = Channel::create().unwrap(); + let vmo = Vmo::create(0 as u64).unwrap(); + + // Duplicate VMO handle and send it along with the call. + let duplicate_vmo_handle = vmo.duplicate_handle(Rights::SAME_RIGHTS).unwrap().into(); + let mut handles_to_send: Vec<Handle> = vec![duplicate_vmo_handle]; + let mut buf = MessageBuf::new(); + assert_eq!(p1.call(ten_ms.after_now(), b"call", &mut handles_to_send, &mut buf), + Err((Status::TIMED_OUT, Status::OK))); + // Handle should be removed from vector even though we didn't get a response, as it was + // still sent over the channel. + assert!(handles_to_send.is_empty()); + + // Should be able to read call even though it timed out waiting for a response. + let mut buf = MessageBuf::new(); + assert!(p2.read(&mut buf).is_ok()); + assert_eq!(buf.bytes(), b"call"); + assert_eq!(buf.n_handles(), 1); + } + + #[test] + fn channel_call() { + // Create a pair of channels + let (p1, p2) = Channel::create().unwrap(); + + // create an mpsc channel for communicating the call data for later assertion + let (tx, rx) = ::std::sync::mpsc::channel(); + + // Start a new thread to respond to the call. + thread::spawn(move || { + let mut buf = MessageBuf::new(); + // if either the read or the write fail, this thread will panic, + // resulting in tx being dropped, which will be noticed by the rx. + p2.wait_handle(Signals::CHANNEL_READABLE, 1.seconds().after_now()).expect("callee wait error"); + p2.read(&mut buf).expect("callee read error"); + p2.write(b"txidresponse", &mut vec![]).expect("callee write error"); + tx.send(buf).expect("callee mpsc send error"); + }); + + // Make the call. + let mut buf = MessageBuf::new(); + buf.ensure_capacity_bytes(12); + // NOTE(raggi): CQ has been seeing some long stalls from channel call, + // and it's as yet unclear why. The timeout here has been made much + // larger in order to avoid that, as the issues are not issues with this + // crate's concerns. The timeout is here just to prevent the tests from + // stalling forever if a developer makes a mistake locally in this + // crate. Tests of Zircon behavior or virtualization behavior should be + // covered elsewhere. See ZX-1324. + p1.call(30.seconds().after_now(), b"txidcall", &mut vec![], &mut buf).expect("channel call error"); + assert_eq!(buf.bytes(), b"txidresponse"); + assert_eq!(buf.n_handles(), 0); + + let sbuf = rx.recv().expect("mpsc channel recv error"); + assert_eq!(sbuf.bytes(), b"txidcall"); + assert_eq!(sbuf.n_handles(), 0); + } +} diff --git a/third_party/rust/fuchsia-zircon/src/cprng.rs b/third_party/rust/fuchsia-zircon/src/cprng.rs new file mode 100644 index 0000000000..433ed26d9e --- /dev/null +++ b/third_party/rust/fuchsia-zircon/src/cprng.rs @@ -0,0 +1,68 @@ +use {Status, ok, sys}; + +/// Draw random bytes from the kernel's CPRNG to fill the given buffer. Returns the actual number of +/// bytes drawn, which may sometimes be less than the size of the buffer provided. +/// +/// The buffer must have length less than `ZX_CPRNG_DRAW_MAX_LEN`. +/// +/// Wraps the +/// [zx_cprng_draw](https://fuchsia.googlesource.com/zircon/+/HEAD/docs/syscalls/cprng_draw.md) +/// syscall. +pub fn cprng_draw(buffer: &mut [u8]) -> Result<usize, Status> { + let mut actual = 0; + let status = unsafe { sys::zx_cprng_draw(buffer.as_mut_ptr(), buffer.len(), &mut actual) }; + ok(status).map(|()| actual) +} + +/// Mix the given entropy into the kernel CPRNG. +/// +/// The buffer must have length less than `ZX_CPRNG_ADD_ENTROPY_MAX_LEN`. +/// +/// Wraps the +/// [zx_cprng_add_entropy](https://fuchsia.googlesource.com/zircon/+/HEAD/docs/syscalls/cprng_add_entropy.md) +/// syscall. +pub fn cprng_add_entropy(buffer: &[u8]) -> Result<(), Status> { + let status = unsafe { sys::zx_cprng_add_entropy(buffer.as_ptr(), buffer.len()) }; + ok(status) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn cprng() { + let mut buffer = [0; 20]; + assert_eq!(cprng_draw(&mut buffer), Ok(20)); + let mut first_zero = 0; + let mut last_zero = 0; + for _ in 0..30 { + let mut buffer = [0; 20]; + assert_eq!(cprng_draw(&mut buffer), Ok(20)); + if buffer[0] == 0 { + first_zero += 1; + } + if buffer[19] == 0 { + last_zero += 1; + } + } + assert_ne!(first_zero, 30); + assert_ne!(last_zero, 30); + } + + #[test] + fn cprng_too_large() { + let mut buffer = [0; sys::ZX_CPRNG_DRAW_MAX_LEN + 1]; + assert_eq!(cprng_draw(&mut buffer), Err(Status::INVALID_ARGS)); + + for mut s in buffer.chunks_mut(sys::ZX_CPRNG_DRAW_MAX_LEN) { + assert_eq!(cprng_draw(&mut s), Ok(s.len())); + } + } + + #[test] + fn cprng_add() { + let buffer = [0, 1, 2]; + assert_eq!(cprng_add_entropy(&buffer), Ok(())); + } +}
\ No newline at end of file diff --git a/third_party/rust/fuchsia-zircon/src/event.rs b/third_party/rust/fuchsia-zircon/src/event.rs new file mode 100644 index 0000000000..533a8aafcc --- /dev/null +++ b/third_party/rust/fuchsia-zircon/src/event.rs @@ -0,0 +1,32 @@ +// 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 event objects. + +use {AsHandleRef, Cookied, HandleBased, Handle, HandleRef, Status}; +use {sys, ok}; + +/// An object representing a Zircon +/// [event object](https://fuchsia.googlesource.com/zircon/+/master/docs/objects/event.md). +/// +/// As essentially a subtype of `Handle`, it can be freely interconverted. +#[derive(Debug, Eq, PartialEq)] +pub struct Event(Handle); +impl_handle_based!(Event); +impl Cookied for Event {} + +impl Event { + /// Create an event object, an object which is signalable but nothing else. Wraps the + /// [zx_event_create](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/event_create.md) + /// syscall. + pub fn create() -> Result<Event, Status> { + let mut out = 0; + let opts = 0; + let status = unsafe { sys::zx_event_create(opts, &mut out) }; + ok(status)?; + unsafe { + Ok(Self::from(Handle::from_raw(out))) + } + } +} diff --git a/third_party/rust/fuchsia-zircon/src/eventpair.rs b/third_party/rust/fuchsia-zircon/src/eventpair.rs new file mode 100644 index 0000000000..6f2d29806d --- /dev/null +++ b/third_party/rust/fuchsia-zircon/src/eventpair.rs @@ -0,0 +1,65 @@ +// Copyright 2016 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 event pairs. + +use {AsHandleRef, Cookied, HandleBased, Handle, HandleRef, Peered, Status}; +use {sys, ok}; + +/// An object representing a Zircon +/// [event pair](https://fuchsia.googlesource.com/zircon/+/master/docs/concepts.md#Other-IPC_Events_Event-Pairs_and-User-Signals). +/// +/// As essentially a subtype of `Handle`, it can be freely interconverted. +#[derive(Debug, Eq, PartialEq)] +pub struct EventPair(Handle); +impl_handle_based!(EventPair); +impl Peered for EventPair {} +impl Cookied for EventPair {} + +impl EventPair { + /// Create an event pair, a pair of objects which can signal each other. Wraps the + /// [zx_eventpair_create](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/eventpair_create.md) + /// syscall. + pub fn create() -> Result<(EventPair, EventPair), Status> { + let mut out0 = 0; + let mut out1 = 0; + let options = 0; + let status = unsafe { sys::zx_eventpair_create(options, &mut out0, &mut out1) }; + ok(status)?; + unsafe { + Ok(( + Self::from(Handle::from_raw(out0)), + Self::from(Handle::from_raw(out1)) + )) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use {DurationNum, Signals}; + + #[test] + fn wait_and_signal_peer() { + let (p1, p2) = EventPair::create().unwrap(); + let eighty_ms = 80.millis(); + + // Waiting on one without setting any signal should time out. + assert_eq!(p2.wait_handle(Signals::USER_0, eighty_ms.after_now()), Err(Status::TIMED_OUT)); + + // If we set a signal, we should be able to wait for it. + assert!(p1.signal_peer(Signals::NONE, Signals::USER_0).is_ok()); + assert_eq!(p2.wait_handle(Signals::USER_0, eighty_ms.after_now()).unwrap(), + Signals::USER_0); + + // Should still work, signals aren't automatically cleared. + assert_eq!(p2.wait_handle(Signals::USER_0, eighty_ms.after_now()).unwrap(), + Signals::USER_0); + + // Now clear it, and waiting should time out again. + assert!(p1.signal_peer(Signals::USER_0, Signals::NONE).is_ok()); + assert_eq!(p2.wait_handle(Signals::USER_0, eighty_ms.after_now()), Err(Status::TIMED_OUT)); + } +} diff --git a/third_party/rust/fuchsia-zircon/src/fifo.rs b/third_party/rust/fuchsia-zircon/src/fifo.rs new file mode 100644 index 0000000000..20af6f5236 --- /dev/null +++ b/third_party/rust/fuchsia-zircon/src/fifo.rs @@ -0,0 +1,98 @@ +// 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 fifo objects. + +use {AsHandleRef, HandleBased, Handle, HandleRef, Status}; +use {sys, ok}; + +/// An object representing a Zircon fifo. +/// +/// As essentially a subtype of `Handle`, it can be freely interconverted. +#[derive(Debug, Eq, PartialEq)] +pub struct Fifo(Handle); +impl_handle_based!(Fifo); + +impl Fifo { + /// Create a pair of fifos and return their endpoints. Writing to one endpoint enqueues an + /// element into the fifo from which the opposing endpoint reads. Wraps the + /// [zx_fifo_create](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/fifo_create.md) + /// syscall. + pub fn create(elem_count: u32, elem_size: u32) + -> Result<(Fifo, Fifo), Status> + { + let mut out0 = 0; + let mut out1 = 0; + let options = 0; + let status = unsafe { + sys::zx_fifo_create(elem_count, elem_size, options, &mut out0, &mut out1) + }; + ok(status)?; + unsafe { Ok(( + Self::from(Handle::from_raw(out0)), + Self::from(Handle::from_raw(out1)) + ))} + } + + /// Attempts to write some number of elements into the fifo. The number of bytes written will be + /// rounded down to a multiple of the fifo's element size. + /// Return value (on success) is number of elements actually written. + /// + /// Wraps + /// [zx_fifo_write](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/fifo_write.md). + pub fn write(&self, bytes: &[u8]) -> Result<u32, Status> { + let mut num_entries_written = 0; + let status = unsafe { + sys::zx_fifo_write(self.raw_handle(), bytes.as_ptr(), bytes.len(), + &mut num_entries_written) + }; + ok(status).map(|()| num_entries_written) + } + + /// Attempts to read some number of elements out of the fifo. The number of bytes read will + /// always be a multiple of the fifo's element size. + /// Return value (on success) is number of elements actually read. + /// + /// Wraps + /// [zx_fifo_read](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/fifo_read.md). + pub fn read(&self, bytes: &mut [u8]) -> Result<u32, Status> { + let mut num_entries_read = 0; + let status = unsafe { + sys::zx_fifo_read(self.raw_handle(), bytes.as_mut_ptr(), bytes.len(), + &mut num_entries_read) + }; + ok(status).map(|()| num_entries_read) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn fifo_basic() { + let (fifo1, fifo2) = Fifo::create(4, 2).unwrap(); + + // Trying to write less than one element should fail. + assert_eq!(fifo1.write(b""), Err(Status::OUT_OF_RANGE)); + assert_eq!(fifo1.write(b"h"), Err(Status::OUT_OF_RANGE)); + + // Should write one element "he" and ignore the last half-element as it rounds down. + assert_eq!(fifo1.write(b"hex").unwrap(), 1); + + // Should write three elements "ll" "o " "wo" and drop the rest as it is full. + assert_eq!(fifo1.write(b"llo worlds").unwrap(), 3); + + // Now that the fifo is full any further attempts to write should fail. + assert_eq!(fifo1.write(b"blah blah"), Err(Status::SHOULD_WAIT)); + + // Read all 4 entries from the other end. + let mut read_vec = vec![0; 8]; + assert_eq!(fifo2.read(&mut read_vec).unwrap(), 4); + assert_eq!(read_vec, b"hello wo"); + + // Reading again should fail as the fifo is empty. + assert_eq!(fifo2.read(&mut read_vec), Err(Status::SHOULD_WAIT)); + } +} diff --git a/third_party/rust/fuchsia-zircon/src/handle.rs b/third_party/rust/fuchsia-zircon/src/handle.rs new file mode 100644 index 0000000000..5c50f29f0e --- /dev/null +++ b/third_party/rust/fuchsia-zircon/src/handle.rs @@ -0,0 +1,243 @@ +use {Port, Rights, Signals, Status, Time, WaitAsyncOpts, ok, sys}; +use std::marker::PhantomData; +use std::mem; + +/// An object representing a Zircon +/// [handle](https://fuchsia.googlesource.com/zircon/+/master/docs/handles.md). +/// +/// Internally, it is represented as a 32-bit integer, but this wrapper enforces +/// strict ownership semantics. The `Drop` implementation closes the handle. +/// +/// This type represents the most general reference to a kernel object, and can +/// be interconverted to and from more specific types. Those conversions are not +/// enforced in the type system; attempting to use them will result in errors +/// returned by the kernel. These conversions don't change the underlying +/// representation, but do change the type and thus what operations are available. +#[derive(Debug, Eq, PartialEq, Hash)] +pub struct Handle(sys::zx_handle_t); + +impl AsHandleRef for Handle { + fn as_handle_ref(&self) -> HandleRef { + HandleRef { handle: self.0, phantom: Default::default() } + } +} + +impl HandleBased for Handle {} + +impl Drop for Handle { + fn drop(&mut self) { + if self.0 != sys::ZX_HANDLE_INVALID { + unsafe { sys::zx_handle_close(self.0) }; + } + } +} + +impl Handle { + /// Initialize a handle backed by ZX_HANDLE_INVALID, the only safe non-handle. + pub fn invalid() -> Handle { + Handle(sys::ZX_HANDLE_INVALID) + } + + /// If a raw handle is obtained from some other source, this method converts + /// it into a type-safe owned handle. + pub unsafe fn from_raw(raw: sys::zx_handle_t) -> Handle { + Handle(raw) + } + + pub fn is_invalid(&self) -> bool { + self.0 == sys::ZX_HANDLE_INVALID + } + + pub fn replace(self, rights: Rights) -> Result<Handle, Status> { + let handle = self.0; + let mut out = 0; + let status = unsafe { sys::zx_handle_replace(handle, rights.bits(), &mut out) }; + ok(status).map(|()| Handle(out)) + } +} + +/// A borrowed reference to a `Handle`. +/// +/// Mostly useful as part of a `WaitItem`. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub struct HandleRef<'a> { + handle: sys::zx_handle_t, + phantom: PhantomData<&'a sys::zx_handle_t>, +} + +impl<'a> HandleRef<'a> { + pub fn raw_handle(&self) -> sys::zx_handle_t { + self.handle + } + + pub fn duplicate(&self, rights: Rights) -> Result<Handle, Status> { + let handle = self.handle; + let mut out = 0; + let status = unsafe { sys::zx_handle_duplicate(handle, rights.bits(), &mut out) }; + ok(status).map(|()| Handle(out)) + } + + pub fn signal(&self, clear_mask: Signals, set_mask: Signals) -> Result<(), Status> { + let handle = self.handle; + let status = unsafe { sys::zx_object_signal(handle, clear_mask.bits(), set_mask.bits()) }; + ok(status) + } + + pub fn wait(&self, signals: Signals, deadline: Time) -> Result<Signals, Status> { + let handle = self.handle; + let mut pending = Signals::empty().bits(); + let status = unsafe { + sys::zx_object_wait_one(handle, signals.bits(), deadline.nanos(), &mut pending) + }; + ok(status).map(|()| Signals::from_bits_truncate(pending)) + } + + pub fn wait_async(&self, port: &Port, key: u64, signals: Signals, options: WaitAsyncOpts) + -> Result<(), Status> + { + let handle = self.handle; + let status = unsafe { + sys::zx_object_wait_async( + handle, port.raw_handle(), key, signals.bits(), options as u32) + }; + ok(status) + } +} + +/// A trait to get a reference to the underlying handle of an object. +pub trait AsHandleRef { + /// Get a reference to the handle. One important use of such a reference is + /// for `object_wait_many`. + fn as_handle_ref(&self) -> HandleRef; + + /// Interpret the reference as a raw handle (an integer type). Two distinct + /// handles will have different raw values (so it can perhaps be used as a + /// key in a data structure). + fn raw_handle(&self) -> sys::zx_handle_t { + self.as_handle_ref().raw_handle() + } + + /// Set and clear userspace-accessible signal bits on an object. Wraps the + /// [zx_object_signal](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/object_signal.md) + /// syscall. + fn signal_handle(&self, clear_mask: Signals, set_mask: Signals) -> Result<(), Status> { + self.as_handle_ref().signal(clear_mask, set_mask) + } + + /// Waits on a handle. Wraps the + /// [zx_object_wait_one](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/object_wait_one.md) + /// syscall. + fn wait_handle(&self, signals: Signals, deadline: Time) -> Result<Signals, Status> { + self.as_handle_ref().wait(signals, deadline) + } + + /// Causes packet delivery on the given port when the object changes state and matches signals. + /// [zx_object_wait_async](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/object_wait_async.md) + /// syscall. + fn wait_async_handle(&self, port: &Port, key: u64, signals: Signals, options: WaitAsyncOpts) + -> Result<(), Status> + { + self.as_handle_ref().wait_async(port, key, signals, options) + } +} + +impl<'a> AsHandleRef for HandleRef<'a> { + fn as_handle_ref(&self) -> HandleRef { *self } +} + +/// A trait implemented by all handle-based types. +/// +/// Note: it is reasonable for user-defined objects wrapping a handle to implement +/// this trait. For example, a specific interface in some protocol might be +/// represented as a newtype of `Channel`, and implement the `as_handle_ref` +/// method and the `From<Handle>` trait to facilitate conversion from and to the +/// interface. +pub trait HandleBased: AsHandleRef + From<Handle> + Into<Handle> { + /// Duplicate a handle, possibly reducing the rights available. Wraps the + /// [zx_handle_duplicate](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/handle_duplicate.md) + /// syscall. + fn duplicate_handle(&self, rights: Rights) -> Result<Self, Status> { + self.as_handle_ref().duplicate(rights).map(|handle| Self::from(handle)) + } + + /// Create a replacement for a handle, possibly reducing the rights available. This invalidates + /// the original handle. Wraps the + /// [zx_handle_replace](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/handle_replace.md) + /// syscall. + fn replace_handle(self, rights: Rights) -> Result<Self, Status> { + <Self as Into<Handle>>::into(self) + .replace(rights).map(|handle| Self::from(handle)) + } + + /// Converts the value into its inner handle. + /// + /// This is a convenience function which simply forwards to the `Into` trait. + fn into_handle(self) -> Handle { + self.into() + } + + /// Converts the handle into it's raw representation. + /// + /// The caller takes ownership over the raw handle, and must close or transfer it to avoid a handle leak. + fn into_raw(self) -> sys::zx_handle_t { + let h = self.into_handle(); + let r = h.0; + mem::forget(h); + r + } + + /// Creates an instance of this type from a handle. + /// + /// This is a convenience function which simply forwards to the `From` trait. + fn from_handle(handle: Handle) -> Self { + Self::from(handle) + } + + /// Creates an instance of another handle-based type from this value's inner handle. + fn into_handle_based<H: HandleBased>(self) -> H { + H::from_handle(self.into_handle()) + } + + /// Creates an instance of this type from the inner handle of another + /// handle-based type. + fn from_handle_based<H: HandleBased>(h: H) -> Self { + Self::from_handle(h.into_handle()) + } +} + +/// A trait implemented by all handles for objects which have a peer. +pub trait Peered: HandleBased { + /// Set and clear userspace-accessible signal bits on the object's peer. Wraps the + /// [zx_object_signal_peer](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/object_signal.md) + /// syscall. + fn signal_peer(&self, clear_mask: Signals, set_mask: Signals) -> Result<(), Status> { + let handle = self.as_handle_ref().handle; + let status = unsafe { + sys::zx_object_signal_peer(handle, clear_mask.bits(), set_mask.bits()) + }; + ok(status) + } +} + +/// A trait implemented by all handles for objects which can have a cookie attached. +pub trait Cookied: HandleBased { + /// Get the cookie attached to this object, if any. Wraps the + /// [zx_object_get_cookie](https://fuchsia.googlesource.com/zircon/+/HEAD/docs/syscalls/object_get_cookie.md) + /// syscall. + fn get_cookie(&self, scope: &HandleRef) -> Result<u64, Status> { + let handle = self.as_handle_ref().handle; + let mut cookie = 0; + let status = unsafe { sys::zx_object_get_cookie(handle, scope.handle, &mut cookie) }; + ok(status).map(|()| cookie) + } + + /// Attach an opaque cookie to this object with the given scope. The cookie may be read or + /// changed in future only with the same scope. Wraps the + /// [zx_object_set_cookie](https://fuchsia.googlesource.com/zircon/+/HEAD/docs/syscalls/object_set_cookie.md) + /// syscall. + fn set_cookie(&self, scope: &HandleRef, cookie: u64) -> Result<(), Status> { + let handle = self.as_handle_ref().handle; + let status = unsafe { sys::zx_object_set_cookie(handle, scope.handle, cookie) }; + ok(status) + } +} diff --git a/third_party/rust/fuchsia-zircon/src/job.rs b/third_party/rust/fuchsia-zircon/src/job.rs new file mode 100644 index 0000000000..1bb1ef274d --- /dev/null +++ b/third_party/rust/fuchsia-zircon/src/job.rs @@ -0,0 +1,14 @@ +// 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 job. + +use {AsHandleRef, HandleBased, Handle, HandleRef}; + +/// An object representing a Zircon job. +/// +/// As essentially a subtype of `Handle`, it can be freely interconverted. +#[derive(Debug, Eq, PartialEq)] +pub struct Job(Handle); +impl_handle_based!(Job);
\ No newline at end of file diff --git a/third_party/rust/fuchsia-zircon/src/lib.rs b/third_party/rust/fuchsia-zircon/src/lib.rs new file mode 100644 index 0000000000..26444402cc --- /dev/null +++ b/third_party/rust/fuchsia-zircon/src/lib.rs @@ -0,0 +1,365 @@ +// Copyright 2016 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 kernel +//! [syscalls](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls.md). + +#![deny(warnings)] + +#[macro_use] +extern crate bitflags; + +pub extern crate fuchsia_zircon_sys as sys; + +#[deprecated(note="use fuchsia_zircon::sys::ZX_CPRNG_DRAW_MAX_LEN instead")] +#[doc(hidden)] +pub use sys::ZX_CPRNG_DRAW_MAX_LEN; + +// Implements the HandleBased traits for a Handle newtype struct +macro_rules! impl_handle_based { + ($type_name:path) => { + impl AsHandleRef for $type_name { + fn as_handle_ref(&self) -> HandleRef { + self.0.as_handle_ref() + } + } + + impl From<Handle> for $type_name { + fn from(handle: Handle) -> Self { + $type_name(handle) + } + } + + impl From<$type_name> for Handle { + fn from(x: $type_name) -> Handle { + x.0 + } + } + + impl HandleBased for $type_name {} + } +} + +// Creates associated constants of TypeName of the form +// `pub const NAME: TypeName = TypeName(value);` +macro_rules! assoc_consts { + ($typename:ident, [$($name:ident = $num:expr;)*]) => { + #[allow(non_upper_case_globals)] + impl $typename { + $( + pub const $name: $typename = $typename($num); + )* + } + } +} + +mod channel; +mod cprng; +mod event; +mod eventpair; +mod fifo; +mod handle; +mod job; +mod port; +mod process; +mod rights; +mod socket; +mod signals; +mod status; +mod time; +mod thread; +mod vmar; +mod vmo; + +pub use channel::*; +pub use cprng::*; +pub use event::*; +pub use eventpair::*; +pub use fifo::*; +pub use handle::*; +pub use job::*; +pub use port::*; +pub use process::*; +pub use rights::*; +pub use socket::*; +pub use signals::*; +pub use status::*; +pub use thread::*; +pub use time::*; +pub use vmar::*; +pub use vmo::*; + +/// Prelude containing common utility traits. +/// Designed for use like `use fuchsia_zircon::prelude::*;` +pub mod prelude { + pub use { + AsHandleRef, + Cookied, + DurationNum, + HandleBased, + Peered, + }; +} + +/// Convenience re-export of `Status::ok`. +pub fn ok(raw: sys::zx_status_t) -> Result<(), Status> { + Status::ok(raw) +} + +/// A "wait item" containing a handle reference and information about what signals +/// to wait on, and, on return from `object_wait_many`, which are pending. +#[repr(C)] +#[derive(Debug)] +pub struct WaitItem<'a> { + /// The handle to wait on. + pub handle: HandleRef<'a>, + /// A set of signals to wait for. + pub waitfor: Signals, + /// The set of signals pending, on return of `object_wait_many`. + pub pending: Signals, +} + +/// An identifier to select a particular clock. See +/// [zx_time_get](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/time_get.md) +/// for more information about the possible values. +#[repr(u32)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum ClockId { + /// The number of nanoseconds since the system was powered on. Corresponds to + /// `ZX_CLOCK_MONOTONIC`. + Monotonic = 0, + /// The number of wall clock nanoseconds since the Unix epoch (midnight on January 1 1970) in + /// UTC. Corresponds to ZX_CLOCK_UTC. + UTC = 1, + /// The number of nanoseconds the current thread has been running for. Corresponds to + /// ZX_CLOCK_THREAD. + Thread = 2, +} + +/// Wait on multiple handles. +/// The success return value is a bool indicating whether one or more of the +/// provided handle references was closed during the wait. +/// +/// Wraps the +/// [zx_object_wait_many](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/object_wait_many.md) +/// syscall. +pub fn object_wait_many(items: &mut [WaitItem], deadline: Time) -> Result<bool, Status> +{ + let len = try!(usize_into_u32(items.len()).map_err(|_| Status::OUT_OF_RANGE)); + let items_ptr = items.as_mut_ptr() as *mut sys::zx_wait_item_t; + let status = unsafe { sys::zx_object_wait_many( items_ptr, len, deadline.nanos()) }; + if status == sys::ZX_ERR_CANCELED { + return Ok(true) + } + ok(status).map(|()| false) +} + +#[cfg(test)] +mod tests { + use super::*; + #[allow(unused_imports)] + use super::prelude::*; + + #[test] + fn monotonic_time_increases() { + let time1 = Time::get(ClockId::Monotonic); + 1_000.nanos().sleep(); + let time2 = Time::get(ClockId::Monotonic); + assert!(time2 > time1); + } + + #[test] + fn utc_time_increases() { + let time1 = Time::get(ClockId::UTC); + 1_000.nanos().sleep(); + let time2 = Time::get(ClockId::UTC); + assert!(time2 > time1); + } + + #[test] + fn thread_time_increases() { + let time1 = Time::get(ClockId::Thread); + 1_000.nanos().sleep(); + let time2 = Time::get(ClockId::Thread); + assert!(time2 > time1); + } + + #[test] + fn ticks_increases() { + let ticks1 = ticks_get(); + 1_000.nanos().sleep(); + let ticks2 = ticks_get(); + assert!(ticks2 > ticks1); + } + + #[test] + fn tick_length() { + let sleep_time = 1.milli(); + let ticks1 = ticks_get(); + sleep_time.sleep(); + let ticks2 = ticks_get(); + + // The number of ticks should have increased by at least 1 ms worth + let sleep_ticks = sleep_time.millis() * ticks_per_second() / 1000; + assert!(ticks2 >= (ticks1 + sleep_ticks)); + } + + #[test] + fn into_raw() { + let vmo = Vmo::create(1).unwrap(); + let h = vmo.into_raw(); + let vmo2 = Vmo::from(unsafe { Handle::from_raw(h) }); + assert!(vmo2.write(b"1", 0).is_ok()); + } + + #[test] + fn sleep() { + let sleep_ns = 1.millis(); + let time1 = Time::get(ClockId::Monotonic); + sleep_ns.sleep(); + let time2 = Time::get(ClockId::Monotonic); + assert!(time2 > time1 + sleep_ns); + } + + /// Test duplication by means of a VMO + #[test] + fn duplicate() { + let hello_length: usize = 5; + + // Create a VMO and write some data to it. + let vmo = Vmo::create(hello_length as u64).unwrap(); + assert!(vmo.write(b"hello", 0).is_ok()); + + // Replace, reducing rights to read. + let readonly_vmo = vmo.duplicate_handle(Rights::READ).unwrap(); + // Make sure we can read but not write. + let mut read_vec = vec![0; hello_length]; + assert_eq!(readonly_vmo.read(&mut read_vec, 0).unwrap(), hello_length); + assert_eq!(read_vec, b"hello"); + assert_eq!(readonly_vmo.write(b"", 0), Err(Status::ACCESS_DENIED)); + + // Write new data to the original handle, and read it from the new handle + assert!(vmo.write(b"bye", 0).is_ok()); + assert_eq!(readonly_vmo.read(&mut read_vec, 0).unwrap(), hello_length); + assert_eq!(read_vec, b"byelo"); + } + + // Test replace by means of a VMO + #[test] + fn replace() { + let hello_length: usize = 5; + + // Create a VMO and write some data to it. + let vmo = Vmo::create(hello_length as u64).unwrap(); + assert!(vmo.write(b"hello", 0).is_ok()); + + // Replace, reducing rights to read. + let readonly_vmo = vmo.replace_handle(Rights::READ).unwrap(); + // Make sure we can read but not write. + let mut read_vec = vec![0; hello_length]; + assert_eq!(readonly_vmo.read(&mut read_vec, 0).unwrap(), hello_length); + assert_eq!(read_vec, b"hello"); + assert_eq!(readonly_vmo.write(b"", 0), Err(Status::ACCESS_DENIED)); + } + + #[test] + fn wait_and_signal() { + let event = Event::create().unwrap(); + let ten_ms = 10.millis(); + + // Waiting on it without setting any signal should time out. + assert_eq!(event.wait_handle( + Signals::USER_0, ten_ms.after_now()), Err(Status::TIMED_OUT)); + + // If we set a signal, we should be able to wait for it. + assert!(event.signal_handle(Signals::NONE, Signals::USER_0).is_ok()); + assert_eq!(event.wait_handle(Signals::USER_0, ten_ms.after_now()).unwrap(), + Signals::USER_0); + + // Should still work, signals aren't automatically cleared. + assert_eq!(event.wait_handle(Signals::USER_0, ten_ms.after_now()).unwrap(), + Signals::USER_0); + + // Now clear it, and waiting should time out again. + assert!(event.signal_handle(Signals::USER_0, Signals::NONE).is_ok()); + assert_eq!(event.wait_handle( + Signals::USER_0, ten_ms.after_now()), Err(Status::TIMED_OUT)); + } + + #[test] + fn wait_many_and_signal() { + let ten_ms = 10.millis(); + let e1 = Event::create().unwrap(); + let e2 = Event::create().unwrap(); + + // Waiting on them now should time out. + let mut items = vec![ + WaitItem { handle: e1.as_handle_ref(), waitfor: Signals::USER_0, pending: Signals::NONE }, + WaitItem { handle: e2.as_handle_ref(), waitfor: Signals::USER_1, pending: Signals::NONE }, + ]; + assert_eq!(object_wait_many(&mut items, ten_ms.after_now()), Err(Status::TIMED_OUT)); + assert_eq!(items[0].pending, Signals::NONE); + assert_eq!(items[1].pending, Signals::NONE); + + // Signal one object and it should return success. + assert!(e1.signal_handle(Signals::NONE, Signals::USER_0).is_ok()); + assert!(object_wait_many(&mut items, ten_ms.after_now()).is_ok()); + assert_eq!(items[0].pending, Signals::USER_0); + assert_eq!(items[1].pending, Signals::NONE); + + // Signal the other and it should return both. + assert!(e2.signal_handle(Signals::NONE, Signals::USER_1).is_ok()); + assert!(object_wait_many(&mut items, ten_ms.after_now()).is_ok()); + assert_eq!(items[0].pending, Signals::USER_0); + assert_eq!(items[1].pending, Signals::USER_1); + + // Clear signals on both; now it should time out again. + assert!(e1.signal_handle(Signals::USER_0, Signals::NONE).is_ok()); + assert!(e2.signal_handle(Signals::USER_1, Signals::NONE).is_ok()); + assert_eq!(object_wait_many(&mut items, ten_ms.after_now()), Err(Status::TIMED_OUT)); + assert_eq!(items[0].pending, Signals::NONE); + assert_eq!(items[1].pending, Signals::NONE); + } + + #[test] + fn cookies() { + let event = Event::create().unwrap(); + let scope = Event::create().unwrap(); + + // Getting a cookie when none has been set should fail. + assert_eq!(event.get_cookie(&scope.as_handle_ref()), Err(Status::ACCESS_DENIED)); + + // Set a cookie. + assert_eq!(event.set_cookie(&scope.as_handle_ref(), 42), Ok(())); + + // Should get it back.... + assert_eq!(event.get_cookie(&scope.as_handle_ref()), Ok(42)); + + // but not with the wrong scope! + assert_eq!(event.get_cookie(&event.as_handle_ref()), Err(Status::ACCESS_DENIED)); + + // Can change it, with the same scope... + assert_eq!(event.set_cookie(&scope.as_handle_ref(), 123), Ok(())); + + // but not with a different scope. + assert_eq!(event.set_cookie(&event.as_handle_ref(), 123), Err(Status::ACCESS_DENIED)); + } +} + +pub fn usize_into_u32(n: usize) -> Result<u32, ()> { + if n > ::std::u32::MAX as usize || n < ::std::u32::MIN as usize { + return Err(()) + } + Ok(n as u32) +} + +pub fn size_to_u32_sat(n: usize) -> u32 { + if n > ::std::u32::MAX as usize { + return ::std::u32::MAX; + } + if n < ::std::u32::MIN as usize { + return ::std::u32::MIN; + } + n as u32 +} diff --git a/third_party/rust/fuchsia-zircon/src/port.rs b/third_party/rust/fuchsia-zircon/src/port.rs new file mode 100644 index 0000000000..6a9e8a8f7f --- /dev/null +++ b/third_party/rust/fuchsia-zircon/src/port.rs @@ -0,0 +1,344 @@ +// 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 port objects. + +use std::mem; + +use {AsHandleRef, HandleBased, Handle, HandleRef, Signals, Status, Time}; +use {sys, ok}; + +/// An object representing a Zircon +/// [port](https://fuchsia.googlesource.com/zircon/+/master/docs/objects/port.md). +/// +/// As essentially a subtype of `Handle`, it can be freely interconverted. +#[derive(Debug, Eq, PartialEq)] +pub struct Port(Handle); +impl_handle_based!(Port); + +/// A packet sent through a port. This is a type-safe wrapper for +/// [zx_port_packet_t](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/port_wait.md). +#[derive(PartialEq, Eq, Debug)] +pub struct Packet(sys::zx_port_packet_t); + +/// The contents of a `Packet`. +#[derive(Debug, Copy, Clone)] +pub enum PacketContents { + /// A user-generated packet. + User(UserPacket), + /// A one-shot signal packet generated via `object_wait_async`. + SignalOne(SignalPacket), + /// A repeating signal packet generated via `object_wait_async`. + SignalRep(SignalPacket), + + #[doc(hidden)] + __Nonexhaustive +} + +/// Contents of a user packet (one sent by `port_queue`). This is a type-safe wrapper for +/// [zx_packet_user_t](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/port_wait.md). +#[derive(Debug, Copy, Clone)] +pub struct UserPacket(sys::zx_packet_user_t); + +/// Contents of a signal packet (one generated by the kernel). This is a type-safe wrapper for +/// [zx_packet_signal_t](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/port_wait.md). +#[derive(Debug, Copy, Clone)] +pub struct SignalPacket(sys::zx_packet_signal_t); + +impl Packet { + /// Creates a new packet with `UserPacket` data. + pub fn from_user_packet(key: u64, status: i32, user: UserPacket) -> Packet { + Packet( + sys::zx_port_packet_t { + key: key, + packet_type: sys::zx_packet_type_t::ZX_PKT_TYPE_USER, + status: status, + union: user.0, + } + ) + } + + /// The packet's key. + pub fn key(&self) -> u64 { + self.0.key + } + + /// The packet's status. + // TODO: should this type be wrapped? + pub fn status(&self) -> i32 { + self.0.status + } + + /// The contents of the packet. + pub fn contents(&self) -> PacketContents { + if self.0.packet_type == sys::zx_packet_type_t::ZX_PKT_TYPE_USER { + PacketContents::User(UserPacket(self.0.union)) + } else if self.0.packet_type == sys::zx_packet_type_t::ZX_PKT_TYPE_SIGNAL_ONE { + PacketContents::SignalOne(SignalPacket(unsafe { mem::transmute_copy(&self.0.union) })) + } else if self.0.packet_type == sys::zx_packet_type_t::ZX_PKT_TYPE_SIGNAL_REP { + PacketContents::SignalRep(SignalPacket(unsafe { mem::transmute_copy(&self.0.union) })) + } else { + panic!("unexpected packet type"); + } + } +} + +impl UserPacket { + pub fn from_u8_array(val: [u8; 32]) -> UserPacket { + UserPacket(val) + } + + pub fn as_u8_array(&self) -> &[u8; 32] { + &self.0 + } + + pub fn as_mut_u8_array(&mut self) -> &mut [u8; 32] { + &mut self.0 + } +} + +impl SignalPacket { + /// The signals used in the call to `object_wait_async`. + pub fn trigger(&self) -> Signals { + Signals::from_bits_truncate(self.0.trigger) + } + + /// The observed signals. + pub fn observed(&self) -> Signals { + Signals::from_bits_truncate(self.0.observed) + } + + /// A per object count of pending operations. + pub fn count(&self) -> u64 { + self.0.count + } +} + +impl Port { + /// Create an IO port, allowing IO packets to be read and enqueued. + /// + /// Wraps the + /// [zx_port_create](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/port_create.md) + /// syscall. + pub fn create() -> Result<Port, Status> { + unsafe { + let mut handle = 0; + let opts = 0; + let status = sys::zx_port_create(opts, &mut handle); + ok(status)?; + Ok(Handle::from_raw(handle).into()) + } + } + + /// Attempt to queue a user packet to the IO port. + /// + /// Wraps the + /// [zx_port_queue](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/port_queue.md) + /// syscall. + pub fn queue(&self, packet: &Packet) -> Result<(), Status> { + let status = unsafe { + sys::zx_port_queue(self.raw_handle(), + &packet.0 as *const sys::zx_port_packet_t, 0) + }; + ok(status) + } + + /// Wait for a packet to arrive on a (V2) port. + /// + /// Wraps the + /// [zx_port_wait](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/port_wait.md) + /// syscall. + pub fn wait(&self, deadline: Time) -> Result<Packet, Status> { + let mut packet = Default::default(); + let status = unsafe { + sys::zx_port_wait(self.raw_handle(), deadline.nanos(), + &mut packet as *mut sys::zx_port_packet_t, 0) + }; + ok(status)?; + Ok(Packet(packet)) + } + + /// Cancel pending wait_async calls for an object with the given key. + /// + /// Wraps the + /// [zx_port_cancel](https://fuchsia.googlesource.com/zircon/+/HEAD/docs/syscalls/port_cancel.md) + /// syscall. + pub fn cancel<H>(&self, source: &H, key: u64) -> Result<(), Status> where H: HandleBased { + let status = unsafe { + sys::zx_port_cancel(self.raw_handle(), source.raw_handle(), key) + }; + ok(status) + } +} + +/// Options for wait_async. +#[repr(u32)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum WaitAsyncOpts { + Once = sys::ZX_WAIT_ASYNC_ONCE, + Repeating = sys::ZX_WAIT_ASYNC_REPEATING, +} + +#[cfg(test)] +mod tests { + use super::*; + use {DurationNum, Event}; + + #[test] + fn port_basic() { + let ten_ms = 10.millis(); + + let port = Port::create().unwrap(); + + // Waiting now should time out. + assert_eq!(port.wait(ten_ms.after_now()), Err(Status::TIMED_OUT)); + + // Send a valid packet. + let packet = Packet::from_user_packet( + 42, + 123, + UserPacket::from_u8_array([13; 32]), + ); + assert!(port.queue(&packet).is_ok()); + + // Waiting should succeed this time. We should get back the packet we sent. + let read_packet = port.wait(ten_ms.after_now()).unwrap(); + assert_eq!(read_packet, packet); + } + + #[test] + fn wait_async_once() { + let ten_ms = 10.millis(); + let key = 42; + + let port = Port::create().unwrap(); + let event = Event::create().unwrap(); + + assert!(event.wait_async_handle(&port, key, Signals::USER_0 | Signals::USER_1, + WaitAsyncOpts::Once).is_ok()); + + // Waiting without setting any signal should time out. + assert_eq!(port.wait(ten_ms.after_now()), Err(Status::TIMED_OUT)); + + // If we set a signal, we should be able to wait for it. + assert!(event.signal_handle(Signals::NONE, Signals::USER_0).is_ok()); + let read_packet = port.wait(ten_ms.after_now()).unwrap(); + assert_eq!(read_packet.key(), key); + assert_eq!(read_packet.status(), 0); + match read_packet.contents() { + PacketContents::SignalOne(sig) => { + assert_eq!(sig.trigger(), Signals::USER_0 | Signals::USER_1); + assert_eq!(sig.observed(), Signals::USER_0); + assert_eq!(sig.count(), 1); + } + _ => panic!("wrong packet type"), + } + + // Shouldn't get any more packets. + assert_eq!(port.wait(ten_ms.after_now()), Err(Status::TIMED_OUT)); + + // Calling wait_async again should result in another packet. + assert!(event.wait_async_handle(&port, key, Signals::USER_0, WaitAsyncOpts::Once).is_ok()); + let read_packet = port.wait(ten_ms.after_now()).unwrap(); + assert_eq!(read_packet.key(), key); + assert_eq!(read_packet.status(), 0); + match read_packet.contents() { + PacketContents::SignalOne(sig) => { + assert_eq!(sig.trigger(), Signals::USER_0); + assert_eq!(sig.observed(), Signals::USER_0); + assert_eq!(sig.count(), 1); + } + _ => panic!("wrong packet type"), + } + + // Calling wait_async_handle then cancel, we should not get a packet as cancel will + // remove it from the queue. + assert!(event.wait_async_handle(&port, key, Signals::USER_0, WaitAsyncOpts::Once).is_ok()); + assert!(port.cancel(&event, key).is_ok()); + assert_eq!(port.wait(ten_ms.after_now()), Err(Status::TIMED_OUT)); + + // If the event is signalled after the cancel, we also shouldn't get a packet. + assert!(event.signal_handle(Signals::USER_0, Signals::NONE).is_ok()); // clear signal + assert!(event.wait_async_handle(&port, key, Signals::USER_0, WaitAsyncOpts::Once).is_ok()); + assert!(port.cancel(&event, key).is_ok()); + assert!(event.signal_handle(Signals::NONE, Signals::USER_0).is_ok()); + assert_eq!(port.wait(ten_ms.after_now()), Err(Status::TIMED_OUT)); + } + + #[test] + fn wait_async_repeating() { + let ten_ms = 10.millis(); + let key = 42; + + let port = Port::create().unwrap(); + let event = Event::create().unwrap(); + + assert!(event.wait_async_handle(&port, key, Signals::USER_0 | Signals::USER_1, + WaitAsyncOpts::Repeating).is_ok()); + + // Waiting without setting any signal should time out. + assert_eq!(port.wait(ten_ms.after_now()), Err(Status::TIMED_OUT)); + + // If we set a signal, we should be able to wait for it. + assert!(event.signal_handle(Signals::NONE, Signals::USER_0).is_ok()); + let read_packet = port.wait(ten_ms.after_now()).unwrap(); + assert_eq!(read_packet.key(), key); + assert_eq!(read_packet.status(), 0); + match read_packet.contents() { + PacketContents::SignalRep(sig) => { + assert_eq!(sig.trigger(), Signals::USER_0 | Signals::USER_1); + assert_eq!(sig.observed(), Signals::USER_0); + assert_eq!(sig.count(), 1); + } + _ => panic!("wrong packet type"), + } + + // Should not get any more packets, as ZX_WAIT_ASYNC_REPEATING is edge triggered rather than + // level triggered. + assert_eq!(port.wait(ten_ms.after_now()), Err(Status::TIMED_OUT)); + + // If we clear and resignal, we should get the same packet again, + // even though we didn't call event.wait_async again. + assert!(event.signal_handle(Signals::USER_0, Signals::NONE).is_ok()); // clear signal + assert!(event.signal_handle(Signals::NONE, Signals::USER_0).is_ok()); + let read_packet = port.wait(ten_ms.after_now()).unwrap(); + assert_eq!(read_packet.key(), key); + assert_eq!(read_packet.status(), 0); + match read_packet.contents() { + PacketContents::SignalRep(sig) => { + assert_eq!(sig.trigger(), Signals::USER_0 | Signals::USER_1); + assert_eq!(sig.observed(), Signals::USER_0); + assert_eq!(sig.count(), 1); + } + _ => panic!("wrong packet type"), + } + + // Cancelling the wait should stop us getting packets... + assert!(port.cancel(&event, key).is_ok()); + assert_eq!(port.wait(ten_ms.after_now()), Err(Status::TIMED_OUT)); + // ... even if we clear and resignal + assert!(event.signal_handle(Signals::USER_0, Signals::NONE).is_ok()); // clear signal + assert!(event.signal_handle(Signals::NONE, Signals::USER_0).is_ok()); + assert_eq!(port.wait(ten_ms.after_now()), Err(Status::TIMED_OUT)); + + // Calling wait_async again should result in another packet. + assert!(event.wait_async_handle( + &port, key, Signals::USER_0, WaitAsyncOpts::Repeating).is_ok()); + let read_packet = port.wait(ten_ms.after_now()).unwrap(); + assert_eq!(read_packet.key(), key); + assert_eq!(read_packet.status(), 0); + match read_packet.contents() { + PacketContents::SignalRep(sig) => { + assert_eq!(sig.trigger(), Signals::USER_0); + assert_eq!(sig.observed(), Signals::USER_0); + assert_eq!(sig.count(), 1); + } + _ => panic!("wrong packet type"), + } + + // Closing the handle should stop us getting packets. + drop(event); + assert_eq!(port.wait(ten_ms.after_now()), Err(Status::TIMED_OUT)); + } +} diff --git a/third_party/rust/fuchsia-zircon/src/process.rs b/third_party/rust/fuchsia-zircon/src/process.rs new file mode 100644 index 0000000000..b46f1bdd23 --- /dev/null +++ b/third_party/rust/fuchsia-zircon/src/process.rs @@ -0,0 +1,14 @@ +// 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 process. + +use {AsHandleRef, HandleBased, Handle, HandleRef}; + +/// An object representing a Zircon process. +/// +/// As essentially a subtype of `Handle`, it can be freely interconverted. +#[derive(Debug, Eq, PartialEq)] +pub struct Process(Handle); +impl_handle_based!(Process);
\ No newline at end of file diff --git a/third_party/rust/fuchsia-zircon/src/rights.rs b/third_party/rust/fuchsia-zircon/src/rights.rs new file mode 100644 index 0000000000..a41ad12f54 --- /dev/null +++ b/third_party/rust/fuchsia-zircon/src/rights.rs @@ -0,0 +1,28 @@ +use sys; + +bitflags! { + /// Rights associated with a handle. + /// + /// See [rights.md](https://fuchsia.googlesource.com/zircon/+/master/docs/rights.md) + /// for more information. + #[repr(C)] + pub struct Rights: sys::zx_rights_t { + const NONE = sys::ZX_RIGHT_NONE; + const DUPLICATE = sys::ZX_RIGHT_DUPLICATE; + const TRANSFER = sys::ZX_RIGHT_TRANSFER; + const READ = sys::ZX_RIGHT_READ; + const WRITE = sys::ZX_RIGHT_WRITE; + const EXECUTE = sys::ZX_RIGHT_EXECUTE; + const MAP = sys::ZX_RIGHT_MAP; + const GET_PROPERTY = sys::ZX_RIGHT_GET_PROPERTY; + const SET_PROPERTY = sys::ZX_RIGHT_SET_PROPERTY; + const ENUMERATE = sys::ZX_RIGHT_ENUMERATE; + const DESTROY = sys::ZX_RIGHT_DESTROY; + const SET_POLICY = sys::ZX_RIGHT_SET_POLICY; + const GET_POLICY = sys::ZX_RIGHT_GET_POLICY; + const SIGNAL = sys::ZX_RIGHT_SIGNAL; + const SIGNAL_PEER = sys::ZX_RIGHT_SIGNAL_PEER; + const WAIT = sys::ZX_RIGHT_WAIT; + const SAME_RIGHTS = sys::ZX_RIGHT_SAME_RIGHTS; + } +}
\ No newline at end of file diff --git a/third_party/rust/fuchsia-zircon/src/signals.rs b/third_party/rust/fuchsia-zircon/src/signals.rs new file mode 100644 index 0000000000..e5189f5ebc --- /dev/null +++ b/third_party/rust/fuchsia-zircon/src/signals.rs @@ -0,0 +1,105 @@ +use sys::*; + +bitflags! { + /// Signals that can be waited upon. + /// + /// See + /// [Objects and signals](https://fuchsia.googlesource.com/zircon/+/master/docs/concepts.md#Objects-and-Signals) + /// in the Zircon kernel documentation. Note: the names of signals are still in flux. + #[repr(C)] + pub struct Signals: zx_signals_t { + const NONE = ZX_SIGNAL_NONE; + const OBJECT_ALL = ZX_OBJECT_SIGNAL_ALL; + const USER_ALL = ZX_USER_SIGNAL_ALL; + const OBJECT_0 = ZX_OBJECT_SIGNAL_0; + const OBJECT_1 = ZX_OBJECT_SIGNAL_1; + const OBJECT_2 = ZX_OBJECT_SIGNAL_2; + const OBJECT_3 = ZX_OBJECT_SIGNAL_3; + const OBJECT_4 = ZX_OBJECT_SIGNAL_4; + const OBJECT_5 = ZX_OBJECT_SIGNAL_5; + const OBJECT_6 = ZX_OBJECT_SIGNAL_6; + const OBJECT_7 = ZX_OBJECT_SIGNAL_7; + const OBJECT_8 = ZX_OBJECT_SIGNAL_8; + const OBJECT_9 = ZX_OBJECT_SIGNAL_9; + const OBJECT_10 = ZX_OBJECT_SIGNAL_10; + const OBJECT_11 = ZX_OBJECT_SIGNAL_11; + const OBJECT_12 = ZX_OBJECT_SIGNAL_12; + const OBJECT_13 = ZX_OBJECT_SIGNAL_13; + const OBJECT_14 = ZX_OBJECT_SIGNAL_14; + const OBJECT_15 = ZX_OBJECT_SIGNAL_15; + const OBJECT_16 = ZX_OBJECT_SIGNAL_16; + const OBJECT_17 = ZX_OBJECT_SIGNAL_17; + const OBJECT_18 = ZX_OBJECT_SIGNAL_18; + const OBJECT_19 = ZX_OBJECT_SIGNAL_19; + const OBJECT_20 = ZX_OBJECT_SIGNAL_20; + const OBJECT_21 = ZX_OBJECT_SIGNAL_21; + const OBJECT_22 = ZX_OBJECT_SIGNAL_22; + const OBJECT_HANDLE_CLOSED = ZX_OBJECT_HANDLE_CLOSED; + const USER_0 = ZX_USER_SIGNAL_0; + const USER_1 = ZX_USER_SIGNAL_1; + const USER_2 = ZX_USER_SIGNAL_2; + const USER_3 = ZX_USER_SIGNAL_3; + const USER_4 = ZX_USER_SIGNAL_4; + const USER_5 = ZX_USER_SIGNAL_5; + const USER_6 = ZX_USER_SIGNAL_6; + const USER_7 = ZX_USER_SIGNAL_7; + + const OBJECT_READABLE = ZX_OBJECT_READABLE; + const OBJECT_WRITABLE = ZX_OBJECT_WRITABLE; + const OBJECT_PEER_CLOSED = ZX_OBJECT_PEER_CLOSED; + + // Cancelation (handle was closed while waiting with it) + const HANDLE_CLOSED = ZX_SIGNAL_HANDLE_CLOSED; + + // Event + const EVENT_SIGNALED = ZX_EVENT_SIGNALED; + + // EventPair + const EVENT_PAIR_SIGNALED = ZX_EPAIR_SIGNALED; + const EVENT_PAIR_CLOSED = ZX_EPAIR_CLOSED; + + // Task signals (process, thread, job) + const TASK_TERMINATED = ZX_TASK_TERMINATED; + + // Channel + const CHANNEL_READABLE = ZX_CHANNEL_READABLE; + const CHANNEL_WRITABLE = ZX_CHANNEL_WRITABLE; + const CHANNEL_PEER_CLOSED = ZX_CHANNEL_PEER_CLOSED; + + // Socket + const SOCKET_READABLE = ZX_SOCKET_READABLE; + const SOCKET_WRITABLE = ZX_SOCKET_WRITABLE; + const SOCKET_PEER_CLOSED = ZX_SOCKET_PEER_CLOSED; + + // Port + const PORT_READABLE = ZX_PORT_READABLE; + + // Resource + const RESOURCE_DESTROYED = ZX_RESOURCE_DESTROYED; + const RESOURCE_READABLE = ZX_RESOURCE_READABLE; + const RESOURCE_WRITABLE = ZX_RESOURCE_WRITABLE; + const RESOURCE_CHILD_ADDED = ZX_RESOURCE_CHILD_ADDED; + + // Fifo + const FIFO_READABLE = ZX_FIFO_READABLE; + const FIFO_WRITABLE = ZX_FIFO_WRITABLE; + const FIFO_PEER_CLOSED = ZX_FIFO_PEER_CLOSED; + + // Job + const JOB_NO_PROCESSES = ZX_JOB_NO_PROCESSES; + const JOB_NO_JOBS = ZX_JOB_NO_JOBS; + + // Process + const PROCESS_TERMINATED = ZX_PROCESS_TERMINATED; + + // Thread + const THREAD_TERMINATED = ZX_THREAD_TERMINATED; + + // Log + const LOG_READABLE = ZX_LOG_READABLE; + const LOG_WRITABLE = ZX_LOG_WRITABLE; + + // Timer + const TIMER_SIGNALED = ZX_TIMER_SIGNALED; + } +}
\ No newline at end of file diff --git a/third_party/rust/fuchsia-zircon/src/socket.rs b/third_party/rust/fuchsia-zircon/src/socket.rs new file mode 100644 index 0000000000..c93e98cb73 --- /dev/null +++ b/third_party/rust/fuchsia-zircon/src/socket.rs @@ -0,0 +1,126 @@ +// Copyright 2016 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 sockets. + +use {AsHandleRef, HandleBased, Handle, HandleRef, Peered}; +use {sys, Status, ok}; + +use std::ptr; + +/// An object representing a Zircon +/// [socket](https://fuchsia.googlesource.com/zircon/+/master/docs/concepts.md#Message-Passing_Sockets-and-Channels). +/// +/// As essentially a subtype of `Handle`, it can be freely interconverted. +#[derive(Debug, Eq, PartialEq)] +pub struct Socket(Handle); +impl_handle_based!(Socket); +impl Peered for Socket {} + +impl Socket { + /// Create a socket, accessed through a pair of endpoints. Data written + /// into one may be read from the other. + /// + /// Wraps + /// [zx_socket_create](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/socket_create.md). + pub fn create() -> Result<(Socket, Socket), Status> { + unsafe { + let mut out0 = 0; + let mut out1 = 0; + let opts = 0; + let status = sys::zx_socket_create(opts, &mut out0, &mut out1); + ok(status)?; + Ok(( + Self::from(Handle::from_raw(out0)), + Self::from(Handle::from_raw(out1)) + )) + } + } + + /// Write the given bytes into the socket. + /// Return value (on success) is number of bytes actually written. + /// + /// Wraps + /// [zx_socket_write](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/socket_write.md). + pub fn write(&self, bytes: &[u8]) -> Result<usize, Status> { + let mut actual = 0; + let opts = 0; + let status = unsafe { + sys::zx_socket_write(self.raw_handle(), opts, bytes.as_ptr(), bytes.len(), + &mut actual) + }; + ok(status).map(|()| actual) + } + + /// Read the given bytes from the socket. + /// Return value (on success) is number of bytes actually read. + /// + /// Wraps + /// [zx_socket_read](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/socket_read.md). + pub fn read(&self, bytes: &mut [u8]) -> Result<usize, Status> { + let mut actual = 0; + let opts = 0; + let status = unsafe { + sys::zx_socket_read(self.raw_handle(), opts, bytes.as_mut_ptr(), + bytes.len(), &mut actual) + }; + ok(status) + .map(|()| actual) + .map_err(|status| { + // If an error is returned then actual is undefined, so to be safe + // we set it to 0 and ignore any data that is set in bytes. + actual = 0; + status + }) + } + + /// Close half of the socket, so attempts by the other side to write will fail. + /// + /// Implements the `ZX_SOCKET_HALF_CLOSE` option of + /// [zx_socket_write](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/socket_write.md). + pub fn half_close(&self) -> Result<(), Status> { + let status = unsafe { sys::zx_socket_write(self.raw_handle(), sys::ZX_SOCKET_HALF_CLOSE, + ptr::null(), 0, ptr::null_mut()) }; + ok(status) + } + + pub fn outstanding_read_bytes(&self) -> Result<usize, Status> { + let mut outstanding = 0; + let status = unsafe { + sys::zx_socket_read(self.raw_handle(), 0, ptr::null_mut(), 0, &mut outstanding) + }; + ok(status).map(|()| outstanding) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn socket_basic() { + let (s1, s2) = Socket::create().unwrap(); + + // Write in one end and read it back out the other. + assert_eq!(s1.write(b"hello").unwrap(), 5); + + let mut read_vec = vec![0; 8]; + assert_eq!(s2.read(&mut read_vec).unwrap(), 5); + assert_eq!(&read_vec[0..5], b"hello"); + + // Try reading when there is nothing to read. + assert_eq!(s2.read(&mut read_vec), Err(Status::SHOULD_WAIT)); + + // Close the socket from one end. + assert!(s1.half_close().is_ok()); + assert_eq!(s2.read(&mut read_vec), Err(Status::BAD_STATE)); + assert_eq!(s1.write(b"fail"), Err(Status::BAD_STATE)); + + // Writing in the other direction should still work. + assert_eq!(s1.read(&mut read_vec), Err(Status::SHOULD_WAIT)); + assert_eq!(s2.write(b"back").unwrap(), 4); + assert_eq!(s1.read(&mut read_vec).unwrap(), 4); + assert_eq!(&read_vec[0..4], b"back"); + } +} diff --git a/third_party/rust/fuchsia-zircon/src/status.rs b/third_party/rust/fuchsia-zircon/src/status.rs new file mode 100644 index 0000000000..4f3e38f988 --- /dev/null +++ b/third_party/rust/fuchsia-zircon/src/status.rs @@ -0,0 +1,162 @@ +use std::ffi::NulError; +use std::io; +use sys; + +/// Status type indicating the result of a Fuchsia syscall. +/// +/// This type is generally used to indicate the reason for an error. +/// While this type can contain `Status::OK` (`ZX_OK` in C land), elements of this type are +/// generally constructed using the `ok` method, which checks for `ZX_OK` and returns a +/// `Result<(), Status>` appropriately. +#[repr(C)] +#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] +pub struct Status(sys::zx_status_t); +impl Status { + /// Returns `Ok(())` if the status was `OK`, + /// otherwise returns `Err(status)`. + pub fn ok(raw: sys::zx_status_t) -> Result<(), Status> { + if raw == Status::OK.0 { + Ok(()) + } else { + Err(Status(raw)) + } + } + + pub fn from_raw(raw: sys::zx_status_t) -> Self { + Status(raw) + } + + pub fn into_raw(self) -> sys::zx_status_t { + self.0 + } +} +assoc_consts!(Status, [ + OK = sys::ZX_OK; + INTERNAL = sys::ZX_ERR_INTERNAL; + NOT_SUPPORTED = sys::ZX_ERR_NOT_SUPPORTED; + NO_RESOURCES = sys::ZX_ERR_NO_RESOURCES; + NO_MEMORY = sys::ZX_ERR_NO_MEMORY; + CALL_FAILED = sys::ZX_ERR_CALL_FAILED; + INTERRUPTED_RETRY = sys::ZX_ERR_INTERRUPTED_RETRY; + INVALID_ARGS = sys::ZX_ERR_INVALID_ARGS; + BAD_HANDLE = sys::ZX_ERR_BAD_HANDLE; + WRONG_TYPE = sys::ZX_ERR_WRONG_TYPE; + BAD_SYSCALL = sys::ZX_ERR_BAD_SYSCALL; + OUT_OF_RANGE = sys::ZX_ERR_OUT_OF_RANGE; + BUFFER_TOO_SMALL = sys::ZX_ERR_BUFFER_TOO_SMALL; + BAD_STATE = sys::ZX_ERR_BAD_STATE; + TIMED_OUT = sys::ZX_ERR_TIMED_OUT; + SHOULD_WAIT = sys::ZX_ERR_SHOULD_WAIT; + CANCELED = sys::ZX_ERR_CANCELED; + PEER_CLOSED = sys::ZX_ERR_PEER_CLOSED; + NOT_FOUND = sys::ZX_ERR_NOT_FOUND; + ALREADY_EXISTS = sys::ZX_ERR_ALREADY_EXISTS; + ALREADY_BOUND = sys::ZX_ERR_ALREADY_BOUND; + UNAVAILABLE = sys::ZX_ERR_UNAVAILABLE; + ACCESS_DENIED = sys::ZX_ERR_ACCESS_DENIED; + IO = sys::ZX_ERR_IO; + IO_REFUSED = sys::ZX_ERR_IO_REFUSED; + IO_DATA_INTEGRITY = sys::ZX_ERR_IO_DATA_INTEGRITY; + IO_DATA_LOSS = sys::ZX_ERR_IO_DATA_LOSS; + BAD_PATH = sys::ZX_ERR_BAD_PATH; + NOT_DIR = sys::ZX_ERR_NOT_DIR; + NOT_FILE = sys::ZX_ERR_NOT_FILE; + FILE_BIG = sys::ZX_ERR_FILE_BIG; + NO_SPACE = sys::ZX_ERR_NO_SPACE; + STOP = sys::ZX_ERR_STOP; + NEXT = sys::ZX_ERR_NEXT; +]); + +impl Status { + pub fn into_io_error(self) -> io::Error { + self.into() + } +} + +impl From<io::ErrorKind> for Status { + fn from(kind: io::ErrorKind) -> Self { + use std::io::ErrorKind::*; + match kind { + NotFound => Status::NOT_FOUND, + PermissionDenied => Status::ACCESS_DENIED, + ConnectionRefused => Status::IO_REFUSED, + ConnectionAborted => Status::PEER_CLOSED, + AddrInUse => Status::ALREADY_BOUND, + AddrNotAvailable => Status::UNAVAILABLE, + BrokenPipe => Status::PEER_CLOSED, + AlreadyExists => Status::ALREADY_EXISTS, + WouldBlock => Status::SHOULD_WAIT, + InvalidInput => Status::INVALID_ARGS, + TimedOut => Status::TIMED_OUT, + Interrupted => Status::INTERRUPTED_RETRY, + UnexpectedEof | + WriteZero | + ConnectionReset | + NotConnected | + Other | _ => Status::IO, + } + } +} + +impl From<Status> for io::ErrorKind { + fn from(status: Status) -> io::ErrorKind { + use std::io::ErrorKind::*; + match status { + Status::INTERRUPTED_RETRY => Interrupted, + Status::BAD_HANDLE => BrokenPipe, + Status::TIMED_OUT => TimedOut, + Status::SHOULD_WAIT => WouldBlock, + Status::PEER_CLOSED => ConnectionAborted, + Status::NOT_FOUND => NotFound, + Status::ALREADY_EXISTS => AlreadyExists, + Status::ALREADY_BOUND => AlreadyExists, + Status::UNAVAILABLE => AddrNotAvailable, + Status::ACCESS_DENIED => PermissionDenied, + Status::IO_REFUSED => ConnectionRefused, + Status::IO_DATA_INTEGRITY => InvalidData, + + Status::BAD_PATH | + Status::INVALID_ARGS | + Status::OUT_OF_RANGE | + Status::WRONG_TYPE => InvalidInput, + + Status::OK | + Status::NEXT | + Status::STOP | + Status::NO_SPACE | + Status::FILE_BIG | + Status::NOT_FILE | + Status::NOT_DIR | + Status::IO_DATA_LOSS | + Status::IO | + Status::CANCELED | + Status::BAD_STATE | + Status::BUFFER_TOO_SMALL | + Status::BAD_SYSCALL | + Status::INTERNAL | + Status::NOT_SUPPORTED | + Status::NO_RESOURCES | + Status::NO_MEMORY | + Status::CALL_FAILED | + _ => Other, + } + } +} + +impl From<io::Error> for Status { + fn from(err: io::Error) -> Status { + err.kind().into() + } +} + +impl From<Status> for io::Error { + fn from(status: Status) -> io::Error { + io::Error::from(io::ErrorKind::from(status)) + } +} + +impl From<NulError> for Status { + fn from(_error: NulError) -> Status { + Status::INVALID_ARGS + } +} diff --git a/third_party/rust/fuchsia-zircon/src/thread.rs b/third_party/rust/fuchsia-zircon/src/thread.rs new file mode 100644 index 0000000000..be482375d4 --- /dev/null +++ b/third_party/rust/fuchsia-zircon/src/thread.rs @@ -0,0 +1,14 @@ +// 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 thread. + +use {AsHandleRef, HandleBased, Handle, HandleRef}; + +/// An object representing a Zircon thread. +/// +/// As essentially a subtype of `Handle`, it can be freely interconverted. +#[derive(Debug, Eq, PartialEq)] +pub struct Thread(Handle); +impl_handle_based!(Thread);
\ No newline at end of file diff --git a/third_party/rust/fuchsia-zircon/src/time.rs b/third_party/rust/fuchsia-zircon/src/time.rs new file mode 100644 index 0000000000..1b1deaceed --- /dev/null +++ b/third_party/rust/fuchsia-zircon/src/time.rs @@ -0,0 +1,346 @@ +// Copyright 2016 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 timer objects. + +use {AsHandleRef, ClockId, HandleBased, Handle, HandleRef, Status}; +use {sys, ok}; +use std::ops; +use std::time as stdtime; + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub struct Duration(sys::zx_duration_t); + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub struct Time(sys::zx_time_t); + +impl From<stdtime::Duration> for Duration { + fn from(dur: stdtime::Duration) -> Self { + Duration::from_seconds(dur.as_secs()) + + Duration::from_nanos(dur.subsec_nanos() as u64) + } +} + +impl From<Duration> for stdtime::Duration { + fn from(dur: Duration) -> Self { + let secs = dur.seconds(); + let nanos = (dur.nanos() - (secs * 1_000_000_000)) as u32; + stdtime::Duration::new(secs, nanos) + } +} + +impl ops::Add<Duration> for Time { + type Output = Time; + fn add(self, dur: Duration) -> Time { + Time::from_nanos(dur.nanos() + self.nanos()) + } +} + +impl ops::Add<Time> for Duration { + type Output = Time; + fn add(self, time: Time) -> Time { + Time::from_nanos(self.nanos() + time.nanos()) + } +} + +impl ops::Add for Duration { + type Output = Duration; + fn add(self, dur: Duration) -> Duration { + Duration::from_nanos(self.nanos() + dur.nanos()) + } +} + +impl ops::Sub for Duration { + type Output = Duration; + fn sub(self, dur: Duration) -> Duration { + Duration::from_nanos(self.nanos() - dur.nanos()) + } +} + +impl ops::Sub<Duration> for Time { + type Output = Time; + fn sub(self, dur: Duration) -> Time { + Time::from_nanos(self.nanos() - dur.nanos()) + } +} + +impl ops::AddAssign for Duration { + fn add_assign(&mut self, dur: Duration) { + self.0 += dur.nanos() + } +} + +impl ops::SubAssign for Duration { + fn sub_assign(&mut self, dur: Duration) { + self.0 -= dur.nanos() + } +} + +impl ops::AddAssign<Duration> for Time { + fn add_assign(&mut self, dur: Duration) { + self.0 += dur.nanos() + } +} + +impl ops::SubAssign<Duration> for Time { + fn sub_assign(&mut self, dur: Duration) { + self.0 -= dur.nanos() + } +} + +impl<T> ops::Mul<T> for Duration + where T: Into<u64> +{ + type Output = Self; + fn mul(self, mul: T) -> Self { + Duration::from_nanos(self.0 * mul.into()) + } +} + +impl<T> ops::Div<T> for Duration + where T: Into<u64> +{ + type Output = Self; + fn div(self, div: T) -> Self { + Duration::from_nanos(self.0 / div.into()) + } +} + +impl Duration { + /// Sleep for the given amount of time. + pub fn sleep(self) { + Time::after(self).sleep() + } + + pub fn nanos(self) -> u64 { + self.0 + } + + pub fn millis(self) -> u64 { + self.0 / 1_000_000 + } + + pub fn seconds(self) -> u64 { + self.millis() / 1_000 + } + + pub fn minutes(self) -> u64 { + self.seconds() / 60 + } + + pub fn hours(self) -> u64 { + self.minutes() / 60 + } + + pub fn from_nanos(nanos: u64) -> Self { + Duration(nanos) + } + + pub fn from_millis(millis: u64) -> Self { + Duration(millis * 1_000_000) + } + + pub fn from_seconds(secs: u64) -> Self { + Duration::from_millis(secs * 1_000) + } + + pub fn from_minutes(min: u64) -> Self { + Duration::from_seconds(min * 60) + } + + pub fn from_hours(hours: u64) -> Self { + Duration::from_minutes(hours * 60) + } + + /// Returns a `Time` which is a `Duration` after the current time. + /// `duration.after_now()` is equivalent to `Time::after(duration)`. + pub fn after_now(self) -> Time { + Time::after(self) + } +} + +pub trait DurationNum: Sized { + fn nanos(self) -> Duration; + fn millis(self) -> Duration; + fn seconds(self) -> Duration; + fn minutes(self) -> Duration; + fn hours(self) -> Duration; + + // Singular versions to allow for `1.milli()` and `1.second()`, etc. + fn milli(self) -> Duration { self.millis() } + fn second(self) -> Duration { self.seconds() } + fn minute(self) -> Duration { self.minutes() } + fn hour(self) -> Duration { self.hours() } +} + +// Note: this could be implemented for other unsized integer types, but it doesn't seem +// necessary to support the usual case. +impl DurationNum for u64 { + fn nanos(self) -> Duration { + Duration::from_nanos(self) + } + + fn millis(self) -> Duration { + Duration::from_millis(self) + } + + fn seconds(self) -> Duration { + Duration::from_seconds(self) + } + + fn minutes(self) -> Duration { + Duration::from_minutes(self) + } + + fn hours(self) -> Duration { + Duration::from_hours(self) + } +} + +impl Time { + pub const INFINITE: Time = Time(sys::ZX_TIME_INFINITE); + + /// Get the current time, from the specific clock id. + /// + /// Wraps the + /// [zx_time_get](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/time_get.md) + /// syscall. + pub fn get(clock_id: ClockId) -> Time { + unsafe { Time(sys::zx_time_get(clock_id as u32)) } + } + + /// Compute a deadline for the time in the future that is the given `Duration` away. + /// + /// Wraps the + /// [zx_deadline_after](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/deadline_after.md) + /// syscall. + pub fn after(duration: Duration) -> Time { + unsafe { Time(sys::zx_deadline_after(duration.0)) } + } + + /// Sleep until the given time. + /// + /// Wraps the + /// [zx_nanosleep](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/nanosleep.md) + /// syscall. + pub fn sleep(self) { + unsafe { sys::zx_nanosleep(self.0); } + } + + pub fn nanos(self) -> u64 { + self.0 + } + + pub fn from_nanos(nanos: u64) -> Self { + Time(nanos) + } +} + +/// Read the number of high-precision timer ticks since boot. These ticks may be processor cycles, +/// high speed timer, profiling timer, etc. They are not guaranteed to continue advancing when the +/// system is asleep. +/// +/// Wraps the +/// [zx_ticks_get](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/ticks_get.md) +/// syscall. +pub fn ticks_get() -> u64 { + unsafe { sys::zx_ticks_get() } +} + +/// Return the number of high-precision timer ticks in a second. +/// +/// Wraps the +/// [zx_ticks_per_second](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/ticks_per_second.md) +/// syscall. +pub fn ticks_per_second() -> u64 { + unsafe { sys::zx_ticks_per_second() } +} + +/// An object representing a Zircon timer, such as the one returned by +/// [zx_timer_create](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/timer_create.md). +/// +/// As essentially a subtype of `Handle`, it can be freely interconverted. +#[derive(Debug, Eq, PartialEq)] +pub struct Timer(Handle); +impl_handle_based!(Timer); + +impl Timer { + /// Create a timer, an object that can signal when a specified point in time has been reached. + /// Wraps the + /// [zx_timer_create](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/timer_create.md) + /// syscall. + pub fn create(clock_id: ClockId) -> Result<Timer, Status> { + let mut out = 0; + let opts = 0; + let status = unsafe { sys::zx_timer_create(opts, clock_id as u32, &mut out) }; + ok(status)?; + unsafe { + Ok(Self::from(Handle::from_raw(out))) + } + } + + /// Start a one-shot timer that will fire when `deadline` passes. Wraps the + /// [zx_timer_set](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/timer_set.md) + /// syscall. + pub fn set(&self, deadline: Time, slack: Duration) -> Result<(), Status> { + let status = unsafe { + sys::zx_timer_set(self.raw_handle(), deadline.nanos(), slack.nanos()) + }; + ok(status) + } + + /// Cancels a pending timer that was started with start(). Wraps the + /// [zx_timer_cancel](https://fuchsia.googlesource.com/zircon/+/master/docs/syscalls/timer_cancel.md) + /// syscall. + pub fn cancel(&self) -> Result<(), Status> { + let status = unsafe { sys::zx_timer_cancel(self.raw_handle()) }; + ok(status) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use Signals; + + #[test] + fn create_timer_invalid_clock() { + assert_eq!(Timer::create(ClockId::UTC).unwrap_err(), Status::INVALID_ARGS); + assert_eq!(Timer::create(ClockId::Thread), Err(Status::INVALID_ARGS)); + } + + #[test] + fn into_from_std() { + let std_dur = stdtime::Duration::new(25, 25); + assert_eq!(std_dur, stdtime::Duration::from(Duration::from(std_dur))); + } + + #[test] + fn timer_basic() { + let slack = 0.millis(); + let ten_ms = 10.millis(); + let five_secs = 5.seconds(); + let six_secs = 6.seconds(); + + // Create a timer + let timer = Timer::create(ClockId::Monotonic).unwrap(); + + // Should not signal yet. + assert_eq!( + timer.wait_handle(Signals::TIMER_SIGNALED, ten_ms.after_now()), + Err(Status::TIMED_OUT)); + + // Set it, and soon it should signal. + assert_eq!(timer.set(five_secs.after_now(), slack), Ok(())); + assert_eq!( + timer.wait_handle(Signals::TIMER_SIGNALED, six_secs.after_now()), + Ok(Signals::TIMER_SIGNALED)); + + // Cancel it, and it should stop signalling. + assert_eq!(timer.cancel(), Ok(())); + assert_eq!( + timer.wait_handle(Signals::TIMER_SIGNALED, ten_ms.after_now()), + Err(Status::TIMED_OUT)); + } +} diff --git a/third_party/rust/fuchsia-zircon/src/vmar.rs b/third_party/rust/fuchsia-zircon/src/vmar.rs new file mode 100644 index 0000000000..a828195df4 --- /dev/null +++ b/third_party/rust/fuchsia-zircon/src/vmar.rs @@ -0,0 +1,18 @@ +use sys; + +bitflags! { + /// Flags to VMAR routines + #[repr(C)] + pub struct VmarFlags: u32 { + const PERM_READ = sys::ZX_VM_FLAG_PERM_READ; + const PERM_WRITE = sys::ZX_VM_FLAG_PERM_WRITE; + const PERM_EXECUTE = sys::ZX_VM_FLAG_PERM_EXECUTE; + const COMPACT = sys::ZX_VM_FLAG_COMPACT; + const SPECIFIC = sys::ZX_VM_FLAG_SPECIFIC; + const SPECIFIC_OVERWRITE = sys::ZX_VM_FLAG_SPECIFIC_OVERWRITE; + const CAN_MAP_SPECIFIC = sys::ZX_VM_FLAG_CAN_MAP_SPECIFIC; + const CAN_MAP_READ = sys::ZX_VM_FLAG_CAN_MAP_READ; + const CAN_MAP_WRITE = sys::ZX_VM_FLAG_CAN_MAP_WRITE; + const CAN_MAP_EXECUTE = sys::ZX_VM_FLAG_CAN_MAP_EXECUTE; + } +}
\ No newline at end of file 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"); + } +} |