kernel/abi/linux/riscv64/
pipe.rs

1//! Linux RISC-V 64 pipe syscalls (minimum implementation)
2//!
3
4use crate::{
5    abi::linux::riscv64::{
6        LinuxRiscv64Abi, errno,
7        fs::{FD_CLOEXEC, O_CLOEXEC, O_NONBLOCK},
8    },
9    arch::Trapframe,
10    ipc::UnidirectionalPipe,
11    object::capability::selectable::Selectable,
12    task::mytask,
13};
14
15/// Minimal sys_pipe2 implementation for Linux ABI (returns 0 on success, -1 on error)
16pub fn sys_pipe2(abi: &mut LinuxRiscv64Abi, trapframe: &mut Trapframe) -> usize {
17    let task = match mytask() {
18        Some(t) => t,
19        None => return errno::to_result(errno::EIO),
20    };
21
22    let pipefd_user = trapframe.get_arg(0);
23    let flags = trapframe.get_arg(1) as i32;
24
25    trapframe.increment_pc_next(task);
26
27    // Validate flags: Linux pipe2 only accepts O_CLOEXEC and O_NONBLOCK.
28    let allowed = O_CLOEXEC | O_NONBLOCK;
29    if flags & !allowed != 0 {
30        return errno::to_result(errno::EINVAL);
31    }
32
33    let pipefd_ptr = match task.vm_manager.translate_vaddr(pipefd_user) {
34        Some(ptr) => ptr as *mut u32,
35        None => return errno::to_result(errno::EFAULT),
36    };
37
38    let (read_end, write_end) = UnidirectionalPipe::create_pair(4096);
39
40    let read_handle = match task.handle_table.insert(read_end) {
41        Ok(h) => h,
42        Err(_) => return errno::to_result(errno::ENFILE),
43    };
44    let write_handle = match task.handle_table.insert(write_end) {
45        Ok(h) => h,
46        Err(_) => {
47            let _ = task.handle_table.remove(read_handle);
48            return errno::to_result(errno::ENFILE);
49        }
50    };
51
52    let read_fd = match abi.allocate_fd(read_handle as u32) {
53        Ok(fd) => fd,
54        Err(_) => {
55            let _ = task.handle_table.remove(read_handle);
56            let _ = task.handle_table.remove(write_handle);
57            return errno::to_result(errno::EMFILE);
58        }
59    };
60    let write_fd = match abi.allocate_fd(write_handle as u32) {
61        Ok(fd) => fd,
62        Err(_) => {
63            let _ = abi.remove_fd(read_fd);
64            let _ = task.handle_table.remove(read_handle);
65            let _ = task.handle_table.remove(write_handle);
66            return errno::to_result(errno::EMFILE);
67        }
68    };
69
70    let mut status_flags: u32 = 0;
71    if (flags & O_NONBLOCK) != 0 {
72        status_flags |= O_NONBLOCK as u32;
73    }
74
75    if abi.set_file_status_flags(read_fd, status_flags).is_err() {
76        let _ = abi.remove_fd(write_fd);
77        let _ = abi.remove_fd(read_fd);
78        let _ = task.handle_table.remove(write_handle);
79        let _ = task.handle_table.remove(read_handle);
80        return errno::to_result(errno::EMFILE);
81    }
82    if abi.set_file_status_flags(write_fd, status_flags).is_err() {
83        let _ = abi.remove_fd(write_fd);
84        let _ = abi.remove_fd(read_fd);
85        let _ = task.handle_table.remove(write_handle);
86        let _ = task.handle_table.remove(read_handle);
87        return errno::to_result(errno::EMFILE);
88    }
89
90    if (flags & O_CLOEXEC) != 0 {
91        let _ = abi.set_fd_flags(read_fd, FD_CLOEXEC);
92        let _ = abi.set_fd_flags(write_fd, FD_CLOEXEC);
93    }
94
95    if let Some(obj) = task.handle_table.get(read_handle) {
96        if let Some(sel) = obj.as_selectable() {
97            sel.set_nonblocking((flags & O_NONBLOCK) != 0);
98        }
99    }
100    if let Some(obj) = task.handle_table.get(write_handle) {
101        if let Some(sel) = obj.as_selectable() {
102            sel.set_nonblocking((flags & O_NONBLOCK) != 0);
103        }
104    }
105
106    unsafe {
107        core::ptr::write_unaligned(pipefd_ptr, read_fd as u32);
108        core::ptr::write_unaligned(pipefd_ptr.add(1), write_fd as u32);
109    }
110
111    0
112}