blob: acedb1f85b5bc0a9c0269db7039ccfd746bb45d6 [file]
// Copyright 2026 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
use crate::object::{
Dispatcher, HandleValue, ProcessDispatcher, ThreadDispatcher, VmObjectDispatcher,
};
use crate::user_copy::{UserInPtr, UserOutPtr};
use boot_options::BootOptions;
use core::mem::MaybeUninit;
use debug::{ltrace_entry, ltracef};
use syscalls_macro::syscall;
use zx_status::Status;
use zx_types::{
ZX_HANDLE_INVALID, ZX_MAX_NAME_LEN, ZX_RIGHT_DUPLICATE, ZX_RIGHT_MANAGE_THREAD, ZX_RIGHT_READ,
ZX_RIGHT_WRITE, ZX_THREAD_STATE_DEBUG_REGS, zx_exception_context_t, zx_excp_type_t, zx_rseq_t,
zx_status_t,
};
unsafe extern "C" {
/// Resets the restartable sequence (rseq) registration for the calling thread.
///
/// # Safety
///
/// Must only be called in a valid thread context.
fn cpp_thread_reset_rseq();
/// Registers a restartable sequence (rseq) for the calling thread backed by `vmo_dispatcher`.
///
/// # Safety
///
/// `vmo_dispatcher` must be a valid reference to an initialized `VmObjectDispatcher`.
fn cpp_thread_set_rseq(vmo_dispatcher: &VmObjectDispatcher, offset: u64) -> zx_status_t;
}
const LOCAL_TRACE: u32 = 0;
#[syscall]
pub fn sys_thread_create(
process_handle: HandleValue,
name_ptr: UserInPtr<u8>,
name_len: usize,
options: u32,
out: &mut HandleValue,
) -> Result<(), Status> {
ltracef!("process handle {:#x}, options {:#x}\n", process_handle.raw_value(), options);
// currently, the only valid option value is 0
if options != 0 {
return Err(Status::INVALID_ARGS);
}
if name_ptr.is_null() {
return Err(Status::INVALID_ARGS);
}
// copy out the name
let mut name_buf = [MaybeUninit::<u8>::uninit(); ZX_MAX_NAME_LEN];
// Silently truncate the given name.
let len = name_len.min(ZX_MAX_NAME_LEN);
if len > 0 {
name_ptr.copy_slice_from_user(&mut name_buf[..len]).map_err(|_| Status::INVALID_ARGS)?;
}
let str_len = if len == ZX_MAX_NAME_LEN { len - 1 } else { len };
name_buf[str_len].write(0);
// SAFETY: elements 0..=str_len in `name_buf` have been initialized.
let slice: &[u8] =
unsafe { core::slice::from_raw_parts(name_buf.as_ptr() as *const u8, str_len) };
ltracef!("name {}\n", core::str::from_utf8(slice).unwrap_or("<non-utf8>"));
// convert process handle to process dispatcher
let process =
Dispatcher::get_with_rights::<ProcessDispatcher>(process_handle, ZX_RIGHT_MANAGE_THREAD)?;
// create the thread dispatcher
let (handle, rights) = ThreadDispatcher::create(process, options, slice)?;
handle.dispatcher().initialize()?;
*out = ProcessDispatcher::with_current(|up| up.make_and_add_handle(handle, rights))?;
Ok(())
}
#[syscall]
pub fn sys_thread_start_regs(
handle: HandleValue,
thread_entry: u64,
stack: u64,
arg1: u64,
arg2: u64,
tp: u64,
abi_reg: u64,
) -> Result<(), Status> {
ltracef!(
"handle {:#x}, entry {:#x}, sp {:#x}, arg1 {:#x}, arg2 {:#x}\n",
handle.raw_value(),
thread_entry,
stack,
arg1,
arg2
);
let thread = Dispatcher::get_with_rights::<ThreadDispatcher>(handle, ZX_RIGHT_MANAGE_THREAD)?;
#[cfg(target_arch = "x86_64")]
{
// A noncanonical address cannot be written into the MSR.
if !crate::arch_rs::x86::is_vaddr_canonical(tp) {
return Err(Status::INVALID_ARGS);
}
}
thread.start(thread_entry as usize, stack as usize, arg1, arg2, tp, abi_reg, false)?;
Ok(())
}
#[syscall]
pub fn sys_thread_exit() {
ltrace_entry!();
ThreadDispatcher::exit_current();
}
#[syscall]
pub fn sys_thread_read_state(
handle: HandleValue,
kind: u32,
buffer: UserOutPtr<u8>,
buffer_size: usize,
) -> Result<(), Status> {
ltracef!("handle {:#x}, kind {}\n", handle.raw_value(), kind);
// TODO(https://fxbug.dev/42105831): debug rights
let thread = Dispatcher::get_with_rights::<ThreadDispatcher>(handle, ZX_RIGHT_READ)?;
thread.read_state(kind, buffer.reinterpret::<core::ffi::c_void>().as_ptr(), buffer_size)?;
Ok(())
}
#[syscall]
pub fn sys_thread_write_state(
handle: HandleValue,
kind: u32,
buffer: UserInPtr<u8>,
buffer_size: usize,
) -> Result<(), Status> {
ltracef!("handle {:#x}, kind {}\n", handle.raw_value(), kind);
if (kind & ZX_THREAD_STATE_DEBUG_REGS) != 0 && !BootOptions::get().enable_debugging_syscalls {
return Err(Status::NOT_SUPPORTED);
}
// TODO(https://fxbug.dev/42105831): debug rights
let thread = Dispatcher::get_with_rights::<ThreadDispatcher>(handle, ZX_RIGHT_WRITE)?;
thread.write_state(kind, buffer.reinterpret::<core::ffi::c_void>().as_ptr(), buffer_size)?;
Ok(())
}
#[syscall]
pub fn sys_thread_raise_exception(
options: u32,
exception_type: zx_excp_type_t,
user_context_ptr: UserInPtr<zx_exception_context_t>,
) -> Result<(), Status> {
ltracef!("options {:#x}, exception type {:#x}\n", options, exception_type);
if user_context_ptr.is_null() {
return Err(Status::INVALID_ARGS);
}
let mut context = MaybeUninit::<zx_exception_context_t>::uninit();
let context_ref =
user_context_ptr.copy_from_user(&mut context).map_err(|_| Status::INVALID_ARGS)?;
ThreadDispatcher::raise_user_exception(options, exception_type, context_ref)?;
Ok(())
}
#[syscall]
pub fn sys_thread_set_rseq(vmo_handle: HandleValue, offset: u64, size: u64) -> Result<(), Status> {
ltracef!("vmo handle {:#x}, offset {}, size {}\n", vmo_handle.raw_value(), offset, size);
// Is this an "unregister" operation?
if vmo_handle.raw_value() == ZX_HANDLE_INVALID {
if offset != 0 || size != 0 {
return Err(Status::INVALID_ARGS);
}
// SAFETY: Resets the restartable sequence configuration on the current thread context.
unsafe { cpp_thread_reset_rseq() };
return Ok(());
}
// It's a register operation.
//
// Validate arguments.
if size != core::mem::size_of::<zx_rseq_t>() as u64 {
return Err(Status::INVALID_ARGS);
}
if !offset.is_multiple_of(core::mem::align_of::<zx_rseq_t>() as u64) {
return Err(Status::INVALID_ARGS);
}
// Get the VMO dispatcher.
let vmo_dispatcher = Dispatcher::get_with_rights::<VmObjectDispatcher>(
vmo_handle,
ZX_RIGHT_READ | ZX_RIGHT_WRITE | ZX_RIGHT_DUPLICATE,
)?;
// SAFETY: `vmo_dispatcher` is a valid `VmObjectDispatcher` reference.
let status = unsafe { cpp_thread_set_rseq(&vmo_dispatcher, offset) };
Status::ok(status)?;
Ok(())
}
#[syscall]
pub fn sys_thread_legacy_yield(options: u32) -> Result<(), Status> {
ltracef!("options {:#x}\n", options);
ThreadDispatcher::legacy_yield(options)?;
Ok(())
}