diff --git a/labs/syscall.html b/labs/syscall.html index a167885..dad86fe 100644 --- a/labs/syscall.html +++ b/labs/syscall.html @@ -10,7 +10,9 @@ This lab makes you familiar with the implementation of system calls. In particular, you will implement a new system call: alarm. - +Note: before this lab, it would be good to have recitation section + on gdb +
In this exercise you will modify the xv6 kernel to print out a line @@ -46,6 +48,56 @@ print its output.)
Optional: print the system call arguments. +
For the alarm system call it will be important to understand RISC-V +assembly. Since in later labs you will also read and write assembly, +it is important that you familiarize yourself with RISC_V assembly. + +
Add a file user/call.c with the following content, modify the + Makefile to add the program to the user programs, and compile (make + fs.img). The Makefile also produces a binary and a readable + assembly a version of the program in the file user/call.asm. +
+#include "kernel/param.h" +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +int g(int x) { + return x+3; +} + +int f(int x) { + return g(x); +} + +void main(void) { + printf(1, "%d %d\n", f(8)+1, 13); + exit(); +} ++ +
Since you will be reading and writing RISC-V assembly code for xv6, + you should read through call.asm and understand it. The instruction + manual for RISC-V is in the doc directory (doc/riscv-spec-v2.2.pdf). + Here are some questions that you should answer for yourself: + +
@@ -70,25 +122,37 @@ interrupts.
You should put the following example program in user/alarmtest.c: +XXX Insert the final program here
#include "kernel/param.h" #include "kernel/types.h" #include "kernel/stat.h" +#include "kernel/riscv.h" #include "user/user.h" +void test0(); +void test1(); void periodic(); int main(int argc, char *argv[]) +{ + test0(); + test1(); + exit(); +} + +void test0() { int i; - printf(1, "alarmtest starting\n"); - alarm(10, periodic); - for(i = 0; i < 25*500000; i++){ + printf(1, "test0 start\n"); + alarm(2, periodic); + for(i = 0; i < 1000*500000; i++){ if((i % 250000) == 0) write(2, ".", 1); } - exit(); + alarm(0, 0); + printf(1, "test0 done\n"); } void @@ -96,13 +160,37 @@ periodic() { printf(1, "alarm!\n"); } + +void __attribute__ ((noinline)) foo(int i, int *j) { + if((i % 2500000) == 0) { + write(2, ".", 1); + } + *j += 1; +} + +void test1() { + int i; + int j; + + printf(1, "test1 start\n"); + j = 0; + alarm(2, periodic); + for(i = 0; i < 1000*500000; i++){ + foo(i, &j); + } + if(i != j) { + printf(2, "i %d should = j %d\n", i, j); + exit(); + } + printf(1, "test1 done\n"); +}-The program calls alarm(10, periodic) to ask the kernel to +The program calls alarm(2, periodic1) in test0 to ask the kernel to force a call to periodic() every 10 ticks, and then spins for a while. After you have implemented the alarm() system call in the kernel, -alarmtest should produce output like this: +alarmtest should produce output like this for test0:
$ alarmtest @@ -125,7 +213,26 @@ alarmtest starting (If you only see one "alarm!", try increasing the number of iterations in alarmtest.c by 10x.) -Here are some hints: +The main challenge will be to arrange that the handler is invoked + when the process's alarm interval expires. In your usertrap, when a + process's alarm interval expires, you'll want to cause it to execute + its handler. How can you do that? You will need to understand in + details how system calls work (i.e., the code in kernel/trampoline.S + and kernel/trap.c). Which register contains the address where + systems calls return to? + +
Your solution will be few lines of code, but it will be tricky to + write the right lines of code. Common failure scenarios are: the + user program crashes or doesn't terminate. You can see the assembly + code for the alarmtest program in alarmtest.asm, which will be handy + for debugging. + +
Test0
+ +To get started, the best strategy is to first pass test0, which + will force you to handle the main challenge above. Here are some + hints how to pass test0: +
- if(which_dev == 2) .. + if(which_dev == 2) ...-
-In your usertrap, when a process's alarm interval expires, -you'll want to cause it to execute its handler. How can you do that? - -
Test0 doesn't stress whether the handler returns correctly to + interrupted instruction in test0. If you didn't get this right, it + is likely that test1 will fail (the program crashes or the program + goes into an infinite loop). + +
A main challenge is to arrange that when the handler returns, it + returns to the instruction where the program was interrupted. Which + register contains the return address of a function? When the kernel + receives an interrupt, which register contains the address of the + interrupted instruction? + +
Your solution is likely to require you to save and restore a + register. There are several ways to do this. It is ok to change the + API of alarm() and have an alarm stub in user space that cooperates + with the kernel. +
-Optional challenges: 1) Save and restore the caller-saved user registers around the -call to handler. 2) Prevent re-entrant calls to the handler -- if a handler -hasn't returned yet, don't call it again. 3) Assuming your code doesn't -check that tf->esp is valid, implement a security attack on the -kernel that exploits your alarm handler calling code. +Optional challenges: Prevent re-entrant calls to the handler----if a + handler hasn't returned yet, don't call it again. + + + +