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