blob: a9ac98696147087be8d5e011e016f9ca506d4a7c [file] [log] [blame]
// Copyright 2019 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 Fuchsia-specific `libc` functionality.
//!
//! This crate is a minimal extension on top of the `fuchsia-zircon` crate,
//! which provides bindings to the Zircon kernel's syscalls, but does not
//! depend on functionality from `libc`.
// AKA `libc`-granted ambient-authority crate ;)
#![deny(missing_docs)]
use {
fuchsia_zircon::{
sys::{zx_handle_t, zx_status_t, ZX_HANDLE_INVALID}, // handle type (primitive, non-owning)
Clock,
Handle,
HandleBased,
Job,
Process,
Rights,
Status,
Thread,
Time,
Unowned,
Vmar,
},
num_derive::FromPrimitive,
num_traits::cast::FromPrimitive,
thiserror::Error,
};
// TODO(https://fxbug.dev/42139436): Document these.
#[allow(missing_docs)]
extern "C" {
pub fn dl_clone_loader_service(out: *mut zx_handle_t) -> zx_status_t;
pub fn zx_take_startup_handle(hnd_info: u32) -> zx_handle_t;
pub fn zx_thread_self() -> zx_handle_t;
pub fn zx_process_self() -> zx_handle_t;
pub fn thrd_get_zx_process() -> zx_handle_t;
pub fn zx_vmar_root_self() -> zx_handle_t;
pub fn zx_job_default() -> zx_handle_t;
pub fn zx_utc_reference_get() -> zx_handle_t;
pub fn zx_utc_reference_swap(
new_handle: zx_handle_t,
prev_handle: *mut zx_handle_t,
) -> zx_status_t;
}
/// Handle types as defined by the processargs protocol.
///
/// See [//zircon/system/public/zircon/processargs.h][processargs.h] for canonical definitions.
///
/// Short descriptions of each handle type are given, but more complete documentation may be found
/// in the [processargs.h] header.
///
/// [processargs.h]: https://fuchsia.googlesource.com/fuchsia/+/HEAD/zircon/system/public/zircon/processargs.h
#[repr(u8)]
#[derive(FromPrimitive, Copy, Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum HandleType {
/// Handle to our own process.
///
/// Equivalent to PA_PROC_SELF.
ProcessSelf = 0x01,
/// Handle to the initial thread of our own process.
///
/// Equivalent to PA_THREAD_SELF.
ThreadSelf = 0x02,
/// Handle to a job object which can be used to make child processes.
///
/// The job can be the same as the one used to create this process or it can
/// be different.
///
/// Equivalent to PA_JOB_DEFAULT.
DefaultJob = 0x03,
/// Handle to the root of our address space.
///
/// Equivalent to PA_VMAR_ROOT.
RootVmar = 0x04,
/// Handle to the VMAR used to load the initial program image.
///
/// Equivalent to PA_VMAR_LOADED.
LoadedVmar = 0x05,
/// Service for loading shared libraries.
///
/// See `fuchsia.ldsvc.Loader` for the interface definition.
///
/// Equivalent to PA_LDSVC_LOADER.
LdsvcLoader = 0x10,
/// Handle to the VMO containing the vDSO ELF image.
///
/// Equivalent to PA_VMO_VDSO.
VdsoVmo = 0x11,
/// Handle to the VMO used to map the initial thread's stack.
///
/// Equivalent to PA_VMO_STACK.
StackVmo = 0x13,
/// Handle to the VMO for the main executable file.
///
/// Equivalent to PA_VMO_EXECUTABLE.
ExecutableVmo = 0x14,
/// Used by kernel and userboot during startup.
///
/// Equivalent to PA_VMO_BOOTDATA.
BootdataVmo = 0x1A,
/// Used by kernel and userboot during startup.
///
/// Equivalent to PA_VMO_BOOTFS.
BootfsVmo = 0x1B,
/// Used by the kernel to export debug information as a file in bootfs.
///
/// Equivalent to PA_VMO_KERNEL_FILE.
KernelFileVmo = 0x1C,
/// A Handle to a component's process' configuration VMO.
///
/// Equivalent to PA_VMO_COMPONENT_CONFIG.
ComponentConfigVmo = 0x1D,
/// A handle to a fuchsia.io.Directory service to be used as a directory in the process's
/// namespace. Corresponds to a path in the processargs bootstrap message's namespace table
/// based on the argument of a HandleInfo of this type.
///
/// Equivalent to PA_NS_DIR.
NamespaceDirectory = 0x20,
/// A handle which will be used as a file descriptor.
///
/// Equivalent to PA_FD.
FileDescriptor = 0x30,
/// A Handle to a channel on which the process may serve the
/// the |fuchsia.process.Lifecycle| protocol.
///
/// Equivalent to PA_LIFECYCLE.
Lifecycle = 0x3A,
/// Server endpoint for handling connections to a process's outgoing directory.
///
/// Equivalent to PA_DIRECTORY_REQUEST.
DirectoryRequest = 0x3B,
/// A Handle to a resource object. Used by devcoordinator and devhosts.
///
/// Equivalent to PA_RESOURCE.
Resource = 0x3F,
/// A Handle to a clock object representing UTC. Used by runtimes to gain
/// access to UTC time.
///
/// Equivalent to PA_CLOCK_UTC.
ClockUtc = 0x40,
/// A Handle to an MMIO resource object.
///
/// Equivalent to PA_MMIO_RESOURCE.
MmioResource = 0x50,
/// A Handle to an IRQ resource object.
///
/// Equivalent to PA_IRQ_RESOURCE.
IrqResource = 0x51,
/// A Handle to an IO Port resource object.
///
/// Equivalent to PA_IOPORT_RESOURCE.
IoportResource = 0x52,
/// A Handle to an SMC resource object.
///
/// Equivalent to PA_SMC_RESOURCE.
SmcResource = 0x53,
/// A Handle to the System resource object.
///
/// Equivalent to PA_SYSTEM_RESOURCE.
SystemResource = 0x54,
/// A handle type with user-defined meaning.
///
/// Equivalent to PA_USER0.
User0 = 0xF0,
/// A handle type with user-defined meaning.
///
/// Equivalent to PA_USER1.
User1 = 0xF1,
/// A handle type with user-defined meaning.
///
/// Equivalent to PA_USER2.
User2 = 0xF2,
}
/// Metadata information for a handle in a processargs message. Contains a handle type and an
/// unsigned 16-bit value, whose meaning is handle type dependent.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct HandleInfo {
htype: HandleType,
arg: u16,
}
impl HandleInfo {
/// Create a handle info struct from a handle type and an argument.
///
/// For example, a `HandleInfo::new(HandleType::FileDescriptor, 32)` identifies
/// the respective handle as file descriptor 32.
///
/// Corresponds to PA_HND in processargs.h.
pub const fn new(htype: HandleType, arg: u16) -> Self {
HandleInfo { htype, arg }
}
/// Returns the handle type for this handle info struct.
#[inline(always)]
pub fn handle_type(&self) -> HandleType {
self.htype
}
/// Returns the argument for this handle info struct.
#[inline(always)]
pub fn arg(&self) -> u16 {
self.arg
}
/// Convert the handle info into a raw u32 value for FFI purposes.
pub const fn as_raw(&self) -> u32 {
((self.htype as u32) & 0xFF) | (self.arg as u32) << 16
}
}
/// An implementation of the From trait to create a [HandleInfo] from a [HandleType] with an argument
/// of 0.
impl From<HandleType> for HandleInfo {
fn from(ty: HandleType) -> Self {
Self::new(ty, 0)
}
}
/// Possible errors when converting a raw u32 to a HandleInfo with the TryFrom<u32> impl on
/// HandleInfo.
#[derive(Error, Debug)]
pub enum HandleInfoError {
/// Unknown handle type.
#[error("Unknown handle type for HandleInfo: {:#x?}", _0)]
UnknownHandleType(u32),
/// Otherwise invalid raw value, like reserved bytes being non-zero.
#[error("Invalid value for HandleInfo: {:#x?}", _0)]
InvalidHandleInfo(u32),
}
impl TryFrom<u32> for HandleInfo {
type Error = HandleInfoError;
/// Attempt to convert a u32 to a handle ID value. Can fail if the value represents an
/// unknown handle type or is otherwise invalid.
///
/// Useful to convert existing handle info values received through FIDL APIs, e.g. from a
/// client that creates them using the PA_HND macro in processargs.h from C/C++.
fn try_from(value: u32) -> Result<HandleInfo, HandleInfoError> {
// 2nd byte should be zero, it is currently unused.
if value & 0xFF00 != 0 {
return Err(HandleInfoError::InvalidHandleInfo(value));
}
let htype = HandleType::from_u8((value & 0xFF) as u8)
.ok_or(HandleInfoError::UnknownHandleType(value))?;
Ok(HandleInfo::new(htype, (value >> 16) as u16))
}
}
/// Removes the handle of type `HandleType` from the list of handles received at startup.
///
/// This function will return `Some` at-most once per handle type.
/// This function will return `None` if the requested type was not received at
/// startup or if the handle with the provided type was already taken.
pub fn take_startup_handle(info: HandleInfo) -> Option<Handle> {
unsafe {
let raw = zx_take_startup_handle(info.as_raw());
if raw == ZX_HANDLE_INVALID {
None
} else {
Some(Handle::from_raw(raw))
}
}
}
/// Get a reference to the handle of the current thread.
pub fn thread_self() -> Unowned<'static, Thread> {
unsafe {
let handle = zx_thread_self();
Unowned::from_raw_handle(handle)
}
}
/// Get a reference to the handle of the current process.
pub fn process_self() -> Unowned<'static, Process> {
unsafe {
// zx_process_self() doesn't work correctly in jobs where multiple processes share
// the portion of their address space for global variables. Use thrd_get_zx_process() to
// return the correct value in that context. See https://fxbug.dev/42083701 for background.
let handle = thrd_get_zx_process();
Unowned::from_raw_handle(handle)
}
}
/// Get a reference to the handle of the current address space.
pub fn vmar_root_self() -> Unowned<'static, Vmar> {
unsafe {
let handle = zx_vmar_root_self();
Unowned::from_raw_handle(handle)
}
}
/// Get a reference to the fuchsia.ldsvc.Loader channel.
pub fn loader_svc() -> Result<Handle, Status> {
unsafe {
let mut handle: zx_handle_t = 0;
let status = dl_clone_loader_service(&mut handle);
Status::ok(status)?;
Ok(Handle::from_raw(handle))
}
}
/// Get a reference to the default `Job` provided to the process on startup.
///
/// This typically refers to the `Job` that is the immediate parent of the current
/// process.
///
/// If the current process was launched as a Fuchsia Component, this `Job`
/// will begin with no child processes other than the current process.
pub fn job_default() -> Unowned<'static, Job> {
unsafe {
let handle = zx_job_default();
Unowned::from_raw_handle(handle)
}
}
/// Duplicate the UTC `Clock` registered with the runtime.
pub fn duplicate_utc_clock_handle(rights: Rights) -> Result<Clock, Status> {
let handle: Unowned<'static, Clock> = unsafe {
let handle = zx_utc_reference_get();
Unowned::from_raw_handle(handle)
};
handle.duplicate(rights)
}
/// Swaps the current process-global UTC clock with `new_clock`, returning
/// the old clock on success.
/// If `new_clock` is a valid handle but does not have the ZX_RIGHT_READ right,
/// an error is returned and `new_clock` is dropped.
pub fn swap_utc_clock_handle(new_clock: Clock) -> Result<Clock, Status> {
Ok(unsafe {
let mut prev_handle = ZX_HANDLE_INVALID;
Status::ok(zx_utc_reference_swap(new_clock.into_raw(), &mut prev_handle))?;
Handle::from_raw(prev_handle)
}
.into())
}
/// Reads time from the UTC `Clock` registered with the runtime.
///
/// # Panics
///
/// Panics if there is no UTC clock registered with the runtime or the registered handle does not
/// have the required rights.
pub fn utc_time() -> Time {
let clock: Unowned<'static, Clock> = unsafe {
let handle = zx_utc_reference_get();
Unowned::from_raw_handle(handle)
};
clock.read().expect("Failed to read UTC clock")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn handle_id() {
let mut randbuf = [0; 2];
for type_val in 0..0xFF {
if let Some(htype) = HandleType::from_u8(type_val as u8) {
fuchsia_zircon::cprng_draw(&mut randbuf);
let arg = u16::from_le_bytes(randbuf);
let info = HandleInfo::new(htype, arg);
assert_eq!(info.handle_type(), htype);
assert_eq!(info.arg(), arg);
let info = HandleInfo::from(htype);
assert_eq!(info.handle_type(), htype);
assert_eq!(info.arg(), 0);
}
}
}
#[test]
fn handle_id_raw() {
assert_eq!(HandleInfo::new(HandleType::ProcessSelf, 0).as_raw(), 0x00000001);
assert_eq!(HandleInfo::new(HandleType::DirectoryRequest, 0).as_raw(), 0x0000003B);
assert_eq!(HandleInfo::new(HandleType::LdsvcLoader, 0xABCD).as_raw(), 0xABCD0010);
assert_eq!(HandleInfo::new(HandleType::User0, 0x1).as_raw(), 0x000100F0);
assert_eq!(HandleInfo::new(HandleType::User1, 0xABCD).as_raw(), 0xABCD00F1);
assert_eq!(HandleInfo::new(HandleType::User2, 0xFFFF).as_raw(), 0xFFFF00F2);
}
#[test]
fn handle_id_from_u32() {
assert_eq!(
HandleInfo::try_from(0x00000002).unwrap(),
HandleInfo::new(HandleType::ThreadSelf, 0)
);
assert_eq!(
HandleInfo::try_from(0x00040030).unwrap(),
HandleInfo::new(HandleType::FileDescriptor, 4)
);
assert_eq!(
HandleInfo::try_from(0x501C00F2).unwrap(),
HandleInfo::new(HandleType::User2, 0x501C)
);
// Non-zero unused byte
assert!(HandleInfo::try_from(0x00001100).is_err());
assert!(HandleInfo::try_from(0x00010101).is_err());
// Unknown handle type
assert!(HandleInfo::try_from(0x00000000).is_err());
assert!(HandleInfo::try_from(0x00000006).is_err());
}
#[test]
fn read_utc_time() {
utc_time();
}
#[test]
fn get_loader_svc() {
loader_svc().unwrap();
}
}