diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile | 6 | ||||
-rw-r--r-- | conf/lab.mk | 2 | ||||
-rwxr-xr-x | grade-lab-traps | 74 | ||||
-rw-r--r-- | user/alarmtest.c | 193 | ||||
-rw-r--r-- | user/bttest.c | 10 | ||||
-rw-r--r-- | user/call.c | 17 |
7 files changed, 299 insertions, 4 deletions
@@ -23,5 +23,6 @@ ph barrier /lab-*.json .DS_Store +*.dSYM riscv/ compile_flags.txt @@ -91,7 +91,7 @@ CFLAGS = -Wall -Werror -O -fno-omit-frame-pointer -ggdb -gdwarf-2 ifdef LAB LABUPPER = $(shell echo $(LAB) | tr a-z A-Z) -XCFLAGS += -DSOL_$(LABUPPER) -DLAB_$(LABUPPER) +XCFLAGS += -DSOL_$(LABUPPER) -DLAB_$(LABUPPER) -DLAB_PGTBL endif CFLAGS += $(XCFLAGS) @@ -273,8 +273,8 @@ fs.img: mkfs/mkfs README $(UEXTRA) $(UPROGS) -include kernel/*.d user/*.d -clean: - rm -f *.tex *.dvi *.idx *.aux *.log *.ind *.ilg \ +clean: + rm -f *.tex *.dvi *.idx *.aux *.log *.ind *.ilg *.dSYM *.zip \ */*.o */*.d */*.asm */*.sym \ $U/initcode $U/initcode.out $K/kernel fs.img \ mkfs/mkfs .gdbinit \ diff --git a/conf/lab.mk b/conf/lab.mk index 2992d87..2b9988d 100644 --- a/conf/lab.mk +++ b/conf/lab.mk @@ -1 +1 @@ -LAB=pgtbl +LAB=traps diff --git a/grade-lab-traps b/grade-lab-traps new file mode 100755 index 0000000..10613d3 --- /dev/null +++ b/grade-lab-traps @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 + +import os +import re +import subprocess +from gradelib import * + +r = Runner(save("xv6.out")) + +@test(5, "answers-traps.txt") +def test_answers(): + # just a simple sanity check, will be graded manually + check_answers("answers-traps.txt") + +BACKTRACE_RE = r"^(0x000000008[0-9a-f]+)" + +def addr2line(): + for f in ['riscv64-unknown-elf-addr2line', 'riscv64-linux-gnu-addr2line', 'addr2line', ]: + try: + devnull = open(os.devnull) + subprocess.Popen([f], stdout=devnull, stderr=devnull).communicate() + return f + except OSError: + continue + raise AssertionError('Cannot find the addr2line program') + +@test(10, "backtrace test") +def test_backtracetest(): + r.run_qemu(shell_script([ + 'bttest' + ])) + a2l = addr2line() + matches = re.findall(BACKTRACE_RE, r.qemu.output, re.MULTILINE) + assert_equal(len(matches), 3) + files = ['sysproc.c', 'syscall.c', 'trap.c'] + for f, m in zip(files, matches): + result = subprocess.run([a2l, '-e', 'kernel/kernel', m], stdout=subprocess.PIPE) + if not f in result.stdout.decode("utf-8"): + raise AssertionError('Trace is incorrect; no %s' % f) + +@test(0, "running alarmtest") +def test_alarmtest(): + r.run_qemu(shell_script([ + 'alarmtest' + ])) + +@test(20, "alarmtest: test0", parent=test_alarmtest) +def test_alarmtest_test0(): + r.match('^test0 passed$') + +@test(20, "alarmtest: test1", parent=test_alarmtest) +def test_alarmtest_test1(): + r.match('^\\.?test1 passed$') + +@test(10, "alarmtest: test2", parent=test_alarmtest) +def test_alarmtest_test2(): + r.match('^\\.?test2 passed$') + +@test(10, "alarmtest: test3", parent=test_alarmtest) +def test_alarmtest_test3(): + r.match('^test3 passed$') + +@test(19, "usertests") +def test_usertests(): + r.run_qemu(shell_script([ + 'usertests -q' + ]), timeout=300) + r.match('^ALL TESTS PASSED$') + +@test(1, "time") +def test_time(): + check_time() + +run_tests() diff --git a/user/alarmtest.c b/user/alarmtest.c new file mode 100644 index 0000000..b8d85f7 --- /dev/null +++ b/user/alarmtest.c @@ -0,0 +1,193 @@ +// +// test program for the alarm lab. +// you can modify this file for testing, +// but please make sure your kernel +// modifications pass the original +// versions of these tests. +// + +#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 test2(); +void test3(); +void periodic(); +void slow_handler(); +void dummy_handler(); + +int +main(int argc, char *argv[]) +{ + test0(); + test1(); + test2(); + test3(); + exit(0); +} + +volatile static int count; + +void +periodic() +{ + count = count + 1; + printf("alarm!\n"); + sigreturn(); +} + +// tests whether the kernel calls +// the alarm handler even a single time. +void +test0() +{ + int i; + printf("test0 start\n"); + count = 0; + sigalarm(2, periodic); + for(i = 0; i < 1000*500000; i++){ + if((i % 1000000) == 0) + write(2, ".", 1); + if(count > 0) + break; + } + sigalarm(0, 0); + if(count > 0){ + printf("test0 passed\n"); + } else { + printf("\ntest0 failed: the kernel never called the alarm handler\n"); + } +} + +void __attribute__ ((noinline)) foo(int i, int *j) { + if((i % 2500000) == 0) { + write(2, ".", 1); + } + *j += 1; +} + +// +// tests that the kernel calls the handler multiple times. +// +// tests that, when the handler returns, it returns to +// the point in the program where the timer interrupt +// occurred, with all registers holding the same values they +// held when the interrupt occurred. +// +void +test1() +{ + int i; + int j; + + printf("test1 start\n"); + count = 0; + j = 0; + sigalarm(2, periodic); + for(i = 0; i < 500000000; i++){ + if(count >= 10) + break; + foo(i, &j); + } + if(count < 10){ + printf("\ntest1 failed: too few calls to the handler\n"); + } else if(i != j){ + // the loop should have called foo() i times, and foo() should + // have incremented j once per call, so j should equal i. + // once possible source of errors is that the handler may + // return somewhere other than where the timer interrupt + // occurred; another is that that registers may not be + // restored correctly, causing i or j or the address ofj + // to get an incorrect value. + printf("\ntest1 failed: foo() executed fewer times than it was called\n"); + } else { + printf("test1 passed\n"); + } +} + +// +// tests that kernel does not allow reentrant alarm calls. +void +test2() +{ + int i; + int pid; + int status; + + printf("test2 start\n"); + if ((pid = fork()) < 0) { + printf("test2: fork failed\n"); + } + if (pid == 0) { + count = 0; + sigalarm(2, slow_handler); + for(i = 0; i < 1000*500000; i++){ + if((i % 1000000) == 0) + write(2, ".", 1); + if(count > 0) + break; + } + if (count == 0) { + printf("\ntest2 failed: alarm not called\n"); + exit(1); + } + exit(0); + } + wait(&status); + if (status == 0) { + printf("test2 passed\n"); + } +} + +void +slow_handler() +{ + count++; + printf("alarm!\n"); + if (count > 1) { + printf("test2 failed: alarm handler called more than once\n"); + exit(1); + } + for (int i = 0; i < 1000*500000; i++) { + asm volatile("nop"); // avoid compiler optimizing away loop + } + sigalarm(0, 0); + sigreturn(); +} + +// +// dummy alarm handler; after running immediately uninstall +// itself and finish signal handling +void +dummy_handler() +{ + sigalarm(0, 0); + sigreturn(); +} + +// +// tests that the return from sys_sigreturn() does not +// modify the a0 register +void +test3() +{ + uint64 a0; + + sigalarm(1, dummy_handler); + printf("test3 start\n"); + + asm volatile("lui a5, 0"); + asm volatile("addi a0, a5, 0xac" : : : "a0"); + for(int i = 0; i < 500000000; i++) + ; + asm volatile("mv %0, a0" : "=r" (a0) ); + + if(a0 != 0xac) + printf("test3 failed: register a0 changed\n"); + else + printf("test3 passed\n"); +} diff --git a/user/bttest.c b/user/bttest.c new file mode 100644 index 0000000..05405f9 --- /dev/null +++ b/user/bttest.c @@ -0,0 +1,10 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +int +main(int argc, char *argv[]) +{ + sleep(1); + exit(0); +} diff --git a/user/call.c b/user/call.c new file mode 100644 index 0000000..f725dcb --- /dev/null +++ b/user/call.c @@ -0,0 +1,17 @@ +#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("%d %d\n", f(8)+1, 13); + exit(0); +} |