Skip to main content

kernel/
syscall.rs

1use core::fmt::Display;
2
3use alloc::string::String;
4
5use crate::file::File;
6use crate::fs::FsError;
7use crate::param::NOFILE;
8use crate::proc::{Proc, TrapFrame, current_proc, current_proc_and_data_mut};
9use crate::sysfile::*;
10use crate::sysproc::*;
11use crate::vm::VA;
12
13/// Syscall error codes using POSIX-standard numeric values.
14///
15/// Kernel encodes `-(error_code as isize)` in the return register (`a0`).
16/// User space decodes negative values back into `SysError` variants.
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18#[repr(u16)]
19pub enum SysError {
20    NotPermitted = 1,
21    NoEntry = 2,
22    NoProcess = 3,
23    Interrupted = 4,
24    IoError = 5,
25    InvalidExecutable = 8,
26    BadDescriptor = 9,
27    NoChildren = 10,
28    ResourceUnavailable = 11,
29    OutOfMemory = 12,
30    BadAddress = 14,
31    AlreadyExists = 17,
32    CrossDeviceLink = 18,
33    NotDirectory = 20,
34    IsDirectory = 21,
35    InvalidArgument = 22,
36    FileTableFull = 23,
37    TooManyFiles = 24,
38    NoSpace = 28,
39    TooManyLinks = 31,
40    BrokenPipe = 32,
41    NameTooLong = 36,
42    NotImplemented = 38,
43    NotEmpty = 39,
44}
45
46impl SysError {
47    /// Returns the error code for this error.
48    pub fn as_code(self) -> u16 {
49        self as u16
50    }
51
52    /// Decodes an error code into a `SysError` variant.
53    pub fn from_code(code: u16) -> Self {
54        match code {
55            1 => Self::NotPermitted,
56            2 => Self::NoEntry,
57            3 => Self::NoProcess,
58            4 => Self::Interrupted,
59            5 => Self::IoError,
60            8 => Self::InvalidExecutable,
61            9 => Self::BadDescriptor,
62            10 => Self::NoChildren,
63            11 => Self::ResourceUnavailable,
64            12 => Self::OutOfMemory,
65            14 => Self::BadAddress,
66            17 => Self::AlreadyExists,
67            18 => Self::CrossDeviceLink,
68            20 => Self::NotDirectory,
69            21 => Self::IsDirectory,
70            22 => Self::InvalidArgument,
71            23 => Self::FileTableFull,
72            24 => Self::TooManyFiles,
73            28 => Self::NoSpace,
74            31 => Self::TooManyLinks,
75            32 => Self::BrokenPipe,
76            36 => Self::NameTooLong,
77            38 => Self::NotImplemented,
78            39 => Self::NotEmpty,
79            _ => Self::InvalidArgument,
80        }
81    }
82}
83
84impl Display for SysError {
85    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
86        match self {
87            SysError::NotPermitted => write!(f, "operation not permitted"),
88            SysError::NoEntry => write!(f, "no such file or directory"),
89            SysError::NoProcess => write!(f, "no such process"),
90            SysError::Interrupted => write!(f, "interrupted"),
91            SysError::IoError => write!(f, "input/output error"),
92            SysError::InvalidExecutable => write!(f, "exec format error"),
93            SysError::BadDescriptor => write!(f, "bad file descriptor"),
94            SysError::NoChildren => write!(f, "no child processes"),
95            SysError::ResourceUnavailable => write!(f, "resource temporarily unavailable"),
96            SysError::OutOfMemory => write!(f, "cannot allocate memory"),
97            SysError::BadAddress => write!(f, "bad address"),
98            SysError::AlreadyExists => write!(f, "file exists"),
99            SysError::CrossDeviceLink => write!(f, "cross-device link"),
100            SysError::NotDirectory => write!(f, "not a directory"),
101            SysError::IsDirectory => write!(f, "is a directory"),
102            SysError::InvalidArgument => write!(f, "invalid argument"),
103            SysError::FileTableFull => write!(f, "too many open files in system"),
104            SysError::TooManyFiles => write!(f, "too many open files"),
105            SysError::NoSpace => write!(f, "no space left on device"),
106            SysError::TooManyLinks => write!(f, "too many links"),
107            SysError::BrokenPipe => write!(f, "broken pipe"),
108            SysError::NameTooLong => write!(f, "file name too long"),
109            SysError::NotImplemented => write!(f, "function not implemented"),
110            SysError::NotEmpty => write!(f, "directory not empty"),
111        }
112    }
113}
114
115impl From<FsError> for SysError {
116    fn from(e: FsError) -> Self {
117        match e {
118            FsError::OutOfBlock | FsError::OutOfInode => SysError::NoSpace,
119            FsError::OutOfFile | FsError::OutOfPipe => SysError::FileTableFull,
120            FsError::OutOfRange => SysError::InvalidArgument,
121            FsError::Read | FsError::Write => SysError::IoError,
122            FsError::Create => SysError::NoSpace,
123            FsError::Link => SysError::AlreadyExists,
124            FsError::Resolve => SysError::NoEntry,
125            FsError::Type => SysError::InvalidArgument,
126            FsError::Copy => SysError::BadAddress,
127        }
128    }
129}
130
131/// Wrapper for extracting typed syscall arguments from trapframe.
132pub struct SyscallArgs<'a> {
133    trapframe: &'a TrapFrame,
134    proc: &'static Proc,
135}
136
137impl<'a> SyscallArgs<'a> {
138    /// Creates a new SyscallArgs
139    fn new(trapframe: &'a TrapFrame, proc: &'static Proc) -> Self {
140        Self { trapframe, proc }
141    }
142
143    pub fn proc(&self) -> &Proc {
144        self.proc
145    }
146
147    /// Returns the argument at the given index as a usize.
148    pub fn get_raw(&self, index: usize) -> usize {
149        match index {
150            0 => self.trapframe.a0,
151            1 => self.trapframe.a1,
152            2 => self.trapframe.a2,
153            3 => self.trapframe.a3,
154            4 => self.trapframe.a4,
155            5 => self.trapframe.a5,
156            _ => panic!("invalid syscall argument index {}", index),
157        }
158    }
159
160    /// Returns the argument at the given index as an isize.
161    pub fn get_int(&self, index: usize) -> isize {
162        self.get_raw(index) as isize
163    }
164
165    /// Returns the argument at the given index as a virtual address.
166    ///
167    /// Does not check for legality, since `copyin`/`copyout` will do that.
168    pub fn get_addr(&self, index: usize) -> VA {
169        VA::from(self.get_raw(index))
170    }
171
172    /// Fetch the nth word-sized system call argument as a file descriptor and return both the
173    /// descriptor and the corresponding `File`.
174    pub fn get_file(&self, index: usize) -> Result<(usize, File), SysError> {
175        let fd: usize = try_log!(
176            self.get_int(index)
177                .try_into()
178                .or(Err(SysError::BadDescriptor))
179        );
180
181        if fd >= NOFILE {
182            err!(SysError::BadDescriptor);
183        }
184
185        if let Some(file) = &current_proc().data().open_files[fd] {
186            return Ok((fd, file.clone()));
187        }
188
189        err!(SysError::BadDescriptor);
190    }
191
192    /// Fetches a null-terminated string from user space.
193    pub fn fetch_string(&self, addr: VA, max: usize) -> Result<String, SysError> {
194        let (_proc, data) = current_proc_and_data_mut();
195
196        let mut result = String::with_capacity(max);
197
198        let mut buf = [0u8; 1];
199        for i in 0..max {
200            try_log!(
201                data.pagetable_mut()
202                    .copy_from(VA::from(addr.as_usize() + i), &mut buf)
203                    .map_err(|_| SysError::BadAddress)
204            );
205
206            if buf[0] == 0 {
207                return Ok(result);
208            }
209
210            result.push(buf[0] as char);
211        }
212
213        Ok(result)
214    }
215}
216
217/// System call numbers
218#[repr(usize)]
219#[derive(Debug, Clone, Copy, PartialEq, Eq)]
220pub enum Syscall {
221    Fork = 1,
222    Exit = 2,
223    Wait = 3,
224    Pipe = 4,
225    Read = 5,
226    Kill = 6,
227    Exec = 7,
228    Fstat = 8,
229    Chdir = 9,
230    Dup = 10,
231    Getpid = 11,
232    Sbrk = 12,
233    Sleep = 13,
234    Uptime = 14,
235    Open = 15,
236    Write = 16,
237    Mknod = 17,
238    Unlink = 18,
239    Link = 19,
240    Mkdir = 20,
241    Close = 21,
242}
243
244impl TryFrom<usize> for Syscall {
245    type Error = SysError;
246
247    fn try_from(value: usize) -> Result<Self, Self::Error> {
248        match value {
249            1 => Ok(Syscall::Fork),
250            2 => Ok(Syscall::Exit),
251            3 => Ok(Syscall::Wait),
252            4 => Ok(Syscall::Pipe),
253            5 => Ok(Syscall::Read),
254            6 => Ok(Syscall::Kill),
255            7 => Ok(Syscall::Exec),
256            8 => Ok(Syscall::Fstat),
257            9 => Ok(Syscall::Chdir),
258            10 => Ok(Syscall::Dup),
259            11 => Ok(Syscall::Getpid),
260            12 => Ok(Syscall::Sbrk),
261            13 => Ok(Syscall::Sleep),
262            14 => Ok(Syscall::Uptime),
263            15 => Ok(Syscall::Open),
264            16 => Ok(Syscall::Write),
265            17 => Ok(Syscall::Mknod),
266            18 => Ok(Syscall::Unlink),
267            19 => Ok(Syscall::Link),
268            20 => Ok(Syscall::Mkdir),
269            21 => Ok(Syscall::Close),
270            _ => Err(SysError::NotImplemented),
271        }
272    }
273}
274
275/// Handle a system call.
276///
277/// # Safety
278/// Called from `usertrap` in `trap.rs`.
279#[unsafe(no_mangle)]
280pub unsafe fn syscall(trapframe: &mut TrapFrame) {
281    let proc = current_proc();
282    let args = SyscallArgs::new(trapframe, proc);
283
284    // #[cfg(debug_assertions)]
285    // println!(
286    //     "syscall {} called from proc {} ({})",
287    //     trapframe.a7,
288    //     *proc.inner.lock().pid,
289    //     proc.data().name,
290    // );
291
292    let result = match Syscall::try_from(trapframe.a7) {
293        Ok(syscall) => match syscall {
294            Syscall::Fork => sys_fork(&args),
295            Syscall::Exit => sys_exit(&args),
296            Syscall::Wait => sys_wait(&args),
297            Syscall::Pipe => sys_pipe(&args),
298            Syscall::Read => sys_read(&args),
299            Syscall::Kill => sys_kill(&args),
300            Syscall::Exec => sys_exec(&args),
301            Syscall::Fstat => sys_fstat(&args),
302            Syscall::Chdir => sys_chdir(&args),
303            Syscall::Dup => sys_dup(&args),
304            Syscall::Getpid => sys_getpid(&args),
305            Syscall::Sbrk => sys_sbrk(&args),
306            Syscall::Sleep => sys_sleep(&args),
307            Syscall::Uptime => sys_uptime(&args),
308            Syscall::Open => sys_open(&args),
309            Syscall::Write => sys_write(&args),
310            Syscall::Mknod => sys_mknod(&args),
311            Syscall::Unlink => sys_unlink(&args),
312            Syscall::Link => sys_link(&args),
313            Syscall::Mkdir => sys_mkdir(&args),
314            Syscall::Close => sys_close(&args),
315        },
316        Err(e) => Err(e),
317    };
318
319    trapframe.a0 = match log!(result) {
320        Ok(v) => v,
321        Err(error) => {
322            #[cfg(debug_assertions)]
323            println!(
324                "! syscall error ({}) from proc {} ({})",
325                error,
326                *proc.inner.lock().pid,
327                proc.data().name,
328            );
329            (-(error.as_code() as isize)) as usize
330        }
331    };
332
333    // #[cfg(debug_assertions)]
334    // println!("syscall {} -> {}", trapframe.a7, trapframe.a0);
335}