Skip to main content

starnix_modules_syscalls/
misc.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use fidl_fuchsia_buildinfo as buildinfo;
6use fuchsia_component::client::connect_to_protocol_sync;
7use fuchsia_rcu::RcuReadScope;
8use starnix_core::arch::{ARCH_NAME, ARCH_NAME32};
9use starnix_core::mm::{MemoryAccessor, MemoryAccessorExt, PAGE_SIZE};
10use starnix_core::security;
11use starnix_core::task::CurrentTask;
12use starnix_core::vfs::FsString;
13use starnix_logging::{log_error, track_stub};
14use starnix_syscalls::decls::SyscallDecl;
15use starnix_syscalls::{SUCCESS, SyscallResult};
16use starnix_types::user_buffer::MAX_RW_COUNT;
17use starnix_uapi::auth::{CAP_SYS_ADMIN, CAP_SYS_MODULE};
18use starnix_uapi::errors::Errno;
19use starnix_uapi::personality::PersonalityFlags;
20use starnix_uapi::user_address::{MultiArchUserRef, UserAddress, UserCString, UserRef};
21use starnix_uapi::version::KERNEL_RELEASE;
22use starnix_uapi::{
23    EFAULT, GRND_NONBLOCK, GRND_RANDOM, c_char, errno, error, from_status_like_fdio, uapi, utsname,
24};
25
26uapi::check_arch_independent_layout! {
27    utsname {
28        sysname,
29        nodename,
30        release,
31        version,
32        machine,
33        domainname,
34    }
35}
36
37pub fn do_uname(current_task: &CurrentTask, result: &mut utsname) -> Result<(), Errno> {
38    fn init_array(fixed: &mut [c_char; 65], init: &[u8]) {
39        let len = init.len();
40        #[allow(
41            clippy::undocumented_unsafe_blocks,
42            reason = "Force documented unsafe blocks in Starnix"
43        )]
44        fixed[..len].copy_from_slice(zerocopy::transmute_ref!(init))
45    }
46
47    init_array(&mut result.sysname, b"Linux");
48    if current_task.thread_group().read().personality.contains(PersonalityFlags::UNAME26) {
49        init_array(&mut result.release, b"2.6.40-starnix");
50    } else {
51        init_array(&mut result.release, KERNEL_RELEASE.as_bytes());
52    }
53
54    let version = current_task.kernel().build_version.get_or_try_init(|| {
55        let proxy =
56            connect_to_protocol_sync::<buildinfo::ProviderMarker>().map_err(|_| errno!(ENOENT))?;
57        let buildinfo = proxy.get_build_info(zx::MonotonicInstant::INFINITE).map_err(|e| {
58            log_error!("FIDL error getting build info: {e}");
59            errno!(EIO)
60        })?;
61        Ok(buildinfo.version.unwrap_or_else(|| "starnix".to_string()))
62    })?;
63
64    init_array(&mut result.version, version.as_bytes());
65
66    let personality = current_task.thread_group().read().personality;
67    let machine = if personality.execution_domain() == (uapi::PER_LINUX32 as u32) {
68        ARCH_NAME32
69    } else {
70        ARCH_NAME
71    };
72    init_array(&mut result.machine, machine);
73
74    {
75        // Get the UTS namespace from the perspective of this task.
76        let task_state = current_task.read();
77        let uts_ns = task_state.uts_ns.read();
78        init_array(&mut result.nodename, uts_ns.hostname.as_slice());
79        init_array(&mut result.domainname, uts_ns.domainname.as_slice());
80    }
81    Ok(())
82}
83
84pub fn sys_uname(current_task: &CurrentTask, name: UserRef<utsname>) -> Result<(), Errno> {
85    let mut result = utsname {
86        sysname: [0; 65],
87        nodename: [0; 65],
88        release: [0; 65],
89        version: [0; 65],
90        machine: [0; 65],
91        domainname: [0; 65],
92    };
93    do_uname(current_task, &mut result)?;
94    current_task.write_object(name, &result)?;
95    Ok(())
96}
97
98pub fn sys_sysinfo(
99    current_task: &CurrentTask,
100    info: MultiArchUserRef<uapi::sysinfo, uapi::arch32::sysinfo>,
101) -> Result<(), Errno> {
102    let total_ram = zx::system_get_physmem();
103    let num_procs = current_task.kernel().pids.get_thread_groups(&RcuReadScope::new()).count();
104
105    track_stub!(TODO("https://fxbug.dev/297374270"), "compute system load");
106    let loads = [0; 3];
107
108    track_stub!(TODO("https://fxbug.dev/322874530"), "compute actual free ram usage");
109    let freeram = total_ram / 8;
110
111    let result = uapi::sysinfo {
112        uptime: (zx::MonotonicInstant::get() - zx::MonotonicInstant::ZERO).into_seconds(),
113        loads,
114        totalram: total_ram,
115        freeram,
116        procs: num_procs.try_into().map_err(|_| errno!(EINVAL))?,
117        mem_unit: 1,
118        ..Default::default()
119    };
120
121    current_task.write_multi_arch_object(info, result)?;
122    Ok(())
123}
124
125// Used to read a hostname or domainname from task memory
126fn read_name(current_task: &CurrentTask, name: UserCString, len: u64) -> Result<FsString, Errno> {
127    const MAX_LEN: usize = 64;
128    let len = len as usize;
129
130    if len > MAX_LEN {
131        return error!(EINVAL);
132    }
133
134    // Read maximum characters and mark the null terminator.
135    let mut name = current_task.read_c_string_to_vec(name, MAX_LEN)?;
136
137    // Syscall may have specified an even smaller length, so trim to the requested length.
138    if len < name.len() {
139        name.truncate(len);
140    }
141    Ok(name)
142}
143
144pub fn sys_sethostname(
145    current_task: &CurrentTask,
146    hostname: UserCString,
147    len: u64,
148) -> Result<SyscallResult, Errno> {
149    security::check_task_capable(current_task, CAP_SYS_ADMIN)?;
150
151    let hostname = read_name(current_task, hostname, len)?;
152
153    let task_state = current_task.read();
154    let mut uts_ns = task_state.uts_ns.write();
155    uts_ns.hostname = hostname;
156
157    Ok(SUCCESS)
158}
159
160pub fn sys_setdomainname(
161    current_task: &CurrentTask,
162    domainname: UserCString,
163    len: u64,
164) -> Result<SyscallResult, Errno> {
165    security::check_task_capable(current_task, CAP_SYS_ADMIN)?;
166
167    let domainname = read_name(current_task, domainname, len)?;
168
169    let task_state = current_task.read();
170    let mut uts_ns = task_state.uts_ns.write();
171    uts_ns.domainname = domainname;
172
173    Ok(SUCCESS)
174}
175
176pub fn sys_getrandom(
177    current_task: &CurrentTask,
178    start_addr: UserAddress,
179    size: usize,
180    flags: u32,
181) -> Result<usize, Errno> {
182    if flags & !(GRND_RANDOM | GRND_NONBLOCK) != 0 {
183        return error!(EINVAL);
184    }
185
186    // Copy random bytes in up-to-page-size chunks, stopping either when all the user-requested
187    // space has been written to or when we fault.
188    let mut bytes_written = 0;
189    let mut bounce_buffer = vec![0u8; std::cmp::min(*PAGE_SIZE as usize, size)];
190
191    let bytes_to_write = std::cmp::min(size, *MAX_RW_COUNT);
192
193    while bytes_written < bytes_to_write {
194        let chunk_start = start_addr.saturating_add(bytes_written);
195        let chunk_len = std::cmp::min(*PAGE_SIZE as usize, size - bytes_written);
196
197        let chunk = &mut bounce_buffer[..chunk_len];
198        starnix_crypto::cprng_draw(chunk);
199        match current_task.write_memory_partial(chunk_start, chunk) {
200            Ok(n) => {
201                bytes_written += n;
202
203                // If we didn't write the whole chunk then we faulted. Don't try to write any more.
204                if n < chunk_len {
205                    break;
206                }
207            }
208
209            // write_memory_partial fails if no bytes were written, but we might have
210            // written bytes already.
211            Err(e) if e.code.error_code() == EFAULT && bytes_written > 0 => break,
212            Err(e) => return Err(e),
213        }
214    }
215
216    Ok(bytes_written)
217}
218
219pub fn sys_sched_yield(_current_task: &CurrentTask) -> Result<(), Errno> {
220    // SAFETY: This is unsafe because it is a syscall. zx_thread_legacy_yield is always safe.
221    let status = unsafe { zx::sys::zx_thread_legacy_yield(0) };
222    zx::Status::ok(status).map_err(|status| from_status_like_fdio!(status))
223}
224
225pub fn sys_unknown(
226    current_task: &CurrentTask,
227    syscall_number: u64,
228) -> Result<SyscallResult, Errno> {
229    let decl = SyscallDecl::from_number(syscall_number, current_task.thread_state.arch_width());
230    track_stub!(TODO("https://fxbug.dev/322874143"), decl.name(), syscall_number);
231
232    // TODO(https://fxbug.dev/454657040) We should send SIGSYS once we have signals.
233    error!(ENOSYS)
234}
235
236pub fn sys_personality(current_task: &CurrentTask, persona: u32) -> Result<SyscallResult, Errno> {
237    let mut state = current_task.task.thread_group().write();
238    let previous_value = state.personality.update_from_syscall(persona);
239    Ok(previous_value.into())
240}
241
242pub fn sys_delete_module(
243    current_task: &CurrentTask,
244    user_name: UserCString,
245    _flags: u32,
246) -> Result<SyscallResult, Errno> {
247    security::check_task_capable(current_task, CAP_SYS_MODULE)?;
248    // According to LTP test delete_module02.c
249    const MODULE_NAME_LEN: usize = 64 - std::mem::size_of::<u64>();
250    let _name = current_task.read_c_string_to_vec(user_name, MODULE_NAME_LEN)?;
251    // We don't ever have any modules loaded.
252    error!(ENOENT)
253}
254
255// Syscalls for arch32 usage
256#[cfg(target_arch = "aarch64")]
257mod arch32 {
258    pub use super::{sys_sysinfo as sys_arch32_sysinfo, sys_uname as sys_arch32_uname};
259}
260
261#[cfg(target_arch = "aarch64")]
262pub use arch32::*;