1use 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 let as_c_char = unsafe { std::mem::transmute::<&[u8], &[c_char]>(init) };
49 fixed[..len].copy_from_slice(as_c_char)
50 }
51
52 init_array(&mut result.sysname, b"Linux");
53 if current_task.thread_group().read().personality.contains(PersonalityFlags::UNAME26) {
54 init_array(&mut result.release, b"2.6.40-starnix");
55 } else {
56 init_array(&mut result.release, KERNEL_RELEASE.as_bytes());
57 }
58
59 let version = current_task.kernel().build_version.get_or_try_init(|| {
60 let proxy =
61 connect_to_protocol_sync::<buildinfo::ProviderMarker>().map_err(|_| errno!(ENOENT))?;
62 let buildinfo = proxy.get_build_info(zx::MonotonicInstant::INFINITE).map_err(|e| {
63 log_error!("FIDL error getting build info: {e}");
64 errno!(EIO)
65 })?;
66 Ok(buildinfo.version.unwrap_or_else(|| "starnix".to_string()))
67 })?;
68
69 init_array(&mut result.version, version.as_bytes());
70 init_array(&mut result.machine, ARCH_NAME);
71
72 {
73 let task_state = current_task.read();
75 let uts_ns = task_state.uts_ns.read();
76 init_array(&mut result.nodename, uts_ns.hostname.as_slice());
77 init_array(&mut result.domainname, uts_ns.domainname.as_slice());
78 }
79 Ok(())
80}
81
82pub fn sys_uname(
83 locked: &mut Locked<Unlocked>,
84 current_task: &CurrentTask,
85 name: UserRef<utsname>,
86) -> 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(locked, current_task, &mut result)?;
96 current_task.write_object(name, &result)?;
97 Ok(())
98}
99
100pub fn sys_sysinfo(
101 _locked: &mut Locked<Unlocked>,
102 current_task: &CurrentTask,
103 info: MultiArchUserRef<uapi::sysinfo, uapi::arch32::sysinfo>,
104) -> Result<(), Errno> {
105 let page_size = zx::system_get_page_size();
106 let total_ram_pages = zx::system_get_physmem() / (page_size as u64);
107 let num_procs = current_task.kernel().pids.read().len();
108
109 track_stub!(TODO("https://fxbug.dev/297374270"), "compute system load");
110 let loads = [0; 3];
111
112 track_stub!(TODO("https://fxbug.dev/322874530"), "compute actual free ram usage");
113 let freeram = total_ram_pages / 8;
114
115 let result = uapi::sysinfo {
116 uptime: (zx::MonotonicInstant::get() - zx::MonotonicInstant::ZERO).into_seconds(),
117 loads,
118 totalram: total_ram_pages,
119 freeram,
120 procs: num_procs.try_into().map_err(|_| errno!(EINVAL))?,
121 mem_unit: page_size,
122 ..Default::default()
123 };
124
125 current_task.write_multi_arch_object(info, result)?;
126 Ok(())
127}
128
129fn read_name(current_task: &CurrentTask, name: UserCString, len: u64) -> Result<FsString, Errno> {
131 const MAX_LEN: usize = 64;
132 let len = len as usize;
133
134 if len > MAX_LEN {
135 return error!(EINVAL);
136 }
137
138 let mut name = current_task.read_c_string_to_vec(name, MAX_LEN)?;
140
141 if len < name.len() {
143 name.truncate(len);
144 }
145 Ok(name)
146}
147
148pub fn sys_sethostname(
149 _locked: &mut Locked<Unlocked>,
150 current_task: &CurrentTask,
151 hostname: UserCString,
152 len: u64,
153) -> Result<SyscallResult, Errno> {
154 security::check_task_capable(current_task, CAP_SYS_ADMIN)?;
155
156 let hostname = read_name(current_task, hostname, len)?;
157
158 let task_state = current_task.read();
159 let mut uts_ns = task_state.uts_ns.write();
160 uts_ns.hostname = hostname;
161
162 Ok(SUCCESS)
163}
164
165pub fn sys_setdomainname(
166 _locked: &mut Locked<Unlocked>,
167 current_task: &CurrentTask,
168 domainname: UserCString,
169 len: u64,
170) -> Result<SyscallResult, Errno> {
171 security::check_task_capable(current_task, CAP_SYS_ADMIN)?;
172
173 let domainname = read_name(current_task, domainname, len)?;
174
175 let task_state = current_task.read();
176 let mut uts_ns = task_state.uts_ns.write();
177 uts_ns.domainname = domainname;
178
179 Ok(SUCCESS)
180}
181
182pub fn sys_getrandom(
183 _locked: &mut Locked<Unlocked>,
184 current_task: &CurrentTask,
185 start_addr: UserAddress,
186 size: usize,
187 flags: u32,
188) -> Result<usize, Errno> {
189 if flags & !(GRND_RANDOM | GRND_NONBLOCK) != 0 {
190 return error!(EINVAL);
191 }
192
193 let mut bytes_written = 0;
196 let mut bounce_buffer = Vec::with_capacity(std::cmp::min(*PAGE_SIZE as usize, size));
197 let bounce_buffer = bounce_buffer.spare_capacity_mut();
198
199 let bytes_to_write = std::cmp::min(size, *MAX_RW_COUNT);
200
201 while bytes_written < bytes_to_write {
202 let chunk_start = start_addr.saturating_add(bytes_written);
203 let chunk_len = std::cmp::min(*PAGE_SIZE as usize, size - bytes_written);
204
205 let chunk = zx::cprng_draw_uninit(&mut bounce_buffer[..chunk_len]);
207 match current_task.write_memory_partial(chunk_start, chunk) {
208 Ok(n) => {
209 bytes_written += n;
210
211 if n < chunk_len {
213 break;
214 }
215 }
216
217 Err(e) if e.code.error_code() == EFAULT && bytes_written > 0 => break,
220 Err(e) => return Err(e),
221 }
222 }
223
224 Ok(bytes_written)
225}
226
227pub fn sys_sched_yield(
228 _locked: &mut Locked<Unlocked>,
229 _current_task: &CurrentTask,
230) -> Result<(), Errno> {
231 let status = unsafe { zx::sys::zx_thread_legacy_yield(0) };
233 zx::Status::ok(status).map_err(|status| from_status_like_fdio!(status))
234}
235
236pub fn sys_unknown(
237 _locked: &mut Locked<Unlocked>,
238 #[allow(unused_variables)] current_task: &CurrentTask,
239 syscall_number: u64,
240) -> Result<SyscallResult, Errno> {
241 let decl = SyscallDecl::from_number(syscall_number, current_task.thread_state.arch_width);
242 track_stub!(TODO("https://fxbug.dev/322874143"), decl.name(), syscall_number);
243
244 error!(ENOSYS)
246}
247
248pub fn sys_personality(
249 _locked: &mut Locked<Unlocked>,
250 current_task: &CurrentTask,
251 persona: u32,
252) -> Result<SyscallResult, Errno> {
253 let mut state = current_task.task.thread_group().write();
254 let previous_value = state.personality.bits();
255 if persona != 0xffffffff {
256 state.personality = PersonalityFlags::from_bits_retain(persona);
258 }
259 Ok(previous_value.into())
260}
261
262pub fn sys_delete_module(
263 _locked: &mut Locked<Unlocked>,
264 current_task: &CurrentTask,
265 user_name: UserCString,
266 _flags: u32,
267) -> Result<SyscallResult, Errno> {
268 security::check_task_capable(current_task, CAP_SYS_MODULE)?;
269 const MODULE_NAME_LEN: usize = 64 - std::mem::size_of::<u64>();
271 let _name = current_task.read_c_string_to_vec(user_name, MODULE_NAME_LEN)?;
272 error!(ENOENT)
274}
275
276#[cfg(target_arch = "aarch64")]
278mod arch32 {
279 pub use super::{sys_sysinfo as sys_arch32_sysinfo, sys_uname as sys_arch32_uname};
280}
281
282#[cfg(target_arch = "aarch64")]
283pub use arch32::*;