kernel/abi/xv6/riscv64/
proc.rs

1use crate::{
2    arch::{Trapframe, get_cpu},
3    fs::FileType,
4    library::std::string::cstring_to_string,
5    sched::scheduler::get_scheduler,
6    task::{CloneFlags, WaitError, get_parent_waitpid_waker, mytask},
7};
8use alloc::string::{String, ToString};
9
10/// VFS v2 helper function for path absolutization using VfsManager
11fn to_absolute_path_v2(task: &crate::task::Task, path: &str) -> Result<String, ()> {
12    if path.starts_with('/') {
13        Ok(path.to_string())
14    } else {
15        let vfs = task.vfs.read().clone().ok_or(())?;
16        Ok(vfs.resolve_path_to_absolute(path))
17    }
18}
19
20/// Helper function to replace the missing get_path_str function
21/// TODO: This should be moved to a shared helper when VFS v2 provides public API
22fn get_path_str_v2(ptr: *const u8) -> Result<String, ()> {
23    const MAX_PATH_LENGTH: usize = 128;
24    cstring_to_string(ptr, MAX_PATH_LENGTH)
25        .map(|(s, _)| s)
26        .map_err(|_| ())
27}
28
29pub fn sys_fork(
30    _abi: &mut crate::abi::xv6::riscv64::Xv6Riscv64Abi,
31    trapframe: &mut Trapframe,
32) -> usize {
33    let parent_task = mytask().unwrap();
34
35    trapframe.increment_pc_next(parent_task); /* Increment the program counter */
36
37    /* Save the trapframe to the task before cloning */
38    parent_task.vcpu.lock().store(trapframe);
39
40    /* Clone the task */
41    match parent_task.clone_task(CloneFlags::default()) {
42        Ok(mut child_task) => {
43            child_task.vcpu.lock().iregs.reg[10] = 0; /* Set the return value (a0) to 0 in the child proc */
44
45            let scheduler = get_scheduler();
46            let cpu_id = get_cpu().get_cpuid();
47            let parent_id = parent_task.get_id();
48
49            // Add child and get allocated ID
50            let child_id = scheduler.add_task(child_task, cpu_id);
51
52            // Establish parent-child relationship now that both have valid IDs
53            if let Some(child) = scheduler.get_task_by_id(child_id) {
54                child.set_parent_id(parent_id);
55            }
56            if let Some(parent) = scheduler.get_task_by_id(parent_id) {
57                parent.add_child(child_id);
58            }
59
60            // Get namespace-local ID to return to user space (this is the PID visible to xv6 programs)
61            let child_namespace_id = scheduler
62                .get_task_by_id(child_id)
63                .map(|t| t.get_namespace_id())
64                .unwrap_or(0);
65
66            /* Return the child task namespace ID as pid to the parent proc */
67            child_namespace_id
68        }
69        Err(_) => {
70            usize::MAX /* Return -1 on error */
71        }
72    }
73}
74
75pub fn sys_exit(
76    _abi: &mut crate::abi::xv6::riscv64::Xv6Riscv64Abi,
77    trapframe: &mut Trapframe,
78) -> usize {
79    let task = mytask().unwrap();
80    task.vcpu.lock().store(trapframe);
81    let exit_code = trapframe.get_arg(0) as i32;
82    task.exit(exit_code);
83    get_scheduler().schedule(trapframe);
84    usize::MAX // -1 (If exit is successful, this will not be reached)
85}
86
87pub fn sys_wait(
88    _abi: &mut crate::abi::xv6::riscv64::Xv6Riscv64Abi,
89    trapframe: &mut Trapframe,
90) -> usize {
91    let task = mytask().unwrap();
92    let status_ptr = trapframe.get_arg(0) as *mut i32;
93
94    // Loop until a child exits or an error occurs
95    loop {
96        // Wait for any child process
97        for child_pid in task.get_children().clone() {
98            match task.wait(child_pid) {
99                Ok(status) => {
100                    // Child has exited, return the status
101                    if status_ptr != core::ptr::null_mut() {
102                        let status_ptr = task
103                            .vm_manager
104                            .translate_vaddr(status_ptr as usize)
105                            .unwrap() as *mut i32;
106                        unsafe {
107                            *status_ptr = status;
108                        }
109                    }
110                    trapframe.increment_pc_next(task);
111                    return child_pid;
112                }
113                Err(error) => match error {
114                    WaitError::ChildNotExited(_) => continue,
115                    _ => {
116                        trapframe.increment_pc_next(task);
117                        return usize::MAX;
118                    }
119                },
120            }
121        }
122
123        // No child has exited yet, block until one does
124        let parent_waker = get_parent_waitpid_waker(task.get_id());
125        parent_waker.wait(task.get_id(), trapframe);
126        // Continue the loop to re-check after waking up
127        continue;
128    }
129}
130
131pub fn sys_kill(
132    _abi: &mut crate::abi::xv6::riscv64::Xv6Riscv64Abi,
133    trapframe: &mut Trapframe,
134) -> usize {
135    let task = mytask().unwrap();
136    let pid = trapframe.get_arg(0) as usize;
137    let signal = trapframe.get_arg(1) as i32;
138
139    trapframe.increment_pc_next(task);
140
141    // For xv6 compatibility, only signal 9 (SIGKILL) is implemented for now
142    if signal != 9 {
143        return usize::MAX; // -1 (unsupported signal)
144    }
145
146    // Find the target task via scheduler
147    let scheduler = get_scheduler();
148    if let Some(target_task) = scheduler.get_task_by_id(pid) {
149        // For xv6 compatibility, immediately terminate the target task
150        target_task.exit(9); // SIGKILL equivalent - exit with signal 9
151        0 // Success
152    } else {
153        usize::MAX // -1 (no such process)
154    }
155}
156
157pub fn sys_sbrk(
158    _abi: &mut crate::abi::xv6::riscv64::Xv6Riscv64Abi,
159    trapframe: &mut Trapframe,
160) -> usize {
161    let task = mytask().unwrap();
162    let increment = trapframe.get_arg(0);
163    let brk = task.get_brk();
164    trapframe.increment_pc_next(task);
165    match task.set_brk(unsafe { brk.unchecked_add(increment) }) {
166        Ok(_) => brk,
167        Err(_) => usize::MAX, /* -1 */
168    }
169}
170
171pub fn sys_chdir(
172    _abi: &mut crate::abi::xv6::riscv64::Xv6Riscv64Abi,
173    trapframe: &mut Trapframe,
174) -> usize {
175    let task = mytask().unwrap();
176    trapframe.increment_pc_next(task);
177
178    let path_ptr = task
179        .vm_manager
180        .translate_vaddr(trapframe.get_arg(0) as usize)
181        .unwrap() as *const u8;
182    let path = match get_path_str_v2(path_ptr) {
183        Ok(p) => match to_absolute_path_v2(&task, &p) {
184            Ok(abs_path) => abs_path,
185            Err(_) => return usize::MAX,
186        },
187        Err(_) => return usize::MAX, /* -1 */
188    };
189
190    // Try to open the file
191    let file = match task.vfs.read().clone() {
192        Some(vfs) => vfs.open(&path, 0),
193        None => return usize::MAX, // VFS not initialized
194    };
195    if file.is_err() {
196        return usize::MAX; // -1
197    }
198    let kernel_obj = file.unwrap();
199    let file_handle = kernel_obj.as_file().unwrap();
200    // Check if the file is a directory
201    if file_handle.metadata().unwrap().file_type != FileType::Directory {
202        return usize::MAX; // -1
203    }
204
205    // Update the current working directory via VfsManager
206    if let Some(vfs) = task.vfs.read().clone() {
207        let _ = vfs.set_cwd_by_path(&path);
208    }
209
210    0
211}
212
213pub fn sys_getpid(
214    _abi: &mut crate::abi::xv6::riscv64::Xv6Riscv64Abi,
215    trapframe: &mut Trapframe,
216) -> usize {
217    let task = mytask().unwrap();
218    trapframe.increment_pc_next(task);
219    // Return namespace-local ID (this is the PID visible to xv6 programs)
220    task.get_namespace_id()
221}
222
223pub fn sys_sleep(
224    _abi: &mut crate::abi::xv6::riscv64::Xv6Riscv64Abi,
225    trapframe: &mut Trapframe,
226) -> usize {
227    let ticks = trapframe.get_arg(0) as u64;
228    let task = mytask().unwrap();
229
230    // Increment PC before sleeping to avoid infinite loop
231    trapframe.increment_pc_next(task);
232
233    // Call the blocking sleep method - this will return when sleep completes
234    task.sleep(trapframe, ticks);
235
236    // Set return value to 0 for successful sleep
237    0
238}