keos_project1/
syscall.rs

1//! # System call abi for x86_64.
2//!
3//! Operating systems provide an abstraction of hardware resources to user
4//! programs, allowing them to interact with hardware without needing to
5//! understand its complexities. The kernel is responsible for managing
6//! resources such as memory, processes, and input/output devices, while
7//! offering a simplified interface for user programs. System calls serve as a
8//! crucial interface between user applications and the kernel, providing an
9//! additional layer of abstraction that enables programs to request
10//! services like file I/O and process management, without directly dealing with
11//! the hardware.
12//!
13//! ## System Call Details
14//!
15//! The operating system deals with **software exceptions**, which occur
16//! due to program execution errors such as a page fault or division by zero.
17//! **Exceptions** are also the mechanism by which a user program requests
18//! services from the operating system. These service requests are **system
19//! calls**.
20//!
21//! In traditional x86 architecture, system calls were handled like any other
22//! software exception through the `int` instruction. However, in x86-64
23//! architecture, manufacturers introduced the `syscall` instruction, which
24//! provides a fast and efficient way to invoke system calls.
25//!
26//! In modern systems, the `syscall` instruction is the most commonly used means
27//! of invoking system calls. When a user program wants to make a system call,
28//! it invokes the `syscall` instruction. The system call number and any
29//! additional arguments are placed in registers before invoking `syscall`. Here
30//! are the key details:
31//!
32//! 1. The **system call number** is passed in the `%rax` register.
33//! 2. The **arguments** are passed in the registers `%rdi`, `%rsi`, `%rdx`,
34//!    `%r10`, `%r8`, and `%r9`. Specifically:
35//!    - `%rdi`: First argument
36//!    - `%rsi`: Second argument
37//!    - `%rdx`: Third argument
38//!    - `%r10`: Fourth argument
39//!    - `%r8`: Fifth argument
40//!    - `%r9`: Sixth argument
41//! 3. The **return value** is stored to the `%rax` register.
42//!
43//! ## Handling System Call in KeOS
44//!
45//! When the `syscall` instruction is invoked, the KeOS kernel serves the
46//! requests by calling the [`Task::syscall`] method with the state of the CPU
47//! captured in a structure called [`Registers`]. This structure contains the
48//! state of all registers, which is accessible to the handler and allows for
49//! the manipulation of register values (including the return value) within the
50//! handler.
51//!
52//! On the beginning of the [`Task::syscall`] method, KeOS parses the arguments
53//! of the system call by calling the [`SyscallAbi::from_registers`], which is
54//! currently marked with `todo!()`. Your job is to extend this to retrieve
55//! system call number and arguments according to the abi.
56//!
57//! After acquiring the [`SyscallAbi`] struct, KeOS dispatches the system call
58//! handler based on the parsed system call number. This system call handler
59//! handles the requests and returns the `Result<usize, KernelError>` to
60//! indicate the result.
61//!
62//! ## Error Handling via `Result` Type
63//!
64//! Proper error handling is crucial for maintaining system stability and
65//! ensuring that unexpected conditions do not lead to crashes or undefined
66//! behavior. Errors incured by user **MUST NOT** stop the kernel.
67//! When an error occurs, ensure that the system call returns an appropriate
68//! error code rather than panicking or causing undefined behavior.
69//! Providing meaningful error messages can also improve debugging and user
70//! experience. Properly handling edge cases will help ensure a robust and
71//! stable kernel implementation.
72//!
73//! To this end, KeOS combines the rust's `Result` type with [`KernelError`]
74//! type to enumerate all possible errors that kernel can make.
75//! For example, when implementing system call handlers, you can consider the
76//! following error conditions:
77//!
78//! - [`KernelError::BadFileDescriptor`]: Attempting to operate on a file
79//!   descriptor that is not open.
80//! - [`KernelError::InvalidArgument`]: A process tries to perform an operation
81//!   it lacks permission for (e.g., writing to a read-only file).
82//! - [`KernelError::NoSuchEntry`]: A process attempts to open a non-existent
83//!   file without specifying a creation flag.
84//! - [`KernelError::BadAddress`]: A process attempts to access an unmapped or
85//!   kernel memory.
86//!
87//! During the execution of a function, you might confront the errors. In such
88//! cases, you should **never** handle the errors by panicking the kernel.
89//! Instead, you propagates the error with `?` operator to the
90//! [`Task::syscall`], which conveys the error code to the userspace. Consult
91//! with [`Rust Book`] for how to use `?` operator.
92//!
93//! ### Returning Value to User
94//! The epilog of the [`Task::syscall`] captures both the result and error of
95//! the function, interprets them in to the `usize`, and return the values into
96//! the user space. You can update the user program's registers by directly
97//! modify the fields of [`Registers`] captured in the [`SyscallAbi`].
98//!
99//! ## Implementation Requirements
100//! You need to implement the followings:
101//! - [`SyscallAbi::from_registers`]
102//! - [`SyscallAbi::set_return_value`]
103//!
104//! After implementing both methods, move on to the next [`Section`].
105//!
106//! [`Task::syscall`]: ../../keos/task/trait.Task.html#tymethod.syscall
107//! [`Registers`]: ../../keos/syscall/struct.Registers.html
108//! [`Rust Book`]: <https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html>
109//! [`Section`]: crate::file_struct
110
111use keos::{KernelError, syscall::Registers};
112
113/// A struct representing the system call ABI (Application Binary Interface).
114///
115/// This struct provides a way to access and manipulate the system call's
116/// arguments and return values in the context of the system call handler. It
117/// stores the system call number and up to six arguments that are passed to the
118/// kernel during a system call, as well as a mutable reference to the CPU
119/// registers ([`Registers`]) which hold the current state of the system call.
120///
121/// The [`SyscallAbi`] struct abstracts away the management of system call
122/// parameters and return values, making it easier to implement and handle
123/// system calls in the kernel.
124///
125/// [`Registers`]: ../../keos/syscall/struct.Registers.html
126pub struct SyscallAbi<'a> {
127    /// The system call number that identifies the requested system service.
128    pub sysno: usize,
129    /// First argument for the system call.
130    pub arg1: usize,
131    /// Second argument for the system call.
132    pub arg2: usize,
133    /// Third argument for the system call.
134    pub arg3: usize,
135    /// Fourth argument for the system call.
136    pub arg4: usize,
137    /// Fifth argument for the system call.
138    pub arg5: usize,
139    ///Sixth argument for the system call.
140    pub arg6: usize,
141    /// A mutable reference to the [`Registers`] structure, which holds the
142    /// state of the CPU registers. It is used to manipulate the system
143    /// call return value and access any state needed for the call.
144    pub regs: &'a mut Registers,
145}
146
147impl<'a> SyscallAbi<'a> {
148    /// Constructs a [`SyscallAbi`] instance from the provided registers.
149    ///
150    /// This function extracts the system call number and arguments from the
151    /// [`Registers`] struct and initializes a new [`SyscallAbi`] struct.
152    ///
153    /// # Parameters
154    ///
155    /// - `regs`: A mutable reference to the [`Registers`] structure that
156    ///   contains the current state of the CPU registers.
157    ///
158    /// # Returns
159    ///
160    /// Returns an instance of [`SyscallAbi`] populated with the system call
161    /// number and arguments extracted from the provided registers.
162    ///
163    pub fn from_registers(regs: &'a mut Registers) -> Self {
164        todo!()
165    }
166
167    /// Sets the return value for the system call.
168    ///
169    /// This function modifies the `%rax` register to indicate the result of the
170    /// system call. If the system call was successful, it sets `%rax` to the
171    /// returned value. If the system call encountered an error, it sets `%rax`
172    /// to the corresponding error code with the [`KernelError::into_usize`]
173    /// enum.
174    ///
175    /// # Parameters
176    ///
177    /// - `return_val`: A `Result` indicating either the success value
178    ///   (`Ok(value)`) or the error type (`Err(KernelError)`).
179    pub fn set_return_value(self, return_val: Result<usize, KernelError>) {
180        // Set the return value in the registers based on the result.
181        todo!()
182    }
183}