kernel/abi/linux/riscv64/proc.rs
1use crate::{
2 abi::linux::riscv64::LinuxRiscv64Abi,
3 arch::{Trapframe, get_cpu},
4 sched::scheduler::get_scheduler,
5 task::{CloneFlags, mytask},
6};
7
8// /// VFS v2 helper function for path absolutization
9// /// TODO: Move this to a shared helper module when VFS v2 provides public API
10// fn to_absolute_path_v2(task: &crate::task::Task, path: &str) -> Result<String, ()> {
11// if path.starts_with('/') {
12// Ok(path.to_string())
13// } else {
14// let cwd = task.cwd.clone().ok_or(())?;
15// let mut absolute_path = cwd;
16// if !absolute_path.ends_with('/') {
17// absolute_path.push('/');
18// }
19// absolute_path.push_str(path);
20// // Simple normalization (removes "//", ".", etc.)
21// let mut components = alloc::vec::Vec::new();
22// for comp in absolute_path.split('/') {
23// match comp {
24// "" | "." => {},
25// ".." => { components.pop(); },
26// _ => components.push(comp),
27// }
28// }
29// Ok("/".to_string() + &components.join("/"))
30// }
31// }
32
33// /// Helper function to replace the missing get_path_str function
34// /// TODO: This should be moved to a shared helper when VFS v2 provides public API
35// fn get_path_str_v2(ptr: *const u8) -> Result<String, ()> {
36// const MAX_PATH_LENGTH: usize = 128;
37// cstring_to_string(ptr, MAX_PATH_LENGTH).map(|(s, _)| s).map_err(|_| ())
38// }
39
40// pub fn sys_fork(_abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
41// let parent_task = mytask().unwrap();
42
43// trapframe.increment_pc_next(parent_task); /* Increment the program counter */
44// /* Save the trapframe to the task before cloning */
45// parent_task.vcpu.lock().store(trapframe);
46
47// /* Clone the task */
48// match parent_task.clone_task(CloneFlags::default()) {
49// Ok(mut child_task) => {
50// let child_id = child_task.get_id();
51// child_task.vcpu.regs.reg[10] = 0; /* Set the return value (a0) to 0 in the child proc */
52// get_scheduler().add_task(child_task, get_cpu().get_cpuid());
53// /* Return the child task ID as pid to the parent proc */
54// child_id
55// },
56// Err(_) => {
57// usize::MAX /* Return -1 on error */
58// }
59// }
60// }
61
62pub fn sys_set_tid_address(abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
63 let task = mytask().unwrap();
64 let tid_ptr = trapframe.get_arg(0);
65
66 let tid_opt = (tid_ptr != 0).then_some(tid_ptr);
67 abi.thread_state_mut().clear_child_tid_ptr = tid_opt;
68
69 trapframe.increment_pc_next(task);
70
71 // Return current task namespace ID (Linux TID visible to user space)
72 task.get_namespace_id()
73}
74
75pub fn sys_exit(_abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
76 let task = mytask().unwrap();
77 task.vcpu.lock().store(trapframe);
78 let exit_code = trapframe.get_arg(0) as i32;
79
80 task.exit(exit_code);
81 get_scheduler().schedule(trapframe);
82 usize::MAX
83}
84
85pub fn sys_exit_group(_abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
86 let task = mytask().unwrap();
87 task.vcpu.lock().store(trapframe);
88 let exit_code = trapframe.get_arg(0) as i32;
89 task.exit(exit_code);
90 get_scheduler().schedule(trapframe);
91 usize::MAX
92}
93
94pub fn sys_set_robust_list(abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
95 let task = mytask().unwrap();
96 let head = trapframe.get_arg(0);
97 let len = trapframe.get_arg(1);
98
99 let head_opt = (head != 0).then_some(head);
100 let state = abi.thread_state_mut();
101 state.robust_list_head = head_opt;
102 state.robust_list_len = len as usize;
103
104 trapframe.increment_pc_next(task);
105
106 0
107}
108
109// pub fn sys_wait(_abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
110// let task = mytask().unwrap();
111// let status_ptr = trapframe.get_arg(0) as *mut i32;
112
113// for pid in task.get_children().clone() {
114// match task.wait(pid) {
115// Ok(status) => {
116// // If the child proc is exited, we can return the status
117// if status_ptr != core::ptr::null_mut() {
118// let status_ptr = task.vm_manager.translate_vaddr(status_ptr as usize).unwrap() as *mut i32;
119// unsafe {
120// *status_ptr = status;
121// }
122// }
123// trapframe.increment_pc_next(task);
124// return pid;
125// },
126// Err(error) => {
127// match error {
128// WaitError::ChildNotExited(_) => continue,
129// _ => {
130// return trapframe.get_return_value();
131// },
132// }
133// }
134// }
135// }
136
137// // No child has exited yet, block until one does
138// // xv6's wait() is equivalent to waitpid(-1), so we use the parent waker
139// let parent_waker = get_parent_waker(task.get_id());
140// parent_waker.wait(task, trapframe);
141// }
142
143#[allow(dead_code)]
144pub fn sys_kill(_abi: &mut LinuxRiscv64Abi, _trapframe: &mut Trapframe) -> usize {
145 // Implement the kill syscall
146 // This syscall is not yet implemented. Returning ENOSYS error code (-1).
147 usize::MAX
148}
149
150#[allow(dead_code)]
151pub fn sys_sbrk(_abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
152 let task = mytask().unwrap();
153 let increment = trapframe.get_arg(0) as isize; // Treat as signed increment
154 let current_brk = task.get_brk();
155 trapframe.increment_pc_next(task);
156
157 // Handle increment of 0 (query current brk)
158 if increment == 0 {
159 return current_brk;
160 }
161
162 let new_brk = if increment > 0 {
163 current_brk.checked_add(increment as usize)
164 } else {
165 // Handle negative increment (decrease brk)
166 current_brk.checked_sub((-increment) as usize)
167 };
168
169 let new_brk = match new_brk {
170 Some(brk) => brk,
171 None => {
172 // Overflow/underflow
173 use super::errno;
174 return errno::to_result(errno::ENOMEM);
175 }
176 };
177
178 match task.set_brk(new_brk) {
179 Ok(_) => {
180 let new_actual = task.get_brk();
181 // crate::println!("[brk] sbrk inc={} old={:#x} new={:#x}", increment, current_brk, new_actual);
182 new_actual
183 }
184 Err(_) => {
185 use super::errno;
186 // crate::println!("[brk] sbrk fail inc={} old={:#x}", increment, current_brk);
187 errno::to_result(errno::ENOMEM)
188 }
189 }
190}
191
192pub fn sys_brk(_abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
193 let task = mytask().unwrap();
194 let new_brk = trapframe.get_arg(0);
195 trapframe.increment_pc_next(task);
196
197 // If new_brk is 0, just return current brk (query current brk)
198 if new_brk == 0 {
199 return task.get_brk();
200 }
201
202 let _old = task.get_brk();
203 match task.set_brk(new_brk) {
204 Ok(_) => {
205 let actual = task.get_brk();
206 // crate::println!("[brk] brk req={:#x} old={:#x} -> {:#x}", new_brk, old, actual);
207 actual
208 }
209 Err(_) => {
210 let cur = task.get_brk();
211 // crate::println!("[brk] brk fail req={:#x} keep={:#x}", new_brk, cur);
212 cur
213 }
214 }
215}
216
217// pub fn sys_chdir(_abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
218// let task = mytask().unwrap();
219// trapframe.increment_pc_next(task);
220
221// let path_ptr = task.vm_manager.translate_vaddr(trapframe.get_arg(0) as usize).unwrap() as *const u8;
222// let path = match get_path_str_v2(path_ptr) {
223// Ok(p) => match to_absolute_path_v2(&task, &p) {
224// Ok(abs_path) => abs_path,
225// Err(_) => return usize::MAX,
226// },
227// Err(_) => return usize::MAX, /* -1 */
228// };
229
230// // Try to open the file
231// let file = match task.vfs.read().clone() {
232// Some(vfs) => vfs.open(&path, 0),
233// None => return usize::MAX, // VFS not initialized
234// };
235// if file.is_err() {
236// return usize::MAX; // -1
237// }
238// let kernel_obj = file.unwrap();
239// let file_handle = kernel_obj.as_file().unwrap();
240// // Check if the file is a directory
241// if file_handle.metadata().unwrap().file_type != FileType::Directory {
242// return usize::MAX; // -1
243// }
244
245// task.cwd = Some(path); // Update the current working directory
246
247// 0
248// }
249
250pub fn sys_getpid(_abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
251 let task = mytask().unwrap();
252 // Return TGID for Linux semantics; fallback to Task ID if unset
253 let tgid = _abi.thread_state().tgid;
254 trapframe.increment_pc_next(task);
255 if tgid != 0 { tgid } else { task.get_id() }
256}
257
258pub fn sys_getppid(_abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
259 let task = mytask().unwrap();
260 trapframe.increment_pc_next(task);
261 task.get_parent_id().unwrap_or(1) // Return parent PID or 1 if none
262}
263
264/// Linux gettid system call implementation
265/// Returns the calling thread ID (TID). For now, this equals Scarlet Task ID.
266pub fn sys_gettid(_abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
267 let task = mytask().unwrap();
268 trapframe.increment_pc_next(task);
269 task.get_id()
270}
271
272/// Linux prctl system call (syscall 167)
273///
274/// Operations on a process or thread. This is a stub implementation
275/// that returns success for common operations.
276///
277/// Arguments:
278/// - arg0: option (PR_* operation)
279/// - arg1-arg4: operation-specific arguments
280///
281/// Returns:
282/// - 0 on success
283/// - usize::MAX (Linux -1) for unsupported operations
284pub fn sys_prctl(_abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
285 let task = mytask().unwrap();
286 let option = trapframe.get_arg(0) as i32;
287 let _arg2 = trapframe.get_arg(1);
288 let _arg3 = trapframe.get_arg(2);
289 let _arg4 = trapframe.get_arg(3);
290 let _arg5 = trapframe.get_arg(4);
291
292 trapframe.increment_pc_next(task);
293
294 crate::println!(
295 "[stub] sys_prctl: option={}, arg2={:#x}, arg3={:#x}, arg4={:#x}, arg5={:#x}",
296 option,
297 _arg2,
298 _arg3,
299 _arg4,
300 _arg5
301 );
302
303 // Common PR_* operations (from include/uapi/linux/prctl.h)
304 // For now, just return success for all operations
305 // Specific operations can be implemented as needed
306 0
307}
308
309pub fn sys_setpgid(_abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
310 let task = mytask().unwrap();
311 let _pid = trapframe.get_arg(0);
312 let _pgid = trapframe.get_arg(1);
313 trapframe.increment_pc_next(task);
314 0 // Always succeed
315}
316
317pub fn sys_getpgid(_abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
318 let task = mytask().unwrap();
319 let _pid = trapframe.get_arg(0);
320 trapframe.increment_pc_next(task);
321 task.get_id() // Return current task ID as process group ID
322}
323
324pub fn sys_prlimit64(_abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
325 let task = mytask().unwrap();
326 let _pid = trapframe.get_arg(0) as i32;
327 let _resource = trapframe.get_arg(1);
328 let _new_rlim_ptr = trapframe.get_arg(2);
329 let old_rlim_ptr = trapframe.get_arg(3);
330
331 trapframe.increment_pc_next(task);
332
333 // If old_rlim is requested, write some reasonable default values
334 if old_rlim_ptr != 0 {
335 if let Some(old_rlim_paddr) = task.vm_manager.translate_vaddr(old_rlim_ptr) {
336 unsafe {
337 // Write a simple rlimit structure with high limits
338 // struct rlimit { rlim_t rlim_cur; rlim_t rlim_max; }
339 let rlimit = old_rlim_paddr as *mut [u64; 2];
340 *rlimit = [
341 0xFFFFFFFF, // rlim_cur - current limit (high value)
342 0xFFFFFFFF, // rlim_max - maximum limit (high value)
343 ];
344 }
345 }
346 }
347
348 0 // Always succeed
349}
350
351pub fn sys_getuid(_abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
352 let task = mytask().unwrap();
353 trapframe.increment_pc_next(task);
354
355 0 // Return 0 for the root user (UID 0)
356}
357
358pub fn sys_geteuid(_abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
359 let task = mytask().unwrap();
360 trapframe.increment_pc_next(task);
361
362 0 // Return 0 for the root user (EUID 0)
363}
364
365pub fn sys_getgid(_abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
366 let task = mytask().unwrap();
367 trapframe.increment_pc_next(task);
368
369 0 // Return 0 for the root group (GID 0)
370}
371
372pub fn sys_getegid(_abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
373 let task = mytask().unwrap();
374 trapframe.increment_pc_next(task);
375
376 0 // Return 0 for the root group (EGID 0)
377}
378
379/// Linux utsname structure for uname system call
380/// This structure must match Linux's struct utsname layout
381#[repr(C)]
382#[derive(Debug, Clone, Copy)]
383pub struct UtsName {
384 /// System name (e.g., "Linux")
385 pub sysname: [u8; 65],
386 /// Node name (hostname)
387 pub nodename: [u8; 65],
388 /// Release (kernel version)
389 pub release: [u8; 65],
390 /// Version (kernel build info)
391 pub version: [u8; 65],
392 /// Machine (hardware architecture)
393 pub machine: [u8; 65],
394 /// Domain name (GNU extension)
395 pub domainname: [u8; 65],
396}
397
398impl UtsName {
399 /// Create a new UtsName with Scarlet system information
400 pub fn new() -> Self {
401 let mut uts = UtsName {
402 sysname: [0; 65],
403 nodename: [0; 65],
404 release: [0; 65],
405 version: [0; 65],
406 machine: [0; 65],
407 domainname: [0; 65],
408 };
409
410 // System name - identify as Linux for compatibility
411 let sysname = b"Linux";
412 uts.sysname[..sysname.len()].copy_from_slice(sysname);
413
414 // Node name (hostname)
415 let nodename = b"scarlet";
416 uts.nodename[..nodename.len()].copy_from_slice(nodename);
417
418 // Release (kernel version)
419 let release = b"6.1.0-scarlet_linux_abi_module";
420 uts.release[..release.len()].copy_from_slice(release);
421
422 // Version (build info)
423 let version = b"#1 SMP Scarlet";
424 uts.version[..version.len()].copy_from_slice(version);
425
426 // Machine (architecture)
427 let machine = b"riscv64";
428 uts.machine[..machine.len()].copy_from_slice(machine);
429
430 // Domain name
431 let domainname = b"(none)";
432 uts.domainname[..domainname.len()].copy_from_slice(domainname);
433
434 uts
435 }
436}
437
438/// Linux uname system call implementation
439///
440/// Returns system information including system name, hostname, kernel version,
441/// and hardware architecture. This provides compatibility with Linux applications
442/// that query system information.
443///
444/// # Arguments
445/// - buf: Pointer to utsname structure to fill
446///
447/// # Returns
448/// - 0 on success
449/// - usize::MAX on error (-1 in Linux)
450pub fn sys_uname(_abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
451 let task = mytask().unwrap();
452 let buf_ptr = trapframe.get_arg(0);
453
454 // Increment PC to avoid infinite loop
455 trapframe.increment_pc_next(task);
456
457 // Translate user space pointer
458 let buf_vaddr = match task.vm_manager.translate_vaddr(buf_ptr) {
459 Some(addr) => addr as *mut UtsName,
460 None => return usize::MAX, // Invalid address
461 };
462
463 if buf_vaddr.is_null() {
464 return usize::MAX; // NULL pointer
465 }
466
467 // Create and copy system information
468 let uts = UtsName::new();
469 unsafe {
470 *buf_vaddr = uts;
471 }
472
473 0 // Success
474}
475
476/// Linux sys_clone implementation for RISC-V64 ABI
477///
478/// RISC-V64 clone argument order (Linux ABI):
479/// long clone(unsigned long flags, void *stack, int *parent_tid, unsigned long tls, int *child_tid);
480///
481/// Arguments:
482/// - flags: clone flags (CLONE_VM, CLONE_FS, etc.)
483/// - stack: child stack pointer (NULL to duplicate parent stack)
484/// - parent_tid: pointer to store parent TID (for CLONE_PARENT_SETTID)
485/// - child_tid: pointer to store child TID (for CLONE_CHILD_SETTID/CLONE_CHILD_CLEARTID)
486/// - tls: TLS (Thread Local Storage) pointer
487pub fn sys_clone(abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
488 let parent_task = match mytask() {
489 Some(t) => t,
490 None => return usize::MAX,
491 };
492
493 let flags = trapframe.get_arg(0);
494 let child_stack = trapframe.get_arg(1);
495 let parent_tid_ptr = trapframe.get_arg(2) as *mut i32; // a2
496 let tls = trapframe.get_arg(3); // a3 (RISC-V: TLS here)
497 let child_tid_ptr = trapframe.get_arg(4) as *mut i32; // a4
498
499 let parent_tid_opt = (!parent_tid_ptr.is_null()).then_some(parent_tid_ptr as usize);
500 let child_tid_opt = (!child_tid_ptr.is_null()).then_some(child_tid_ptr as usize);
501 {
502 let state = abi.thread_state_mut();
503 state.parent_tid_ptr = parent_tid_opt;
504 state.child_tid_ptr = child_tid_opt;
505 if (flags & CLONE_CHILD_CLEARTID) != 0 {
506 state.clear_child_tid_ptr = child_tid_opt;
507 }
508 if (flags & CLONE_SETTLS) != 0 {
509 state.tls_pointer = Some(tls);
510 }
511 }
512
513 // Linux clone flags
514 const CLONE_VM: usize = 0x00000100;
515 const CLONE_FS: usize = 0x00000200;
516 const CLONE_FILES: usize = 0x00000400;
517 // Thread-related flags (accepted but not fully implemented yet)
518 #[allow(dead_code)]
519 const CLONE_SIGHAND: usize = 0x00000800;
520 const CLONE_THREAD: usize = 0x00010000;
521 #[allow(dead_code)]
522 const CLONE_SETTLS: usize = 0x00080000;
523 /// Set child's TID at child_tid_ptr in child's memory
524 #[allow(dead_code)]
525 const CLONE_CHILD_SETTID: usize = 0x01000000;
526 #[allow(dead_code)]
527 const CLONE_PARENT_SETTID: usize = 0x00100000;
528 #[allow(dead_code)]
529 const CLONE_CHILD_CLEARTID: usize = 0x00200000;
530
531 // Accept CLONE_THREAD/CLONE_SIGHAND for minimal thread support.
532 // Note: signal handler sharing and full thread group semantics are partial.
533 // Stash CLONE_THREAD intent so on_task_cloned can initialize child's TGID.
534 {
535 let state = abi.thread_state_mut();
536 state.pending_clone_is_thread = (flags & CLONE_THREAD) != 0;
537 }
538
539 trapframe.increment_pc_next(parent_task);
540 parent_task.vcpu.lock().store(trapframe);
541
542 // Map Linux clone flags to Scarlet CloneFlags
543 let mut cflags = CloneFlags::new();
544 if (flags & CLONE_VM) != 0 {
545 cflags.set(crate::task::CloneFlagsDef::Vm);
546 }
547 if (flags & CLONE_FS) != 0 {
548 cflags.set(crate::task::CloneFlagsDef::Fs);
549 }
550 if (flags & CLONE_FILES) != 0 {
551 cflags.set(crate::task::CloneFlagsDef::Files);
552 }
553 if (flags & CLONE_THREAD) != 0 {
554 cflags.set(crate::task::CloneFlagsDef::Thread);
555 }
556
557 let ret = match parent_task.clone_task(cflags) {
558 Ok(mut child_task) => {
559 child_task.vcpu.lock().iregs.reg[10] = 0; // a0 = 0 in child
560 // If child_stack is provided, set child's user SP
561 if child_stack != 0 {
562 child_task.vcpu.lock().set_sp(child_stack);
563 }
564 // If CLONE_SETTLS requested, set tp (x4) to tls for child
565 #[allow(non_snake_case)]
566 const CLONE_SETTLS: usize = 0x00080000;
567 if (flags & CLONE_SETTLS) != 0 {
568 child_task.vcpu.lock().iregs.reg[4] = tls; // x4 = tp
569 }
570
571 let scheduler = get_scheduler();
572 let cpu_id = get_cpu().get_cpuid();
573 let parent_id = parent_task.get_id();
574
575 // Add child and get allocated ID
576 let child_id = scheduler.add_task(child_task, cpu_id);
577
578 // Establish parent-child relationship now that both have valid IDs
579 if let Some(child) = scheduler.get_task_by_id(child_id) {
580 child.set_parent_id(parent_id);
581 }
582 if let Some(parent) = scheduler.get_task_by_id(parent_id) {
583 parent.add_child(child_id);
584 }
585
586 // Do not modify user pthread list; musl manages linkage. No safety-net writes.
587 // Handle parent TID store when CLONE_PARENT_SETTID is requested
588 if (flags & CLONE_PARENT_SETTID) != 0 && !parent_tid_ptr.is_null() {
589 if let Some(paddr) = parent_task
590 .vm_manager
591 .translate_vaddr(parent_tid_ptr as usize)
592 {
593 unsafe {
594 *(paddr as *mut i32) = child_id as i32;
595 }
596 }
597 }
598 // IMPORTANT: Only write child TID when CLONE_CHILD_SETTID is set.
599 // For CLONE_CHILD_CLEARTID, the pointer is a futex lock to clear on exit.
600 if (flags & CLONE_CHILD_SETTID) != 0 && !child_tid_ptr.is_null() {
601 if let Some(paddr) = get_scheduler()
602 .get_task_by_id(child_id)
603 .unwrap()
604 .vm_manager
605 .translate_vaddr(child_tid_ptr as usize)
606 {
607 unsafe {
608 *(paddr as *mut i32) = child_id as i32;
609 }
610 }
611 }
612 child_id
613 }
614 Err(_) => usize::MAX,
615 };
616
617 // Clear pending flag in parent after clone completes
618 abi.thread_state_mut().pending_clone_is_thread = false;
619 ret
620}
621
622/// Linux sys_setgid implementation (syscall 144)
623///
624/// Set group ID. This is a stub implementation that always succeeds.
625///
626/// Arguments:
627/// - gid: group ID to set
628///
629/// Returns:
630/// - 0 on success
631pub fn sys_setgid(_abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
632 let task = match mytask() {
633 Some(t) => t,
634 None => return usize::MAX - 1, // -EPERM
635 };
636
637 let _gid = trapframe.get_arg(0);
638
639 // Increment PC to avoid infinite loop
640 trapframe.increment_pc_next(task);
641
642 // Always succeed - group ID is ignored in this stub
643 0
644}
645
646/// Linux sys_setuid implementation (syscall 146)
647///
648/// Set user ID. This is a stub implementation that always succeeds.
649///
650/// Arguments:
651/// - uid: user ID to set
652///
653/// Returns:
654/// - 0 on success
655pub fn sys_setuid(_abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
656 let task = match mytask() {
657 Some(t) => t,
658 None => return usize::MAX - 1, // -EPERM
659 };
660
661 let _uid = trapframe.get_arg(0);
662
663 // Increment PC to avoid infinite loop
664 trapframe.increment_pc_next(task);
665
666 // Always succeed - user ID is ignored in this stub
667 0
668}
669
670///
671/// Wait for process to change state (wait4 system call).
672/// This is a stub implementation that returns immediately.
673///
674/// Arguments:
675/// Wait for process to change state (wait4 system call).
676///
677/// This is a Linux-compatible implementation that waits for child processes
678/// to exit and returns their process ID and exit status.
679///
680/// # Arguments
681/// - pid: process ID to wait for
682/// * -1: wait for any child process
683/// * >0: wait for specific child process
684/// * 0 or <-1: wait for process group (not implemented)
685/// - wstatus: pointer to store status information (can be null)
686/// - options: wait options (currently ignored - TODO: implement WNOHANG, WUNTRACED)
687/// - rusage: pointer to resource usage structure (can be null, currently ignored)
688///
689/// # Returns
690/// - On success: process ID of child that changed state
691/// - On error: negated error code (e.g., usize::MAX - 9 for -ECHILD)
692///
693/// # Errors
694/// - ECHILD: no child processes or specified child is not our child
695/// - EFAULT: invalid address for wstatus pointer
696/// - ENOSYS: unsupported operation (process groups)
697/// - EPERM: no current task context
698pub fn sys_wait4(_abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
699 use crate::task::{WaitError, get_parent_waitpid_waker};
700
701 let task = match mytask() {
702 Some(t) => t,
703 None => return usize::MAX - 1, // -EPERM
704 };
705
706 let pid = trapframe.get_arg(0) as isize;
707 let wstatus = trapframe.get_arg(1) as *mut i32;
708 let _options = trapframe.get_arg(2); // TODO: Handle WNOHANG, WUNTRACED, etc.
709 let _rusage = trapframe.get_arg(3); // TODO: Implement resource usage tracking
710
711 // Check if the task has any children
712 if task.get_children().is_empty() {
713 trapframe.increment_pc_next(task);
714 return usize::MAX - 9; // -ECHILD (no child processes)
715 }
716
717 // Minimal implementation; no verbose logging.
718
719 // Loop until a child exits or an error occurs
720 loop {
721 if pid == -1 {
722 // Wait for any child process
723 for child_pid in task.get_children().clone() {
724 match task.wait(child_pid) {
725 Ok(status) => {
726 // Child has exited, return the status
727 if wstatus != core::ptr::null_mut() {
728 match task.vm_manager.translate_vaddr(wstatus as usize) {
729 Some(phys_addr) => {
730 let status_ptr = phys_addr as *mut i32;
731 unsafe {
732 *status_ptr = status;
733 }
734 }
735 None => {
736 // Invalid address, return EFAULT
737 trapframe.increment_pc_next(task);
738 return usize::MAX - 13; // -EFAULT
739 }
740 }
741 }
742 trapframe.increment_pc_next(task);
743 return child_pid;
744 }
745 Err(error) => {
746 match error {
747 WaitError::NoSuchChild(_) => {
748 // This child is not our child
749 continue;
750 }
751 WaitError::ChildTaskNotFound(_) => {
752 // Child task not found in scheduler, continue with other children
753 continue;
754 }
755 WaitError::ChildNotExited(_) => {
756 // Child not exited yet, continue with other children
757 continue;
758 }
759 }
760 }
761 }
762 }
763
764 // No child has exited yet, block until one does
765 // Use parent waker for waitpid(-1) semantics
766 let parent_waker = get_parent_waitpid_waker(task.get_id());
767 parent_waker.wait(task.get_id(), task.get_trapframe());
768 // Woken by child exit; re-check children.
769 // Continue the loop to re-check after waking up
770 continue;
771 } else if pid > 0 {
772 // Wait for specific child process
773 let child_pid = pid as usize;
774
775 // Check if this is actually our child
776 if !task.get_children().contains(&child_pid) {
777 trapframe.increment_pc_next(task);
778 return usize::MAX - 9; // -ECHILD (not our child)
779 }
780
781 match task.wait(child_pid) {
782 Ok(status) => {
783 // Child has exited, return the status
784 if wstatus != core::ptr::null_mut() {
785 match task.vm_manager.translate_vaddr(wstatus as usize) {
786 Some(phys_addr) => {
787 let status_ptr = phys_addr as *mut i32;
788 unsafe {
789 *status_ptr = status;
790 }
791 }
792 None => {
793 // Invalid address, return EFAULT
794 trapframe.increment_pc_next(task);
795 return usize::MAX - 13; // -EFAULT
796 }
797 }
798 }
799 trapframe.increment_pc_next(task);
800 return child_pid;
801 }
802 Err(error) => {
803 match error {
804 WaitError::NoSuchChild(_) => {
805 trapframe.increment_pc_next(task);
806 return usize::MAX - 9; // -ECHILD
807 }
808 WaitError::ChildTaskNotFound(_) => {
809 trapframe.increment_pc_next(task);
810 return usize::MAX - 9; // -ECHILD
811 }
812 WaitError::ChildNotExited(_) => {
813 // Child not exited yet, wait for it
814 use crate::task::get_waitpid_waker;
815 let child_waker = get_waitpid_waker(child_pid);
816 child_waker.wait(task.get_id(), task.get_trapframe());
817 // Woken by specific child exit; re-check.
818 // Continue the loop to re-check after waking up
819 continue;
820 }
821 }
822 }
823 }
824 } else {
825 // pid <= 0 && pid != -1: wait for process group (not implemented)
826 trapframe.increment_pc_next(task);
827 return usize::MAX - 37; // -ENOSYS (function not implemented)
828 }
829 }
830}
831
832/// Linux sys_membarrier implementation (syscall 283)
833///
834/// Memory barrier system call for ensuring memory ordering between threads.
835/// This is a stub implementation that always succeeds.
836///
837/// Arguments:
838/// - cmd: membarrier command (various MEMBARRIER_CMD_* constants)
839/// - flags: flags for the command (usually 0)
840/// - cpu_id: CPU ID for per-CPU barriers (usually unused)
841///
842/// Returns:
843/// - 0 on success
844/// - Negative error code on failure
845///
846/// Note: This is a no-op stub. On a real system with multiple cores,
847/// this would issue appropriate memory barrier instructions to ensure
848/// memory ordering visibility across CPUs. For single-core or simple
849/// multi-core systems without complex memory reordering, this stub
850/// should be sufficient for most applications.
851pub fn sys_membarrier(_abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
852 let task = match mytask() {
853 Some(t) => t,
854 None => return usize::MAX - 1, // -EPERM
855 };
856
857 let _cmd = trapframe.get_arg(0);
858 let _flags = trapframe.get_arg(1);
859 let _cpu_id = trapframe.get_arg(2);
860
861 // Increment PC to avoid infinite loop
862 trapframe.increment_pc_next(task);
863
864 // Issue a memory fence to ensure all memory operations are visible
865 // This is a basic implementation - real membarrier has multiple modes
866 core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst);
867
868 // Always succeed - stub implementation
869 0
870}
871
872/// Linux sys_memfd_create - Create an anonymous file descriptor for memory mapping
873///
874/// Creates a new anonymous file descriptor that can be used for memory mapping.
875/// This is primarily used by Wayland clients for shared memory.
876///
877/// Arguments:
878/// - abi: LinuxRiscv64Abi context
879/// - trapframe: Trapframe containing syscall arguments
880/// - arg0: uname (name for the memfd - can be NULL)
881/// - arg1: flags (MFD_CLOEXEC, MFD_ALLOW_SEALING, etc.)
882///
883/// Returns:
884/// - New file descriptor on success
885/// - usize::MAX (Linux -1) on error
886pub fn sys_memfd_create(abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
887 use crate::ipc::SharedMemory;
888 use crate::object::KernelObject;
889
890 let task = match mytask() {
891 Some(t) => t,
892 None => return usize::MAX,
893 };
894
895 let _uname_ptr = trapframe.get_arg(0);
896 let flags = trapframe.get_arg(1) as u32;
897
898 // Increment PC to avoid infinite loop
899 trapframe.increment_pc_next(task);
900
901 // Parse flags (Linux memfd_create flags)
902 const MFD_CLOEXEC: u32 = 0x0001;
903 const MFD_ALLOW_SEALING: u32 = 0x0002;
904 const MFD_SEAL_SEAL: u32 = 0x0004;
905
906 let _cloexec = (flags & MFD_CLOEXEC) != 0;
907 let _allow_sealing = (flags & MFD_ALLOW_SEALING) != 0;
908 let _seal = (flags & MFD_SEAL_SEAL) != 0;
909
910 // Create shared memory object (size 0 initially, will be resized by ftruncate)
911 // Default size for Wayland SHM pools
912 const DEFAULT_SHM_SIZE: usize = crate::environment::PAGE_SIZE; // one page as starting point
913
914 let shm = match SharedMemory::new(DEFAULT_SHM_SIZE, 0x3 /* READ | WRITE */) {
915 Ok(shm) => shm,
916 Err(e) => {
917 crate::early_println!("[sys_memfd_create] Failed to create shared memory: {:?}", e);
918 return usize::MAX;
919 }
920 };
921
922 // Insert into handle table
923 let handle = match task
924 .handle_table
925 .insert(KernelObject::SharedMemory(alloc::sync::Arc::new(shm)))
926 {
927 Ok(h) => h,
928 Err(_) => {
929 crate::early_println!("[sys_memfd_create] Failed to insert handle into table");
930 return usize::MAX;
931 }
932 };
933
934 // Allocate fd for the shared memory
935 let fd = match abi.allocate_fd(handle) {
936 Ok(fd) => fd,
937 Err(_) => {
938 // Clean up on error
939 let _ = task.handle_table.remove(handle);
940 crate::early_println!("[sys_memfd_create] Failed to allocate fd");
941 return usize::MAX;
942 }
943 };
944
945 // crate::early_println!(
946 // "[sys_memfd_create] Created memfd: fd={}, handle={}",
947 // fd,
948 // handle
949 // );
950
951 fd
952}