blob: 4ce5bc001e2d0548eaf0082c9b96190f674f2bbe [file] [log] [blame]
// Copyright 2021 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.
use crate::arch::ARCH_NAME;
use crate::mm::{MemoryAccessor, MemoryAccessorExt, PAGE_SIZE};
use crate::security;
use crate::task::CurrentTask;
use crate::vfs::FsString;
use fidl_fuchsia_buildinfo as buildinfo;
use fuchsia_component::client::connect_to_protocol_sync;
use starnix_logging::{log_error, track_stub};
use starnix_sync::{Locked, Unlocked};
use starnix_syscalls::decls::SyscallDecl;
use starnix_syscalls::{SUCCESS, SyscallResult};
use starnix_types::user_buffer::MAX_RW_COUNT;
use starnix_uapi::auth::{CAP_SYS_ADMIN, CAP_SYS_MODULE};
use starnix_uapi::errors::Errno;
use starnix_uapi::personality::PersonalityFlags;
use starnix_uapi::user_address::{MultiArchUserRef, UserAddress, UserCString, UserRef};
use starnix_uapi::version::KERNEL_RELEASE;
use starnix_uapi::{
EFAULT, GRND_NONBLOCK, GRND_RANDOM, c_char, errno, error, from_status_like_fdio, uapi, utsname,
};
uapi::check_arch_independent_layout! {
utsname {
sysname,
nodename,
release,
version,
machine,
domainname,
}
}
pub fn do_uname(
_locked: &mut Locked<Unlocked>,
current_task: &CurrentTask,
result: &mut utsname,
) -> Result<(), Errno> {
fn init_array(fixed: &mut [c_char; 65], init: &[u8]) {
let len = init.len();
#[allow(
clippy::undocumented_unsafe_blocks,
reason = "Force documented unsafe blocks in Starnix"
)]
let as_c_char = unsafe { std::mem::transmute::<&[u8], &[c_char]>(init) };
fixed[..len].copy_from_slice(as_c_char)
}
init_array(&mut result.sysname, b"Linux");
if current_task.thread_group().read().personality.contains(PersonalityFlags::UNAME26) {
init_array(&mut result.release, b"2.6.40-starnix");
} else {
init_array(&mut result.release, KERNEL_RELEASE.as_bytes());
}
let version = current_task.kernel().build_version.get_or_try_init(|| {
let proxy =
connect_to_protocol_sync::<buildinfo::ProviderMarker>().map_err(|_| errno!(ENOENT))?;
let buildinfo = proxy.get_build_info(zx::MonotonicInstant::INFINITE).map_err(|e| {
log_error!("FIDL error getting build info: {e}");
errno!(EIO)
})?;
Ok(buildinfo.version.unwrap_or_else(|| "starnix".to_string()))
})?;
init_array(&mut result.version, version.as_bytes());
init_array(&mut result.machine, ARCH_NAME);
{
// Get the UTS namespace from the perspective of this task.
let task_state = current_task.read();
let uts_ns = task_state.uts_ns.read();
init_array(&mut result.nodename, uts_ns.hostname.as_slice());
init_array(&mut result.domainname, uts_ns.domainname.as_slice());
}
Ok(())
}
pub fn sys_uname(
locked: &mut Locked<Unlocked>,
current_task: &CurrentTask,
name: UserRef<utsname>,
) -> Result<(), Errno> {
let mut result = utsname {
sysname: [0; 65],
nodename: [0; 65],
release: [0; 65],
version: [0; 65],
machine: [0; 65],
domainname: [0; 65],
};
do_uname(locked, current_task, &mut result)?;
current_task.write_object(name, &result)?;
Ok(())
}
pub fn sys_sysinfo(
_locked: &mut Locked<Unlocked>,
current_task: &CurrentTask,
info: MultiArchUserRef<uapi::sysinfo, uapi::arch32::sysinfo>,
) -> Result<(), Errno> {
let page_size = zx::system_get_page_size();
let total_ram_pages = zx::system_get_physmem() / (page_size as u64);
let num_procs = current_task.kernel().pids.read().len();
track_stub!(TODO("https://fxbug.dev/297374270"), "compute system load");
let loads = [0; 3];
track_stub!(TODO("https://fxbug.dev/322874530"), "compute actual free ram usage");
let freeram = total_ram_pages / 8;
let result = uapi::sysinfo {
uptime: (zx::MonotonicInstant::get() - zx::MonotonicInstant::ZERO).into_seconds(),
loads,
totalram: total_ram_pages,
freeram,
procs: num_procs.try_into().map_err(|_| errno!(EINVAL))?,
mem_unit: page_size,
..Default::default()
};
current_task.write_multi_arch_object(info, result)?;
Ok(())
}
// Used to read a hostname or domainname from task memory
fn read_name(current_task: &CurrentTask, name: UserCString, len: u64) -> Result<FsString, Errno> {
const MAX_LEN: usize = 64;
let len = len as usize;
if len > MAX_LEN {
return error!(EINVAL);
}
// Read maximum characters and mark the null terminator.
let mut name = current_task.read_c_string_to_vec(name, MAX_LEN)?;
// Syscall may have specified an even smaller length, so trim to the requested length.
if len < name.len() {
name.truncate(len);
}
Ok(name)
}
pub fn sys_sethostname(
_locked: &mut Locked<Unlocked>,
current_task: &CurrentTask,
hostname: UserCString,
len: u64,
) -> Result<SyscallResult, Errno> {
security::check_task_capable(current_task, CAP_SYS_ADMIN)?;
let hostname = read_name(current_task, hostname, len)?;
let task_state = current_task.read();
let mut uts_ns = task_state.uts_ns.write();
uts_ns.hostname = hostname;
Ok(SUCCESS)
}
pub fn sys_setdomainname(
_locked: &mut Locked<Unlocked>,
current_task: &CurrentTask,
domainname: UserCString,
len: u64,
) -> Result<SyscallResult, Errno> {
security::check_task_capable(current_task, CAP_SYS_ADMIN)?;
let domainname = read_name(current_task, domainname, len)?;
let task_state = current_task.read();
let mut uts_ns = task_state.uts_ns.write();
uts_ns.domainname = domainname;
Ok(SUCCESS)
}
pub fn sys_getrandom(
_locked: &mut Locked<Unlocked>,
current_task: &CurrentTask,
start_addr: UserAddress,
size: usize,
flags: u32,
) -> Result<usize, Errno> {
if flags & !(GRND_RANDOM | GRND_NONBLOCK) != 0 {
return error!(EINVAL);
}
// Copy random bytes in up-to-page-size chunks, stopping either when all the user-requested
// space has been written to or when we fault.
let mut bytes_written = 0;
let mut bounce_buffer = Vec::with_capacity(std::cmp::min(*PAGE_SIZE as usize, size));
let bounce_buffer = bounce_buffer.spare_capacity_mut();
let bytes_to_write = std::cmp::min(size, *MAX_RW_COUNT);
while bytes_written < bytes_to_write {
let chunk_start = start_addr.saturating_add(bytes_written);
let chunk_len = std::cmp::min(*PAGE_SIZE as usize, size - bytes_written);
// Fine to index, chunk_len can't be greater than bounce_buffer.len();
let chunk = zx::cprng_draw_uninit(&mut bounce_buffer[..chunk_len]);
match current_task.write_memory_partial(chunk_start, chunk) {
Ok(n) => {
bytes_written += n;
// If we didn't write the whole chunk then we faulted. Don't try to write any more.
if n < chunk_len {
break;
}
}
// write_memory_partial fails if no bytes were written, but we might have
// written bytes already.
Err(e) if e.code.error_code() == EFAULT && bytes_written > 0 => break,
Err(e) => return Err(e),
}
}
Ok(bytes_written)
}
pub fn sys_sched_yield(
_locked: &mut Locked<Unlocked>,
_current_task: &CurrentTask,
) -> Result<(), Errno> {
// SAFETY: This is unsafe because it is a syscall. zx_thread_legacy_yield is always safe.
let status = unsafe { zx::sys::zx_thread_legacy_yield(0) };
zx::Status::ok(status).map_err(|status| from_status_like_fdio!(status))
}
pub fn sys_unknown(
_locked: &mut Locked<Unlocked>,
#[allow(unused_variables)] current_task: &CurrentTask,
syscall_number: u64,
) -> Result<SyscallResult, Errno> {
let decl = SyscallDecl::from_number(syscall_number, current_task.thread_state.arch_width);
track_stub!(TODO("https://fxbug.dev/322874143"), decl.name(), syscall_number);
// TODO(https://fxbug.dev/454657040) We should send SIGSYS once we have signals.
error!(ENOSYS)
}
pub fn sys_personality(
_locked: &mut Locked<Unlocked>,
current_task: &CurrentTask,
persona: u32,
) -> Result<SyscallResult, Errno> {
let mut state = current_task.task.thread_group().write();
let previous_value = state.personality.bits();
if persona != 0xffffffff {
// Use `from_bits_retain()` since we want to keep unknown flags.
state.personality = PersonalityFlags::from_bits_retain(persona);
}
Ok(previous_value.into())
}
pub fn sys_delete_module(
_locked: &mut Locked<Unlocked>,
current_task: &CurrentTask,
user_name: UserCString,
_flags: u32,
) -> Result<SyscallResult, Errno> {
security::check_task_capable(current_task, CAP_SYS_MODULE)?;
// According to LTP test delete_module02.c
const MODULE_NAME_LEN: usize = 64 - std::mem::size_of::<u64>();
let _name = current_task.read_c_string_to_vec(user_name, MODULE_NAME_LEN)?;
// We don't ever have any modules loaded.
error!(ENOENT)
}
// Syscalls for arch32 usage
#[cfg(target_arch = "aarch64")]
mod arch32 {
pub use super::{sys_sysinfo as sys_arch32_sysinfo, sys_uname as sys_arch32_uname};
}
#[cfg(target_arch = "aarch64")]
pub use arch32::*;