keos_project1/lib.rs
1//! # Project 1: System Call
2//!
3//! In Project 1, both user applications and the kernel run at the same
4//! privilege level. This is because the system does not yet support memory
5//! isolation; thus, there is no hardware-enforced boundary between user and
6//! kernel memory. In the next project, KeOS will introduce **privilege
7//! separation**, leveraging hardware features to isolate user space from the
8//! kernel, as is standard in modern operating systems.
9//!
10//! This project builds the **system call handling**, which is the groundwork
11//! for secure and structured interaction between user applications and kernel
12//! services in subsequent stages of the system.
13//!
14//! We are aware that you may not yet be familiar with the Rust language as well
15//! as KeOS apis, so this project is intended to be much easier than later
16//! projects.
17//! Please consult the [`Rust Book`] if you are unfamiliar with Rust and be
18//! familiar with Rust by solving exercises in [`Rustling`]. It is highly
19//! recommend to tring those exercises if this is your first project in Rust.
20//!
21//! ## Getting Started
22//!
23//! To begin, navigate to the `keos-project1/grader` directory and run:
24//!
25//! ```bash
26//! $ cargo run
27//! ```
28//!
29//! Initially, KeOS will panic with a "not yet implemented" message, indicating
30//! the missing components that you need to implement.
31//!
32//! ## Modifiable Files
33//! In this project, you can modify the following two files:
34//! - `syscall.rs`
35//! - `file_struct.rs`
36//!
37//! When grading, other than theses two files are overwritten to the original
38//! one.
39//!
40//! ## Project Outline
41//! - [`System Call Infrastructure`]: Extract arguments from system call
42//! requests in a structured manner.
43//! - [`File System Calls`]: Introduce system calls for file operations.
44//!
45//! [`System Call Infrastructure`]: syscall
46//! [`File System Calls`]: file_struct
47//! [`Rust Book`]: <https://doc.rust-lang.org/book/>
48//! [`Rustling`]: <https://rustlings.rust-lang.org/>
49
50#![deny(rustdoc::broken_intra_doc_links)]
51#![deny(unsafe_code)]
52#![no_std]
53#![no_main]
54
55extern crate alloc;
56#[allow(unused_imports)]
57#[macro_use]
58extern crate keos;
59
60pub mod file_struct;
61pub mod process;
62pub mod syscall;
63
64use keos::{KernelError, syscall::Registers, task::Task};
65use syscall::SyscallAbi;
66
67pub use process::Process;
68
69/// Represents system call numbers used in project1.
70///
71/// Each variant corresponds to a specific system call that can be invoked
72/// using the system call interface. The numeric values align with the
73/// syscall table in the operating system.
74#[repr(usize)]
75pub enum SyscallNumber {
76 /// Opens a file and returns a file descriptor.
77 Open = 1,
78 /// Reads data from a file descriptor.
79 Read = 2,
80 /// Writes data to a file descriptor.
81 Write = 3,
82 /// Moves the file offset of an open file.
83 Seek = 4,
84 /// Retrieves the current file offset.
85 Tell = 5,
86 /// Closes an open file descriptor.
87 Close = 6,
88 /// Create an interprocess communication channel.
89 Pipe = 7,
90}
91
92impl TryFrom<usize> for SyscallNumber {
93 type Error = KernelError;
94 fn try_from(no: usize) -> Result<SyscallNumber, Self::Error> {
95 match no {
96 1 => Ok(SyscallNumber::Open),
97 2 => Ok(SyscallNumber::Read),
98 3 => Ok(SyscallNumber::Write),
99 4 => Ok(SyscallNumber::Seek),
100 5 => Ok(SyscallNumber::Tell),
101 6 => Ok(SyscallNumber::Close),
102 7 => Ok(SyscallNumber::Pipe),
103 _ => Err(KernelError::NoSuchSyscall),
104 }
105 }
106}
107
108impl Task for Process {
109 /// Handles a system call request from a user program.
110 ///
111 /// This function is the entry point for system call handling. It retrieves
112 /// the system call ABI from the provided [`Registers`] structure, which
113 /// includes the system call number and its arguments. Based on the
114 /// system call number (`sysno`), it looks up the appropriate handler
115 /// function in a predefined list. If a handler is found, it is invoked
116 /// with the ABI, otherwise, an error ([`KernelError::NoSuchSyscall`]) is
117 /// returned.
118 ///
119 /// After the handler function processes the system call, the return value
120 /// (either a success or error) is set back into the CPU registers via
121 /// the `set_return_value` method. The return value is stored in the `%rax`
122 /// register as per the x86-64 system call convention.
123 ///
124 /// # Parameters
125 ///
126 /// - `regs`: A mutable reference to the [`Registers`] struct, which
127 /// contains the current state of the CPU registers. This structure will
128 /// be used to retrieve the system call number and its arguments, and also
129 /// to set the return value.
130 ///
131 /// # Functionality
132 ///
133 /// The function processes the system call as follows:
134 /// 1. Extracts the system call number and arguments using the
135 /// [`SyscallAbi::from_registers`].
136 /// 2. Looks up the corresponding handler function from a predefined list of
137 /// system calls. The handler function is selected based on the system
138 /// call number (`sysno`).
139 /// 3. If a handler is found, it is executed with the ABI as an argument. If
140 /// no handler is found, the function returns a
141 /// [`KernelError::NoSuchSyscall`] error.
142 ///
143 /// The result of the system call handler (either success or error) is then
144 /// returned via the [`SyscallAbi::set_return_value`] method, which
145 /// modifies the CPU registers accordingly.
146 fn syscall(&mut self, regs: &mut Registers) {
147 // ** YOU DON'T NEED TO CHANGE THIS FUNCTION **
148 let abi = SyscallAbi::from_registers(regs); // Extract ABI from the registers.
149 // Lookup the system call handler function based on the system call number.
150 let return_val = SyscallNumber::try_from(abi.sysno).and_then(|no| match no {
151 SyscallNumber::Open => self.file_struct.open(&abi),
152 SyscallNumber::Read => self.file_struct.read(&abi),
153 SyscallNumber::Write => self.file_struct.write(&abi),
154 SyscallNumber::Seek => self.file_struct.seek(&abi),
155 SyscallNumber::Tell => self.file_struct.tell(&abi),
156 SyscallNumber::Close => self.file_struct.close(&abi),
157 SyscallNumber::Pipe => self.file_struct.pipe(&abi),
158 });
159 // Set the return value of the system call (success or error) back into the
160 // registers.
161 abi.set_return_value(return_val);
162 }
163}