From the man pages of OS161
:
Synopsis
#include <unistd.h>
#include <fcntl.h>
int
open(const char *filename, int flags);
int
open(const char *filename, int flags, mode_t mode);
How the standard c library function open
is defined:
int open(const char *filename, int flags, ...);
The declaration:
/*
* Definition for each syscall.
* All we do is load the syscall number into v0, the register the
* kernel expects to find it in, and jump to the shared syscall code.
* (Note that the addiu instruction is in the jump's delay slot.)
*/
#define SYS_open 45
#define SYSCALL(sym, num) \
.set noreorder ; \
.globl sym ; \
.type sym,@function ; \
.ent sym ; \
sym: ; \
j __syscall ; \
addiu v0, $0, SYS_##sym ; \
.end sym ; \
.set reorder
SYSCALL(open, 45)
When a syscall is issued, a syscall dispatcher is called. The syscall dispatcher takes a pointer to a trapframe
which includes the values of registers before the syscall is issued. One of the registers contains the syscall number, which the dispatcher uses to dispatch to the right syscall function. The dispatcher looks like this:
void
syscall(struct trapframe *tf)
{
int callno;
...
callno = tf->tf_v0;
...
switch (callno) {
case SYS_reboot:
err = sys_reboot(tf->tf_a0);
break;
case SYS___time:
err = sys___time((userptr_t)tf->tf_a0,
(userptr_t)tf->tf_a1);
...
}
Here is a comment that describes how arguments are passed and how to return values:
* The calling conventions for syscalls are as follows: Like ordinary
* function calls, the first 4 32-bit arguments are passed in the 4
* argument registers a0-a3. 64-bit arguments are passed in *aligned*
* pairs of registers, that is, either a0/a1 or a2/a3. This means that
* if the first argument is 32-bit and the second is 64-bit, a1 is
* unused.
*
* This much is the same as the calling conventions for ordinary
* function calls. In addition, the system call number is passed in
* the v0 register.
*
* On successful return, the return value is passed back in the v0
* register, or v0 and v1 if 64-bit. This is also like an ordinary
* function call, and additionally the a3 register is also set to 0 to
* indicate success.
*
* On an error return, the error code is passed back in the v0
* register, and the a3 register is set to 1 to indicate failure.
* (Userlevel code takes care of storing the error code in errno and
* returning the value -1 from the actual userlevel syscall function.
* See src/user/lib/libc/arch/mips/syscalls-mips.S and related files.)
You can see that for example sys_reboot
is called with tf->tf_a0
, That's because before the syscall is issued, the register a0
contained the first (and only) parameter for the syscall.
I'm not gonna dive into details for simplicity and because it's probably irrelevant. For example, the process of issuing a syscall is a bit more complicated, but I've mentioned only the relevant stuff. Also not gonna talk about how to get arguments off the stack since I'll not need it here.
I'm supposed to implement the sys_open
syscall, but I'm not sure how to know which variant of the open
functions has been called...
All I have is the syscall number and the values of the registers, which includes the four arguments registers, the stack pointer and the other registers.
How can I determine whether I'm facing the first variation (with only two parameters) or the second one (with 3 parameters) so that I can behave accordingly?
Some useful information:
The whole code for syscall is here.
The whole repo for OS161 is here.
The exception handler code is loaded into memory during the boot here.
The code for the exception handler (the first code that runs when a syscall is issued) is here.
The exception handler is a function called mips_general_handler
that just calls a function common_exception
.
mips_general_handler
is here.
common_exception
is here.
common_exception
pushed all the values of the registers onto the stack, also pushes a pointer to the beginning of the pushed values (that's the pointer that's passed to the function being called) and calls the function mips_trap
which can be found here.
The function misp_trap
looks for the cause of the exception, and if it's a syscall, calls the function syscall
which is given above.