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