1use crate::arch::{ARCH_NAME, ARCH_NAME32};
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
70 let personality = current_task.thread_group().read().personality;
71 let machine = if personality.execution_domain() == (uapi::PER_LINUX32 as u32) {
72 ARCH_NAME32
73 } else {
74 ARCH_NAME
75 };
76 init_array(&mut result.machine, machine);
77
78 {
79 let task_state = current_task.read();
81 let uts_ns = task_state.uts_ns.read();
82 init_array(&mut result.nodename, uts_ns.hostname.as_slice());
83 init_array(&mut result.domainname, uts_ns.domainname.as_slice());
84 }
85 Ok(())
86}
87
88pub fn sys_uname(
89 locked: &mut Locked<Unlocked>,
90 current_task: &CurrentTask,
91 name: UserRef<utsname>,
92) -> Result<(), Errno> {
93 let mut result = utsname {
94 sysname: [0; 65],
95 nodename: [0; 65],
96 release: [0; 65],
97 version: [0; 65],
98 machine: [0; 65],
99 domainname: [0; 65],
100 };
101 do_uname(locked, current_task, &mut result)?;
102 current_task.write_object(name, &result)?;
103 Ok(())
104}
105
106pub fn sys_sysinfo(
107 _locked: &mut Locked<Unlocked>,
108 current_task: &CurrentTask,
109 info: MultiArchUserRef<uapi::sysinfo, uapi::arch32::sysinfo>,
110) -> Result<(), Errno> {
111 let page_size = zx::system_get_page_size();
112 let total_ram_pages = zx::system_get_physmem() / (page_size as u64);
113 let num_procs = current_task.kernel().pids.read().len();
114
115 track_stub!(TODO("https://fxbug.dev/297374270"), "compute system load");
116 let loads = [0; 3];
117
118 track_stub!(TODO("https://fxbug.dev/322874530"), "compute actual free ram usage");
119 let freeram = total_ram_pages / 8;
120
121 let result = uapi::sysinfo {
122 uptime: (zx::MonotonicInstant::get() - zx::MonotonicInstant::ZERO).into_seconds(),
123 loads,
124 totalram: total_ram_pages,
125 freeram,
126 procs: num_procs.try_into().map_err(|_| errno!(EINVAL))?,
127 mem_unit: page_size,
128 ..Default::default()
129 };
130
131 current_task.write_multi_arch_object(info, result)?;
132 Ok(())
133}
134
135fn read_name(current_task: &CurrentTask, name: UserCString, len: u64) -> Result<FsString, Errno> {
137 const MAX_LEN: usize = 64;
138 let len = len as usize;
139
140 if len > MAX_LEN {
141 return error!(EINVAL);
142 }
143
144 let mut name = current_task.read_c_string_to_vec(name, MAX_LEN)?;
146
147 if len < name.len() {
149 name.truncate(len);
150 }
151 Ok(name)
152}
153
154pub fn sys_sethostname(
155 _locked: &mut Locked<Unlocked>,
156 current_task: &CurrentTask,
157 hostname: UserCString,
158 len: u64,
159) -> Result<SyscallResult, Errno> {
160 security::check_task_capable(current_task, CAP_SYS_ADMIN)?;
161
162 let hostname = read_name(current_task, hostname, len)?;
163
164 let task_state = current_task.read();
165 let mut uts_ns = task_state.uts_ns.write();
166 uts_ns.hostname = hostname;
167
168 Ok(SUCCESS)
169}
170
171pub fn sys_setdomainname(
172 _locked: &mut Locked<Unlocked>,
173 current_task: &CurrentTask,
174 domainname: UserCString,
175 len: u64,
176) -> Result<SyscallResult, Errno> {
177 security::check_task_capable(current_task, CAP_SYS_ADMIN)?;
178
179 let domainname = read_name(current_task, domainname, len)?;
180
181 let task_state = current_task.read();
182 let mut uts_ns = task_state.uts_ns.write();
183 uts_ns.domainname = domainname;
184
185 Ok(SUCCESS)
186}
187
188pub fn sys_getrandom(
189 _locked: &mut Locked<Unlocked>,
190 current_task: &CurrentTask,
191 start_addr: UserAddress,
192 size: usize,
193 flags: u32,
194) -> Result<usize, Errno> {
195 if flags & !(GRND_RANDOM | GRND_NONBLOCK) != 0 {
196 return error!(EINVAL);
197 }
198
199 let mut bytes_written = 0;
202 let mut bounce_buffer = vec![0u8; std::cmp::min(*PAGE_SIZE as usize, size)];
203
204 let bytes_to_write = std::cmp::min(size, *MAX_RW_COUNT);
205
206 while bytes_written < bytes_to_write {
207 let chunk_start = start_addr.saturating_add(bytes_written);
208 let chunk_len = std::cmp::min(*PAGE_SIZE as usize, size - bytes_written);
209
210 let chunk = &mut bounce_buffer[..chunk_len];
211 starnix_crypto::cprng_draw(chunk);
212 match current_task.write_memory_partial(chunk_start, chunk) {
213 Ok(n) => {
214 bytes_written += n;
215
216 if n < chunk_len {
218 break;
219 }
220 }
221
222 Err(e) if e.code.error_code() == EFAULT && bytes_written > 0 => break,
225 Err(e) => return Err(e),
226 }
227 }
228
229 Ok(bytes_written)
230}
231
232pub fn sys_sched_yield(
233 _locked: &mut Locked<Unlocked>,
234 _current_task: &CurrentTask,
235) -> Result<(), Errno> {
236 let status = unsafe { zx::sys::zx_thread_legacy_yield(0) };
238 zx::Status::ok(status).map_err(|status| from_status_like_fdio!(status))
239}
240
241pub fn sys_unknown(
242 _locked: &mut Locked<Unlocked>,
243 #[allow(unused_variables)] current_task: &CurrentTask,
244 syscall_number: u64,
245) -> Result<SyscallResult, Errno> {
246 let decl = SyscallDecl::from_number(syscall_number, current_task.thread_state.arch_width());
247 track_stub!(TODO("https://fxbug.dev/322874143"), decl.name(), syscall_number);
248
249 error!(ENOSYS)
251}
252
253pub fn sys_personality(
254 _locked: &mut Locked<Unlocked>,
255 current_task: &CurrentTask,
256 persona: u32,
257) -> Result<SyscallResult, Errno> {
258 let mut state = current_task.task.thread_group().write();
259 let previous_value = state.personality.update_from_syscall(persona);
260 Ok(previous_value.into())
261}
262
263pub fn sys_delete_module(
264 _locked: &mut Locked<Unlocked>,
265 current_task: &CurrentTask,
266 user_name: UserCString,
267 _flags: u32,
268) -> Result<SyscallResult, Errno> {
269 security::check_task_capable(current_task, CAP_SYS_MODULE)?;
270 const MODULE_NAME_LEN: usize = 64 - std::mem::size_of::<u64>();
272 let _name = current_task.read_c_string_to_vec(user_name, MODULE_NAME_LEN)?;
273 error!(ENOENT)
275}
276
277#[cfg(target_arch = "aarch64")]
279mod arch32 {
280 pub use super::{sys_sysinfo as sys_arch32_sysinfo, sys_uname as sys_arch32_uname};
281}
282
283#[cfg(target_arch = "aarch64")]
284pub use arch32::*;