Expand description
§System call abi for x86_64.
Operating systems provide an abstraction of hardware resources to user programs, allowing them to interact with hardware without needing to understand its complexities. The kernel is responsible for managing resources such as memory, processes, and input/output devices, while offering a simplified interface for user programs. System calls serve as a crucial interface between user applications and the kernel, providing an additional layer of abstraction that enables programs to request services like file I/O and process management, without directly dealing with the hardware.
§System Call Details
The operating system deals with software exceptions, which occur due to program execution errors such as a page fault or division by zero. Exceptions are also the mechanism by which a user program requests services from the operating system. These service requests are system calls.
In traditional x86 architecture, system calls were handled like any other
software exception through the int instruction. However, in x86-64
architecture, manufacturers introduced the syscall instruction, which
provides a fast and efficient way to invoke system calls.
In modern systems, the syscall instruction is the most commonly used means
of invoking system calls. When a user program wants to make a system call,
it invokes the syscall instruction. The system call number and any
additional arguments are placed in registers before invoking syscall. Here
are the key details:
- The system call number is passed in the
%raxregister. - The arguments are passed in the registers
%rdi,%rsi,%rdx,%r10,%r8, and%r9. Specifically:%rdi: First argument%rsi: Second argument%rdx: Third argument%r10: Fourth argument%r8: Fifth argument%r9: Sixth argument
- The return value is stored to the
%raxregister.
§Handling System Call in KeOS
When the syscall instruction is invoked, the KeOS kernel serves the
requests by calling the Task::syscall method with the state of the CPU
captured in a structure called Registers. This structure contains the
state of all registers, which is accessible to the handler and allows for
the manipulation of register values (including the return value) within the
handler.
On the beginning of the Task::syscall method, KeOS parses the arguments
of the system call by calling the SyscallAbi::from_registers, which is
currently marked with todo!(). Your job is to extend this to retrieve
system call number and arguments according to the abi.
After acquiring the SyscallAbi struct, KeOS dispatches the system call
handler based on the parsed system call number. This system call handler
handles the requests and returns the Result<usize, KernelError> to
indicate the result.
§Error Handling via Result Type
Proper error handling is crucial for maintaining system stability and ensuring that unexpected conditions do not lead to crashes or undefined behavior. Errors incured by user MUST NOT stop the kernel. When an error occurs, ensure that the system call returns an appropriate error code rather than panicking or causing undefined behavior. Providing meaningful error messages can also improve debugging and user experience. Properly handling edge cases will help ensure a robust and stable kernel implementation.
To this end, KeOS combines the rust’s Result type with KernelError
type to enumerate all possible errors that kernel can make.
For example, when implementing system call handlers, you can consider the
following error conditions:
KernelError::BadFileDescriptor: Attempting to operate on a file descriptor that is not open.KernelError::InvalidArgument: A process tries to perform an operation it lacks permission for (e.g., writing to a read-only file).KernelError::NoSuchEntry: A process attempts to open a non-existent file without specifying a creation flag.KernelError::BadAddress: A process attempts to access an unmapped or kernel memory.
During the execution of a function, you might confront the errors. In such
cases, you should never handle the errors by panicking the kernel.
Instead, you propagates the error with ? operator to the
Task::syscall, which conveys the error code to the userspace. Consult
with Rust Book for how to use ? operator.
§Returning Value to User
The epilog of the Task::syscall captures both the result and error of
the function, interprets them in to the usize, and return the values into
the user space. You can update the user program’s registers by directly
modify the fields of Registers captured in the SyscallAbi.
§Implementation Requirements
You need to implement the followings:
After implementing both methods, move on to the next Section.
Structs§
- Syscall
Abi - A struct representing the system call ABI (Application Binary Interface).