tldr:
If you called your syscall like this some_syscall(42)
, all you have to do to access 42
is call: argint(0, &local_var)
. This stores the 0th int parameter, 42
, into local_var
.
With argptr, you need to give it the address of a pointer and the number of bytes of memory you want to fetch. However, since a pointer in 32-bit architecture is 4-bytes, argint will also do the job.
Here's a high-level understanding of how it works:
argint
accesses the parameters with some pointer math. It accesses the trapframe struct of the process, containing the user-space registers of the syscall. The trapframe saved the function's parameters starting at the esp
register. It adds 4 to skip an empty word on the stack from some conversion that I believe is xv6 specific. The 4*n
is so you can access the nth 4-byte parameter after the starting address.
fetchint
does some error checking and actually stores the address at the address specified by that *ip
pointer.
// Fetch the nth 32-bit system call argument.
int
argint(int n, int *ip)
{
return fetchint((myproc()->tf->esp) + 4 + 4*n, ip);
}
In syscall.c
, syscall()
manages passing the return value of your kernel-space function to the user. It accesses that user-space stack to set the process's return-value register eax
to whatever your syscall returned. Here's that line for reference:
curproc->tf->eax = syscalls[num]();
This github is pretty helpful for understanding xv6 sometimes:
https://github.com/YehudaShapira/xv6-explained/blob/master/Explanations.md#getting-arguments-in-system-calls