blob: 70761a8a55a2c3e30ba938b08b503fbfdf9e1ffc [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 core::mem::MaybeUninit;
use debug::ltracef;
use syscalls_macro::syscall;
use zx_status::Status;
use zx_types::{
ZX_JOB_CRITICAL_PROCESS_RETCODE_NONZERO, ZX_JOB_POL_ABSOLUTE, ZX_JOB_POL_RELATIVE,
ZX_RIGHT_DESTROY, ZX_RIGHT_MANAGE_JOB, ZX_RIGHT_SET_POLICY, ZX_RIGHT_WAIT,
zx_policy_basic_v1_t, zx_policy_basic_v2_t, zx_policy_timer_slack_t,
};
use crate::object::{Dispatcher, HandleValue, JobDispatcher, ProcessDispatcher};
use crate::user_copy::UserInPtr;
const LOCAL_TRACE: u32 = 0;
const MAX_POLICY_COUNT: usize = 32;
fn job_set_policy_basic_v1(
handle: HandleValue,
options: u32,
policy_ptr: UserInPtr<u8>,
count: u32,
) -> Result<(), Status> {
if options != ZX_JOB_POL_RELATIVE && options != ZX_JOB_POL_ABSOLUTE {
return Err(Status::INVALID_ARGS);
}
if policy_ptr.is_null() || count == 0 || count as usize > MAX_POLICY_COUNT {
return Err(Status::INVALID_ARGS);
}
let mut storage = [MaybeUninit::<zx_policy_basic_v1_t>::uninit(); MAX_POLICY_COUNT];
let slice = &mut storage[..count as usize];
let policy = policy_ptr
.reinterpret::<zx_policy_basic_v1_t>()
.copy_slice_from_user(slice)
.map_err(|_| Status::INVALID_ARGS)?;
let job = Dispatcher::get_with_rights::<JobDispatcher>(handle, ZX_RIGHT_SET_POLICY)?;
job.set_basic_policy_v1(options, policy)?;
Ok(())
}
fn job_set_policy_basic_v2(
handle: HandleValue,
options: u32,
policy_ptr: UserInPtr<u8>,
count: u32,
) -> Result<(), Status> {
if options != ZX_JOB_POL_RELATIVE && options != ZX_JOB_POL_ABSOLUTE {
return Err(Status::INVALID_ARGS);
}
if policy_ptr.is_null() || count == 0 || count as usize > MAX_POLICY_COUNT {
return Err(Status::INVALID_ARGS);
}
let mut storage = [MaybeUninit::<zx_policy_basic_v2_t>::uninit(); MAX_POLICY_COUNT];
let slice = &mut storage[..count as usize];
let policy = policy_ptr
.reinterpret::<zx_policy_basic_v2_t>()
.copy_slice_from_user(slice)
.map_err(|_| Status::INVALID_ARGS)?;
let job = Dispatcher::get_with_rights::<JobDispatcher>(handle, ZX_RIGHT_SET_POLICY)?;
job.set_basic_policy_v2(options, policy)?;
Ok(())
}
fn job_set_policy_timer_slack(
handle: HandleValue,
options: u32,
policy_ptr: UserInPtr<u8>,
count: u32,
) -> Result<(), Status> {
if options != ZX_JOB_POL_RELATIVE {
return Err(Status::INVALID_ARGS);
}
if policy_ptr.is_null() || count != 1 {
return Err(Status::INVALID_ARGS);
}
let mut uninit_policy = MaybeUninit::<zx_policy_timer_slack_t>::uninit();
let slack_policy = policy_ptr
.reinterpret::<zx_policy_timer_slack_t>()
.copy_from_user(&mut uninit_policy)
.map_err(|_| Status::INVALID_ARGS)?;
let job = Dispatcher::get_with_rights::<JobDispatcher>(handle, ZX_RIGHT_SET_POLICY)?;
job.set_timer_slack_policy(slack_policy)?;
Ok(())
}
#[syscall]
pub fn sys_job_create(
parent_job: HandleValue,
options: u32,
out: &mut HandleValue,
) -> Result<(), Status> {
ltracef!("parent: {:#x}\n", parent_job.raw_value());
if options != 0 {
return Err(Status::INVALID_ARGS);
}
let parent = Dispatcher::get_with_rights::<JobDispatcher>(parent_job, ZX_RIGHT_MANAGE_JOB)?;
let (handle, rights) = JobDispatcher::create(options, parent)?;
*out = ProcessDispatcher::with_current(|up| up.make_and_add_handle(handle, rights))?;
Ok(())
}
#[syscall]
pub fn sys_job_set_policy(
handle: HandleValue,
options: u32,
topic: u32,
policy_ptr: UserInPtr<u8>,
count: u32,
) -> Result<(), Status> {
ltracef!("handle {:#x}, options {}, topic {}\n", handle.raw_value(), options, topic);
match topic {
zx_types::ZX_JOB_POL_BASIC_V1 => {
job_set_policy_basic_v1(handle, options, policy_ptr, count)
}
zx_types::ZX_JOB_POL_BASIC_V2 => {
job_set_policy_basic_v2(handle, options, policy_ptr, count)
}
zx_types::ZX_JOB_POL_TIMER_SLACK => {
job_set_policy_timer_slack(handle, options, policy_ptr, count)
}
_ => Err(Status::INVALID_ARGS),
}
}
#[syscall]
pub fn sys_job_set_critical(
job_handle: HandleValue,
options: u32,
process_handle: HandleValue,
) -> Result<(), Status> {
ltracef!(
"job_handle {:#x}, options {}, process_handle {:#x}\n",
job_handle.raw_value(),
options,
process_handle.raw_value()
);
let retcode_nonzero = if options == ZX_JOB_CRITICAL_PROCESS_RETCODE_NONZERO {
true
} else if options != 0 {
return Err(Status::INVALID_ARGS);
} else {
false
};
let job = Dispatcher::get_with_rights::<JobDispatcher>(job_handle, ZX_RIGHT_DESTROY)?;
let process = Dispatcher::get_with_rights::<ProcessDispatcher>(process_handle, ZX_RIGHT_WAIT)?;
process.set_critical_to_job(job, retcode_nonzero)?;
Ok(())
}