summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Makefile6
-rw-r--r--conf/lab.mk2
-rwxr-xr-xgrade-lab-traps74
-rw-r--r--user/alarmtest.c193
-rw-r--r--user/bttest.c10
-rw-r--r--user/call.c17
7 files changed, 299 insertions, 4 deletions
diff --git a/.gitignore b/.gitignore
index 1fe0374..10a47b9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,5 +23,6 @@ ph
barrier
/lab-*.json
.DS_Store
+*.dSYM
riscv/
compile_flags.txt
diff --git a/Makefile b/Makefile
index def8b43..043a9ac 100644
--- a/Makefile
+++ b/Makefile
@@ -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);
+}