4.3 Code: Calling system calls

User programs call library functions in order to make system calls. For example, the shell displays a prompt with this function call (in user/sh.c):

  write(2, "$ ", 2);

Here’s the library function, in user/usys.S:

write:
 li a7, SYS_write
 ecall
 ret

The code that the C compiler generates for the function call loads the three arguments into registers a0, a1, and a2. Then the write() function loads the system call number, SYS_write (16), into a7. The kernel will look at those registers to find out what system call is intended, and what the arguments are. The ecall instruction traps from user space into the kernel and causes uservec, usertrap, and then syscall to execute.

At this point, please read kernel/syscall.c, sys_write() in kernel/sysfile.c, and copyout(), copyin(), and copyinstr() in kernel/vm.c.

syscall (3731) retrieves the system call number from the saved a7 in the trapframe and uses it to index into syscalls (3706). For our example, a7 contains SYS_write (3566), resulting in a call to the system call implementation function sys_write.

When sys_write returns, syscall records its return value in p->trapframe->a0. This will cause the original user-space call to write() to return that value, since the C calling convention on RISC-V places return values in a0. System calls conventionally return negative numbers to indicate errors, and zero or positive numbers for success.