summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.clang-format225
-rw-r--r--.gitignore2
-rw-r--r--Makefile34
-rw-r--r--answers-pgtbl.txt0
-rw-r--r--answers-syscall.txt9
-rw-r--r--answers-traps.txt6
-rwxr-xr-xgrade-lab-cow55
-rwxr-xr-xgrade-lab-pgtbl79
-rwxr-xr-xgrade-lab-traps74
-rw-r--r--kernel/cow.c30
-rw-r--r--kernel/defs.h14
-rw-r--r--kernel/exec.c4
-rw-r--r--kernel/kalloc.c57
-rw-r--r--kernel/printf.c17
-rw-r--r--kernel/proc.c84
-rw-r--r--kernel/proc.h7
-rw-r--r--kernel/riscv.h11
-rw-r--r--kernel/syscall.c57
-rw-r--r--kernel/sysinfo.c24
-rw-r--r--kernel/sysinfo.h4
-rw-r--r--kernel/sysproc.c65
-rw-r--r--kernel/trap.c30
-rw-r--r--kernel/vm.c59
-rw-r--r--time.txt1
-rw-r--r--user/alarmtest.c193
-rw-r--r--user/bttest.c10
-rw-r--r--user/call.c17
-rw-r--r--user/cowtest.c197
-rw-r--r--user/find.c84
-rw-r--r--user/pgtbltest.c70
-rw-r--r--user/pingpong.c72
-rw-r--r--user/primes.c74
-rw-r--r--user/sh.c3
-rw-r--r--user/sleep.c22
-rw-r--r--user/sysinfotest.c153
-rw-r--r--user/trace.c27
-rw-r--r--user/ulib.c14
-rw-r--r--user/user.h5
-rwxr-xr-xuser/usys.pl4
-rw-r--r--user/xargs.c60
-rw-r--r--user/xargstest.sh6
41 files changed, 1882 insertions, 77 deletions
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..065dcdf
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,225 @@
+---
+Language: Cpp
+# BasedOnStyle: Mozilla
+AccessModifierOffset: -2
+AlignAfterOpenBracket: Align
+AlignArrayOfStructures: None
+AlignConsecutiveAssignments:
+ Enabled: false
+ AcrossEmptyLines: false
+ AcrossComments: false
+ AlignCompound: false
+ PadOperators: true
+AlignConsecutiveBitFields:
+ Enabled: false
+ AcrossEmptyLines: false
+ AcrossComments: false
+ AlignCompound: false
+ PadOperators: false
+AlignConsecutiveDeclarations:
+ Enabled: false
+ AcrossEmptyLines: false
+ AcrossComments: false
+ AlignCompound: false
+ PadOperators: false
+AlignConsecutiveMacros:
+ Enabled: false
+ AcrossEmptyLines: false
+ AcrossComments: false
+ AlignCompound: false
+ PadOperators: false
+AlignEscapedNewlines: Right
+AlignOperands: Align
+AlignTrailingComments:
+ Kind: Always
+ OverEmptyLines: 0
+AllowAllArgumentsOnNextLine: true
+AllowAllParametersOfDeclarationOnNextLine: false
+AllowShortBlocksOnASingleLine: Never
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortEnumsOnASingleLine: true
+AllowShortFunctionsOnASingleLine: Inline
+AllowShortIfStatementsOnASingleLine: Never
+AllowShortLambdasOnASingleLine: All
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakAfterDefinitionReturnType: TopLevel
+AlwaysBreakAfterReturnType: TopLevel
+AlwaysBreakBeforeMultilineStrings: false
+AlwaysBreakTemplateDeclarations: Yes
+AttributeMacros:
+ - __capability
+BinPackArguments: false
+BinPackParameters: false
+BitFieldColonSpacing: Both
+BraceWrapping:
+ AfterCaseLabel: false
+ AfterClass: true
+ AfterControlStatement: Never
+ AfterEnum: true
+ AfterExternBlock: true
+ AfterFunction: true
+ AfterNamespace: false
+ AfterObjCDeclaration: false
+ AfterStruct: true
+ AfterUnion: true
+ BeforeCatch: false
+ BeforeElse: false
+ BeforeLambdaBody: false
+ BeforeWhile: false
+ IndentBraces: false
+ SplitEmptyFunction: true
+ SplitEmptyRecord: false
+ SplitEmptyNamespace: true
+BreakAfterAttributes: Never
+BreakAfterJavaFieldAnnotations: false
+BreakArrays: true
+BreakBeforeBinaryOperators: None
+BreakBeforeConceptDeclarations: Always
+BreakBeforeBraces: Mozilla
+BreakBeforeInlineASMColon: OnlyMultiline
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializers: BeforeComma
+BreakInheritanceList: BeforeComma
+BreakStringLiterals: true
+ColumnLimit: 80
+CommentPragmas: '^ IWYU pragma:'
+CompactNamespaces: false
+ConstructorInitializerIndentWidth: 2
+ContinuationIndentWidth: 2
+Cpp11BracedListStyle: false
+DerivePointerAlignment: false
+DisableFormat: false
+EmptyLineAfterAccessModifier: Never
+EmptyLineBeforeAccessModifier: LogicalBlock
+ExperimentalAutoDetectBinPacking: false
+FixNamespaceComments: false
+ForEachMacros:
+ - foreach
+ - Q_FOREACH
+ - BOOST_FOREACH
+IfMacros:
+ - KJ_IF_MAYBE
+IncludeBlocks: Preserve
+IncludeCategories:
+ - Regex: '^"(llvm|llvm-c|clang|clang-c)/'
+ Priority: 2
+ SortPriority: 0
+ CaseSensitive: false
+ - Regex: '^(<|"(gtest|gmock|isl|json)/)'
+ Priority: 3
+ SortPriority: 0
+ CaseSensitive: false
+ - Regex: '.*'
+ Priority: 1
+ SortPriority: 0
+ CaseSensitive: false
+IncludeIsMainRegex: '(Test)?$'
+IncludeIsMainSourceRegex: ''
+IndentAccessModifiers: false
+IndentCaseBlocks: false
+IndentCaseLabels: true
+IndentExternBlock: AfterExternBlock
+IndentGotoLabels: true
+IndentPPDirectives: None
+IndentRequiresClause: true
+IndentWidth: 2
+IndentWrappedFunctionNames: false
+InsertBraces: false
+InsertNewlineAtEOF: false
+InsertTrailingCommas: None
+IntegerLiteralSeparator:
+ Binary: 0
+ BinaryMinDigits: 0
+ Decimal: 0
+ DecimalMinDigits: 0
+ Hex: 0
+ HexMinDigits: 0
+JavaScriptQuotes: Leave
+JavaScriptWrapImports: true
+KeepEmptyLinesAtTheStartOfBlocks: true
+LambdaBodyIndentation: Signature
+LineEnding: DeriveLF
+MacroBlockBegin: ''
+MacroBlockEnd: ''
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: None
+ObjCBinPackProtocolList: Auto
+ObjCBlockIndentWidth: 2
+ObjCBreakBeforeNestedBlockParam: true
+ObjCSpaceAfterProperty: true
+ObjCSpaceBeforeProtocolList: false
+PackConstructorInitializers: BinPack
+PenaltyBreakAssignment: 2
+PenaltyBreakBeforeFirstCallParameter: 19
+PenaltyBreakComment: 300
+PenaltyBreakFirstLessLess: 120
+PenaltyBreakOpenParenthesis: 0
+PenaltyBreakString: 1000
+PenaltyBreakTemplateDeclaration: 10
+PenaltyExcessCharacter: 1000000
+PenaltyIndentedWhitespace: 0
+PenaltyReturnTypeOnItsOwnLine: 200
+PointerAlignment: Left
+PPIndentWidth: -1
+QualifierAlignment: Leave
+ReferenceAlignment: Pointer
+ReflowComments: true
+RemoveBracesLLVM: false
+RemoveSemicolon: false
+RequiresClausePosition: OwnLine
+RequiresExpressionIndentation: OuterScope
+SeparateDefinitionBlocks: Leave
+ShortNamespaceLines: 1
+SortIncludes: CaseSensitive
+SortJavaStaticImport: Before
+SortUsingDeclarations: LexicographicNumeric
+SpaceAfterCStyleCast: false
+SpaceAfterLogicalNot: false
+SpaceAfterTemplateKeyword: false
+SpaceAroundPointerQualifiers: Default
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeCaseColon: false
+SpaceBeforeCpp11BracedList: false
+SpaceBeforeCtorInitializerColon: true
+SpaceBeforeInheritanceColon: true
+SpaceBeforeParens: ControlStatements
+SpaceBeforeParensOptions:
+ AfterControlStatements: true
+ AfterForeachMacros: true
+ AfterFunctionDefinitionName: false
+ AfterFunctionDeclarationName: false
+ AfterIfMacros: true
+ AfterOverloadedOperator: false
+ AfterRequiresInClause: false
+ AfterRequiresInExpression: false
+ BeforeNonEmptyParentheses: false
+SpaceBeforeRangeBasedForLoopColon: true
+SpaceBeforeSquareBrackets: false
+SpaceInEmptyBlock: false
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 1
+SpacesInAngles: Never
+SpacesInConditionalStatement: false
+SpacesInContainerLiterals: true
+SpacesInCStyleCastParentheses: false
+SpacesInLineCommentPrefix:
+ Minimum: 1
+ Maximum: -1
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+Standard: Latest
+StatementAttributeLikeMacros:
+ - Q_EMIT
+StatementMacros:
+ - Q_UNUSED
+ - QT_REQUIRE_VERSION
+TabWidth: 8
+UseTab: Never
+WhitespaceSensitiveMacros:
+ - BOOST_PP_STRINGIZE
+ - CF_SWIFT_NAME
+ - NS_SWIFT_NAME
+ - PP_STRINGIZE
+ - STRINGIZE
+...
+
diff --git a/.gitignore b/.gitignore
index d036a8b..1f45623 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,3 +25,5 @@ barrier
.DS_Store
*.dSYM
*.pcap
+riscv/
+compile_flags.txt
diff --git a/Makefile b/Makefile
index da43ce2..561311a 100644
--- a/Makefile
+++ b/Makefile
@@ -13,12 +13,14 @@ OBJS = \
$K/kalloc.o \
$K/string.o \
$K/main.o \
+ $K/cow.o \
$K/vm.o \
$K/proc.o \
$K/swtch.o \
$K/trampoline.o \
$K/trap.o \
$K/syscall.o \
+ $K/sysinfo.o \
$K/sysproc.o \
$K/bio.o \
$K/fs.o \
@@ -90,7 +92,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)
@@ -188,6 +190,13 @@ UPROGS=\
$U/_grind\
$U/_wc\
$U/_zombie\
+ $U/_sleep\
+ $U/_pingpong\
+ $U/_primes\
+ $U/_find\
+ $U/_xargs\
+ $U/_trace\
+ $U/_sysinfotest\
@@ -197,21 +206,18 @@ UPROGS += \
$U/_stats
endif
-ifeq ($(LAB),traps)
UPROGS += \
$U/_call\
- $U/_bttest
-endif
+ $U/_bttest\
+ $U/_alarmtest
ifeq ($(LAB),lazy)
UPROGS += \
$U/_lazytests
endif
-ifeq ($(LAB),cow)
UPROGS += \
$U/_cowtest
-endif
ifeq ($(LAB),thread)
UPROGS += \
@@ -231,10 +237,8 @@ barrier: notxv6/barrier.c
gcc -o barrier -g -O2 $(XCFLAGS) notxv6/barrier.c -pthread
endif
-ifeq ($(LAB),pgtbl)
UPROGS += \
$U/_pgtbltest
-endif
ifeq ($(LAB),lock)
UPROGS += \
@@ -255,9 +259,7 @@ UPROGS += \
endif
UEXTRA=
-ifeq ($(LAB),util)
- UEXTRA += user/xargstest.sh
-endif
+UEXTRA += user/xargstest.sh
fs.img: mkfs/mkfs README $(UEXTRA) $(UPROGS)
@@ -266,11 +268,13 @@ fs.img: mkfs/mkfs README $(UEXTRA) $(UPROGS)
-include kernel/*.d user/*.d
clean:
- rm -rf *.tex *.dvi *.idx *.aux *.log *.ind *.ilg *.dSYM *.zip *.pcap \
+ rm -f *.tex *.dvi *.idx *.aux *.log *.ind *.ilg *.dSYM *.zip \
*/*.o */*.d */*.asm */*.sym \
- $U/initcode $U/initcode.out $U/usys.S $U/_* \
- $K/kernel \
- mkfs/mkfs fs.img .gdbinit __pycache__ xv6.out* \
+ $U/initcode $U/initcode.out $K/kernel fs.img \
+ mkfs/mkfs .gdbinit \
+ $U/usys.S \
+ $(UPROGS) \
+ *.zip \
ph barrier
# try to generate a unique GDB port
diff --git a/answers-pgtbl.txt b/answers-pgtbl.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/answers-pgtbl.txt
diff --git a/answers-syscall.txt b/answers-syscall.txt
new file mode 100644
index 0000000..ca0ce77
--- /dev/null
+++ b/answers-syscall.txt
@@ -0,0 +1,9 @@
+usertrap()
+7
+SYS_exec
+user mode
+lw a3,0(zero)
+a3
+no
+yes, `scause` 0xd -> interrupt = 0, exception code = 13 -> instruction page fault (riscv-priviledged p71)
+'initcode', 1
diff --git a/answers-traps.txt b/answers-traps.txt
new file mode 100644
index 0000000..dbb40f1
--- /dev/null
+++ b/answers-traps.txt
@@ -0,0 +1,6 @@
+a0~a7, a2
+0x26 (optimized during compile time), 0x14 (inline, unfolded)
+0x666
+0x38 (next instruction)
+He110 World
+the value in register a2
diff --git a/grade-lab-cow b/grade-lab-cow
new file mode 100755
index 0000000..08b6990
--- /dev/null
+++ b/grade-lab-cow
@@ -0,0 +1,55 @@
+#!/usr/bin/env python3
+
+import re
+from gradelib import *
+
+r = Runner(save("xv6.out"))
+
+@test(0, "running cowtest")
+def test_cowtest():
+ r.run_qemu(shell_script([
+ 'cowtest'
+ ]))
+
+@test(30, "simple", parent=test_cowtest)
+def test_simple():
+ matches = re.findall("^simple: ok$", r.qemu.output, re.M)
+ assert_equal(len(matches), 2, "Number of appearances of 'simple: ok'")
+
+@test(30, "three", parent=test_cowtest)
+def test_three():
+ matches = re.findall("^three: ok$", r.qemu.output, re.M)
+ assert_equal(len(matches), 3, "Number of appearances of 'three: ok'")
+
+@test(20, "file", parent=test_cowtest)
+def test_file():
+ r.match('^file: ok$')
+
+@test(0, "usertests")
+def test_usertests():
+ r.run_qemu(shell_script([
+ 'usertests -q'
+ ]), timeout=1000)
+ r.match('^ALL TESTS PASSED$')
+
+def usertest_check(testcase, nextcase, output):
+ if not re.search(r'\ntest {}: [\s\S]*OK\ntest {}'.format(testcase, nextcase), output):
+ raise AssertionError('Failed ' + testcase)
+
+@test(5, "usertests: copyin", parent=test_usertests)
+def test_sbrkbugs():
+ usertest_check("copyin", "copyout", r.qemu.output)
+
+@test(5, "usertests: copyout", parent=test_usertests)
+def test_sbrkbugs():
+ usertest_check("copyout", "copyinstr1", r.qemu.output)
+
+@test(19, "usertests: all tests", parent=test_usertests)
+def test_usertests_all():
+ r.match('^ALL TESTS PASSED$')
+
+@test(1, "time")
+def test_time():
+ check_time()
+
+run_tests()
diff --git a/grade-lab-pgtbl b/grade-lab-pgtbl
new file mode 100755
index 0000000..121bb3b
--- /dev/null
+++ b/grade-lab-pgtbl
@@ -0,0 +1,79 @@
+#!/usr/bin/env python3
+
+import re
+from gradelib import *
+
+r = Runner(save("xv6.out"))
+
+PTE_PRINT = """page table 0x0000000087f6b000
+ ..0: pte 0x0000000021fd9c01 pa 0x0000000087f67000
+ .. ..0: pte 0x0000000021fd9801 pa 0x0000000087f66000
+ .. .. ..0: pte 0x0000000021fda01b pa 0x0000000087f68000
+ .. .. ..1: pte 0x0000000021fd9417 pa 0x0000000087f65000
+ .. .. ..2: pte 0x0000000021fd9007 pa 0x0000000087f64000
+ .. .. ..3: pte 0x0000000021fd8c17 pa 0x0000000087f63000
+ ..255: pte 0x0000000021fda801 pa 0x0000000087f6a000
+ .. ..511: pte 0x0000000021fda401 pa 0x0000000087f69000
+ .. .. ..509: pte 0x0000000021fdcc13 pa 0x0000000087f73000
+ .. .. ..510: pte 0x0000000021fdd007 pa 0x0000000087f74000
+ .. .. ..511: pte 0x0000000020001c0b pa 0x0000000080007000"""
+
+VAL_RE = "(0x00000000[0-9a-f]+)"
+INDENT_RE = r"\s*\.\.\s*"
+INDENT_ESC = "\\\s*\.\.\\\s*"
+
+@test(0, "pgtbltest")
+def test_pgtbltest():
+ r.run_qemu(shell_script([
+ 'pgtbltest'
+ ]), timeout=300)
+
+@test(10, "pgtbltest: ugetpid", parent=test_pgtbltest)
+def test_nettest_():
+ r.match('^ugetpid_test: OK$')
+
+@test(10, "pgtbltest: pgaccess", parent=test_pgtbltest)
+def test_nettest_():
+ r.match('^pgaccess_test: OK$')
+
+@test(10, "pte printout")
+def test_pteprint():
+ first = True
+ r.run_qemu(shell_script([
+ 'echo hi'
+ ]))
+ r.match('^hi')
+ p = re.compile(VAL_RE)
+ d = re.compile(INDENT_RE)
+ for l in PTE_PRINT.splitlines():
+ l = d.sub(INDENT_ESC, l)
+ l = p.sub(VAL_RE, l)
+ r.match(r'^{}$'.format(l))
+ if first:
+ first = False
+ else:
+ matches = re.findall(r'^{}$'.format(l), r.qemu.output, re.MULTILINE)
+ assert_equal(len(matches[0]), 2)
+ pa = (int(matches[0][0], 16) >> 10) << 12
+ assert_equal(int(matches[0][1], 16), pa)
+
+@test(5, "answers-pgtbl.txt")
+def test_answers():
+ # just a simple sanity check, will be graded manually
+ check_answers("answers-pgtbl.txt")
+
+@test(0, "usertests")
+def test_usertests():
+ r.run_qemu(shell_script([
+ 'usertests -q'
+ ]), timeout=300)
+
+@test(10, "usertests: all tests", parent=test_usertests)
+def test_usertests():
+ r.match('^ALL TESTS PASSED$')
+
+@test(1, "time")
+def test_time():
+ check_time()
+
+run_tests()
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/kernel/cow.c b/kernel/cow.c
new file mode 100644
index 0000000..b3634fc
--- /dev/null
+++ b/kernel/cow.c
@@ -0,0 +1,30 @@
+// COW pagefault handler
+#include "types.h"
+#include "riscv.h"
+#include "defs.h"
+
+int
+cow_handler(pagetable_t pagetable, uint64 va)
+{
+ // you can't really write to rediculous pointers
+ if(va >= MAXVA || PGROUNDDOWN(va) == 0)
+ return -1;
+ pte_t *pte = walk(pagetable, va, 0);
+ if(pte == 0 || (*pte & PTE_V) == 0 || (*pte & PTE_U) == 0)
+ return -1;
+ if(*pte & PTE_C){
+ uint64 pa_orig = PTE2PA(*pte);
+ uint64 pa_new = (uint64)kalloc();
+ if(pa_new == 0){
+ printf("cow pagefault: kalloc failed\n");
+ return -1;
+ }
+ // copy the page and add write permission
+ memmove((void*)pa_new, (void*)pa_orig, PGSIZE);
+ uint64 flags = (PTE_FLAGS(*pte) | PTE_W) & ~PTE_C;
+ *pte = PA2PTE(pa_new) | flags;
+ kfree((void*)pa_orig);
+ } else if ((*pte & PTE_W) == 0)
+ return -1;
+ return 0;
+}
diff --git a/kernel/defs.h b/kernel/defs.h
index c6fef8c..02226b5 100644
--- a/kernel/defs.h
+++ b/kernel/defs.h
@@ -12,6 +12,7 @@ struct superblock;
struct mbuf;
struct sock;
#endif
+struct sysinfo;
// bio.c
void binit(void);
@@ -26,6 +27,9 @@ void consoleinit(void);
void consoleintr(int);
void consputc(int);
+// cow.c
+int cow_handler(pagetable_t, uint64);
+
// exec.c
int exec(char*, char**);
@@ -64,9 +68,12 @@ void ramdiskintr(void);
void ramdiskrw(struct buf*);
// kalloc.c
+int refcnt_inc(uint64);
+int refcnt_dec(uint64);
void* kalloc(void);
void kfree(void *);
void kinit(void);
+int get_freemem(void);
// log.c
void initlog(int, struct superblock*);
@@ -84,6 +91,7 @@ int pipewrite(struct pipe*, uint64, int);
void printf(char*, ...);
void panic(char*) __attribute__((noreturn));
void printfinit(void);
+void backtrace(void);
// proc.c
int cpuid(void);
@@ -110,6 +118,8 @@ void yield(void);
int either_copyout(int user_dst, uint64 dst, void *src, uint64 len);
int either_copyin(void *dst, int user_src, uint64 src, uint64 len);
void procdump(void);
+int get_nproc(void);
+int pgaccess(uint64 base, int len, uint64 mask);
// swtch.S
void swtch(struct context*, struct context*);
@@ -149,6 +159,9 @@ int fetchstr(uint64, char*, int);
int fetchaddr(uint64, uint64*);
void syscall();
+// sysinfo.c
+int sys_info(uint64);
+
// trap.c
extern uint ticks;
void trapinit(void);
@@ -181,6 +194,7 @@ uint64 walkaddr(pagetable_t, uint64);
int copyout(pagetable_t, uint64, char *, uint64);
int copyin(pagetable_t, char *, uint64, uint64);
int copyinstr(pagetable_t, char *, uint64, uint64);
+void vmprint(pagetable_t);
// plic.c
void plicinit(void);
diff --git a/kernel/exec.c b/kernel/exec.c
index e18bbb6..35b35f5 100644
--- a/kernel/exec.c
+++ b/kernel/exec.c
@@ -128,6 +128,10 @@ exec(char *path, char **argv)
p->trapframe->sp = sp; // initial stack pointer
proc_freepagetable(oldpagetable, oldsz);
+ if(p->pid == 1){
+ vmprint(p->pagetable);
+ }
+
return argc; // this ends up in a0, the first argument to main(argc, argv)
bad:
diff --git a/kernel/kalloc.c b/kernel/kalloc.c
index 0699e7e..581f0f6 100644
--- a/kernel/kalloc.c
+++ b/kernel/kalloc.c
@@ -23,10 +23,41 @@ struct {
struct run *freelist;
} kmem;
+int phypg_refcnt[PHYSTOP/PGSIZE];
+
+// Increase the refcnt
+int
+refcnt_inc(uint64 pa)
+{
+ acquire(&kmem.lock);
+ int *prefcnt = &phypg_refcnt[pa/PGSIZE];
+ if(pa > PHYSTOP || *prefcnt < 1)
+ panic("increase refcnt");
+ (*prefcnt)++;
+ release(&kmem.lock);
+ return *prefcnt;
+}
+
+// Decrease the refcnt
+int
+refcnt_dec(uint64 pa)
+{
+ acquire(&kmem.lock);
+ int *prefcnt = &phypg_refcnt[pa/PGSIZE];
+ if(pa > PHYSTOP || *prefcnt < 1)
+ panic("decrease refcnt");
+ (*prefcnt)--;
+ release(&kmem.lock);
+ return *prefcnt;
+}
+
void
kinit()
{
initlock(&kmem.lock, "kmem");
+ // init all refcnt to 1, which would later be freed to 0 by kfree()
+ for(uint64 p = PGROUNDUP((uint64)end); p + PGSIZE <= PHYSTOP; p += PGSIZE)
+ phypg_refcnt[p/PGSIZE] = 1;
freerange(end, (void*)PHYSTOP);
}
@@ -51,6 +82,12 @@ kfree(void *pa)
if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP)
panic("kfree");
+ refcnt_dec((uint64)pa);
+
+ if(phypg_refcnt[(uint64)pa/PGSIZE] > 0)
+ // We still have refs to this phy page, do not actually free it
+ return;
+
// Fill with junk to catch dangling refs.
memset(pa, 1, PGSIZE);
@@ -72,11 +109,29 @@ kalloc(void)
acquire(&kmem.lock);
r = kmem.freelist;
- if(r)
+ if(r){
+ if(phypg_refcnt[(uint64)r/PGSIZE])
+ panic("kalloc: invalid refcnt");
+ phypg_refcnt[(uint64)r/PGSIZE] = 1;
kmem.freelist = r->next;
+ }
release(&kmem.lock);
if(r)
memset((char*)r, 5, PGSIZE); // fill with junk
return (void*)r;
}
+
+int
+get_freemem(void)
+{
+ int n;
+ struct run *r;
+
+ acquire(&kmem.lock);
+ for (n = 0, r = kmem.freelist; r; r = r->next)
+ n++;
+ release(&kmem.lock);
+
+ return n * PGSIZE;
+}
diff --git a/kernel/printf.c b/kernel/printf.c
index 1a50203..509c1c5 100644
--- a/kernel/printf.c
+++ b/kernel/printf.c
@@ -122,6 +122,8 @@ panic(char *s)
printf("panic: ");
printf(s);
printf("\n");
+ backtrace();
+
panicked = 1; // freeze uart output from other CPUs
for(;;)
;
@@ -133,3 +135,18 @@ printfinit(void)
initlock(&pr.lock, "pr");
pr.locking = 1;
}
+
+void
+backtrace(void)
+{
+ uint64 fp = r_fp();
+ printf("backtrace:\n");
+ uint64 stackpg = PGROUNDDOWN(fp);
+ // Whereever fp points to should always live in the stack page
+ while(PGROUNDDOWN(fp) == stackpg){
+ // print the return addr (stored in fp-8)
+ printf("%p\n", *(uint64 *)(fp-8));
+ // load previous (upper stack) fp
+ fp = *(uint64 *)(fp-16);
+ }
+}
diff --git a/kernel/proc.c b/kernel/proc.c
index 58a8a0b..9a9bae9 100644
--- a/kernel/proc.c
+++ b/kernel/proc.c
@@ -39,6 +39,7 @@ proc_mapstacks(pagetable_t kpgtbl)
if(pa == 0)
panic("kalloc");
uint64 va = KSTACK((int) (p - proc));
+ p->alarm_tickspassed = 0;
kvmmap(kpgtbl, va, (uint64)pa, PGSIZE, PTE_R | PTE_W);
}
}
@@ -132,6 +133,25 @@ found:
return 0;
}
+ // Allocate a usyscall page and fill pid.
+ if((p->usyscall = (struct usyscall *)kalloc()) == 0){
+ freeproc(p);
+ release(&p->lock);
+ return 0;
+ }
+ p->usyscall->pid = p->pid;
+
+ // reset sigalarm props
+ p->alarm_interval = 0;
+ p->alarm_handler = 0;
+ p->alarm_tickspassed = 0;
+ p->alarm_caninvoke = 1;
+ if((p->atpfm = (struct trapframe *)kalloc()) == 0){
+ freeproc(p);
+ release(&p->lock);
+ return 0;
+ }
+
// An empty user page table.
p->pagetable = proc_pagetable(p);
if(p->pagetable == 0){
@@ -158,8 +178,18 @@ freeproc(struct proc *p)
if(p->trapframe)
kfree((void*)p->trapframe);
p->trapframe = 0;
+ if(p->usyscall)
+ kfree((void*)p->usyscall);
+ p->usyscall = 0;
if(p->pagetable)
proc_freepagetable(p->pagetable, p->sz);
+ if(p->atpfm)
+ kfree((void*)p->atpfm);
+ p->atpfm = 0;
+ p->alarm_interval = 0;
+ p->alarm_handler = 0;
+ p->alarm_tickspassed = 0;
+ p->alarm_caninvoke = 1;
p->pagetable = 0;
p->sz = 0;
p->pid = 0;
@@ -172,7 +202,7 @@ freeproc(struct proc *p)
}
// Create a user page table for a given process, with no user memory,
-// but with trampoline and trapframe pages.
+// but with trampoline, trapframe and usyscall pages.
pagetable_t
proc_pagetable(struct proc *p)
{
@@ -202,6 +232,14 @@ proc_pagetable(struct proc *p)
return 0;
}
+ // map the usyscall page below the trapframe page, for
+ // ugetpid().
+ if(mappages(pagetable, USYSCALL, PGSIZE,
+ (uint64)(p->usyscall), PTE_R | PTE_U) < 0){
+ uvmunmap(pagetable, USYSCALL, 1, 0);
+ uvmfree(pagetable, 0);
+ return 0;
+ }
return pagetable;
}
@@ -212,6 +250,7 @@ proc_freepagetable(pagetable_t pagetable, uint64 sz)
{
uvmunmap(pagetable, TRAMPOLINE, 1, 0);
uvmunmap(pagetable, TRAPFRAME, 1, 0);
+ uvmunmap(pagetable, USYSCALL, 1, 0);
uvmfree(pagetable, sz);
}
@@ -299,6 +338,9 @@ fork(void)
// copy saved user registers.
*(np->trapframe) = *(p->trapframe);
+ // inherit trace_mask
+ np->trace_mask = p->trace_mask;
+
// Cause fork to return 0 in the child.
np->trapframe->a0 = 0;
@@ -686,3 +728,43 @@ procdump(void)
printf("\n");
}
}
+
+int
+get_nproc(void)
+{
+ int n = 0;
+ struct proc *p;
+
+ for(int i = 0; i < NPROC; i++) {
+ p = &proc[i];
+ acquire(&p->lock);
+ if(p->state != UNUSED)
+ n++;
+ release(&p->lock);
+ }
+
+ return n;
+}
+
+// lab pagetable: report which pages have been accessed (r/w)
+// according to PTE_A and store it in a bit mask (3rd param)
+int
+pgaccess(uint64 base, int len, uint64 mask_addr)
+{
+ struct proc *p = myproc();
+ pagetable_t pgtbl = p->pagetable;
+ pte_t *pte;
+ int mask = 0;
+
+ // iterater thru pages
+ for(int i = 0; i < len; i++) {
+ pte = walk(pgtbl, base + i * PGSIZE, 0);
+ if(*pte & PTE_A) {
+ *pte &= (~PTE_A); // clear PTE_A to avoid setting it forever
+ mask |= (1L << i);
+ }
+ }
+
+ // now copyout the mask to user memory
+ return copyout(pgtbl, mask_addr, (char *)&mask, sizeof(mask));
+}
diff --git a/kernel/proc.h b/kernel/proc.h
index d021857..a195b02 100644
--- a/kernel/proc.h
+++ b/kernel/proc.h
@@ -91,6 +91,7 @@ struct proc {
int killed; // If non-zero, have been killed
int xstate; // Exit status to be returned to parent's wait
int pid; // Process ID
+ int trace_mask; // SYS_trace mask (1 << SYS_xxx)
// wait_lock must be held when using this:
struct proc *parent; // Parent process
@@ -100,8 +101,14 @@ struct proc {
uint64 sz; // Size of process memory (bytes)
pagetable_t pagetable; // User page table
struct trapframe *trapframe; // data page for trampoline.S
+ struct usyscall *usyscall; // data page for usyscall
struct context context; // swtch() here to run process
struct file *ofile[NOFILE]; // Open files
struct inode *cwd; // Current directory
char name[16]; // Process name (debugging)
+ int alarm_interval; // sigalarm syscall interval
+ uint64 alarm_handler; // sigalarm syscall handler
+ int alarm_tickspassed; // record how many ticks passed since last sigalarm handler call
+ int alarm_caninvoke; // prevent re-entrant calls to handler
+ struct trapframe *atpfm; // trapframe to resume after handling, must hold p->lock
};
diff --git a/kernel/riscv.h b/kernel/riscv.h
index 20a01db..7d6eb6e 100644
--- a/kernel/riscv.h
+++ b/kernel/riscv.h
@@ -327,6 +327,15 @@ sfence_vma()
asm volatile("sfence.vma zero, zero");
}
+// read the frame pointer of currently executing func
+static inline uint64
+r_fp()
+{
+ uint64 x;
+ asm volatile("mv %0, s0" : "=r" (x) );
+ return x;
+}
+
typedef uint64 pte_t;
typedef uint64 *pagetable_t; // 512 PTEs
@@ -343,6 +352,8 @@ typedef uint64 *pagetable_t; // 512 PTEs
#define PTE_W (1L << 2)
#define PTE_X (1L << 3)
#define PTE_U (1L << 4) // user can access
+#define PTE_A (1L << 6) // riscv access bit
+#define PTE_C (1L << 8) // RSW low bit, use it to mark whether a page is COW
// shift a physical address to the right place for a PTE.
#define PA2PTE(pa) ((((uint64)pa) >> 12) << 10)
diff --git a/kernel/syscall.c b/kernel/syscall.c
index beea0ef..172c5ea 100644
--- a/kernel/syscall.c
+++ b/kernel/syscall.c
@@ -101,6 +101,17 @@ extern uint64 sys_unlink(void);
extern uint64 sys_link(void);
extern uint64 sys_mkdir(void);
extern uint64 sys_close(void);
+extern uint64 sys_trace(void);
+extern uint64 sys_sysinfo(void);
+
+#ifdef LAB_NET
+extern uint64 sys_connect(void);
+#endif
+#ifdef LAB_PGTBL
+extern uint64 sys_pgaccess(void);
+#endif
+extern uint64 sys_sigalarm(void);
+extern uint64 sys_sigreturn(void);
#ifdef LAB_NET
extern uint64 sys_connect(void);
@@ -139,8 +150,46 @@ static uint64 (*syscalls[])(void) = {
#ifdef LAB_PGTBL
[SYS_pgaccess] sys_pgaccess,
#endif
+[SYS_trace] sys_trace,
+[SYS_sysinfo] sys_sysinfo,
+[SYS_sigalarm] sys_sigalarm,
+[SYS_sigreturn] sys_sigreturn,
};
+// syscall name maps for SYS_trace:
+static char *syscall_names[] = {
+[SYS_fork] "fork",
+[SYS_exit] "exit",
+[SYS_wait] "wait",
+[SYS_pipe] "pipe",
+[SYS_read] "read",
+[SYS_kill] "kill",
+[SYS_exec] "exec",
+[SYS_fstat] "fstat",
+[SYS_chdir] "chdir",
+[SYS_dup] "dup",
+[SYS_getpid] "getpid",
+[SYS_sbrk] "sbrk",
+[SYS_sleep] "sleep",
+[SYS_uptime] "uptime",
+[SYS_open] "open",
+[SYS_write] "write",
+[SYS_mknod] "mknod",
+[SYS_unlink] "unlink",
+[SYS_link] "link",
+[SYS_mkdir] "mkdir",
+[SYS_close] "close",
+#ifdef LAB_NET
+[SYS_connect] "connect",
+#endif
+#ifdef LAB_PGTBL
+[SYS_pgaccess] "pgaccess",
+#endif
+[SYS_trace] "trace",
+[SYS_sysinfo] "sysinfo",
+[SYS_sigalarm] "sigalarm",
+[SYS_sigreturn] "sigreturn",
+};
void
@@ -154,9 +203,17 @@ syscall(void)
// Use num to lookup the system call function for num, call it,
// and store its return value in p->trapframe->a0
p->trapframe->a0 = syscalls[num]();
+
+ // SYS_trace: match all the syscalls which number < mask asked
+ // p->trace_mask == 1 << SYS_xxx
+ if(p->trace_mask >> num) {
+ printf("%d: syscall %s -> %d\n", p->pid, syscall_names[num], p->trapframe->a0);
+ }
+
} else {
printf("%d %s: unknown sys call %d\n",
p->pid, p->name, num);
p->trapframe->a0 = -1;
}
}
+
diff --git a/kernel/sysinfo.c b/kernel/sysinfo.c
new file mode 100644
index 0000000..c66324d
--- /dev/null
+++ b/kernel/sysinfo.c
@@ -0,0 +1,24 @@
+#include "types.h"
+#include "riscv.h"
+#include "param.h"
+#include "spinlock.h"
+#include "defs.h"
+#include "sysinfo.h"
+#include "proc.h"
+
+// Get current system info
+// addr is a user virtual address, pointing to a struct sysinfo.
+int
+sys_info(uint64 addr) {
+ struct proc *p = myproc();
+ struct sysinfo info;
+
+ // Fill nums into the sysinfo struct
+ info.freemem = get_freemem();
+ info.nproc = get_nproc();
+
+ if(copyout(p->pagetable, addr, (char *)&info, sizeof(info)) < 0)
+ return -1;
+ return 0;
+}
+
diff --git a/kernel/sysinfo.h b/kernel/sysinfo.h
new file mode 100644
index 0000000..fb878e6
--- /dev/null
+++ b/kernel/sysinfo.h
@@ -0,0 +1,4 @@
+struct sysinfo {
+ uint64 freemem; // amount of free memory (bytes)
+ uint64 nproc; // number of process
+};
diff --git a/kernel/sysproc.c b/kernel/sysproc.c
index 3b4d5bd..715a511 100644
--- a/kernel/sysproc.c
+++ b/kernel/sysproc.c
@@ -1,7 +1,7 @@
#include "types.h"
#include "riscv.h"
-#include "defs.h"
#include "param.h"
+#include "defs.h"
#include "memlayout.h"
#include "spinlock.h"
#include "proc.h"
@@ -54,9 +54,8 @@ sys_sleep(void)
int n;
uint ticks0;
+
argint(0, &n);
- if(n < 0)
- n = 0;
acquire(&tickslock);
ticks0 = ticks;
while(ticks - ticks0 < n){
@@ -66,10 +65,29 @@ sys_sleep(void)
}
sleep(&ticks, &tickslock);
}
+
+ // backtrace();
+
release(&tickslock);
return 0;
}
+
+#ifdef LAB_PGTBL
+int
+sys_pgaccess(void)
+{
+ uint64 base, mask;
+ int len;
+
+
+ argaddr(0, &base);
+ argint(1, &len);
+ argaddr(2, &mask);
+ return pgaccess(base, len, mask);
+}
+#endif
+
uint64
sys_kill(void)
{
@@ -91,3 +109,44 @@ sys_uptime(void)
release(&tickslock);
return xticks;
}
+
+uint64
+sys_trace(void)
+{
+ argint(0, &myproc()->trace_mask);
+
+ return -(myproc()->trace_mask <= 1);
+}
+
+uint64
+sys_sysinfo(void)
+{
+ uint64 si; // user pointer to struct sysinfo
+
+ argaddr(0, &si);
+ return sys_info(si);
+}
+
+uint64
+sys_sigalarm(void)
+{
+ struct proc *p = myproc();
+ uint64 handler;
+
+ argint(0, &p->alarm_interval);
+ argaddr(1, &handler);
+ p->alarm_handler = handler;
+
+ return 0;
+}
+
+uint64 sys_sigreturn(void)
+{
+ struct proc *p = myproc();
+ // retore saved trapframe to resume
+ memmove(p->trapframe, p->atpfm, sizeof(struct trapframe));
+ p->alarm_tickspassed = 0;
+ p->alarm_caninvoke = 1;
+ // make sure return the original a0 in trapframe to pass test3
+ return p->trapframe->a0;
+}
diff --git a/kernel/trap.c b/kernel/trap.c
index 5332dda..7cc69b1 100644
--- a/kernel/trap.c
+++ b/kernel/trap.c
@@ -6,6 +6,12 @@
#include "proc.h"
#include "defs.h"
+/*
+ * Always remember that RISC-V disables interrupts when it starts to take a trap,
+ * so there's no need to call intr_off() at the beginning of trap handling.
+ * Reference: xv6-riscv-book 4.5
+ */
+
struct spinlock tickslock;
uint ticks;
@@ -67,11 +73,18 @@ usertrap(void)
syscall();
} else if((which_dev = devintr()) != 0){
// ok
+ } else if(r_scause() == 13 || r_scause() == 15){
+ // deal with page fault
+ uint64 va = r_stval();
+ if(cow_handler(p->pagetable, va) < 0)
+ goto err;
} else {
printf("usertrap(): unexpected scause %p pid=%d\n", r_scause(), p->pid);
printf(" sepc=%p stval=%p\n", r_sepc(), r_stval());
+ err:
+ printf("killing the process...\n");
setkilled(p);
}
@@ -79,9 +92,22 @@ usertrap(void)
exit(-1);
- // give up the CPU if this is a timer interrupt.
- if(which_dev == 2)
+ if(which_dev == 2){
+ // timer interrupt
+ if(p->alarm_interval > 0 && p->alarm_caninvoke){
+ // record sigalarm
+ p->alarm_tickspassed++;
+ if(p->alarm_tickspassed == p->alarm_interval){
+ // store original trapframe in p->atpfm
+ memmove(p->atpfm, p->trapframe, sizeof(struct trapframe));
+ p->alarm_tickspassed = 0;
+ p->alarm_caninvoke = 0;
+ p->trapframe->epc = p->alarm_handler;
+ }
+ }
+ // give up the CPU.
yield();
+ }
usertrapret();
}
diff --git a/kernel/vm.c b/kernel/vm.c
index 7119242..be7d042 100644
--- a/kernel/vm.c
+++ b/kernel/vm.c
@@ -320,20 +320,26 @@ uvmcopy(pagetable_t old, pagetable_t new, uint64 sz)
pte_t *pte;
uint64 pa, i;
uint flags;
- char *mem;
+ // char *mem;
for(i = 0; i < sz; i += PGSIZE){
if((pte = walk(old, i, 0)) == 0)
panic("uvmcopy: pte should exist");
if((*pte & PTE_V) == 0)
panic("uvmcopy: page not present");
- pa = PTE2PA(*pte);
- flags = PTE_FLAGS(*pte);
+ // do not do the actual copy, just increase the refcnt and mark pages readonly COW
+ /*
if((mem = kalloc()) == 0)
goto err;
memmove(mem, (char*)pa, PGSIZE);
- if(mappages(new, i, PGSIZE, (uint64)mem, flags) != 0){
- kfree(mem);
+ */
+ *pte &= ~PTE_W;
+ *pte |= PTE_C;
+ pa = PTE2PA(*pte);
+ refcnt_inc(pa);
+ flags = PTE_FLAGS(*pte);
+ if(mappages(new, i, PGSIZE, (uint64)pa, flags) != 0){
+ // kfree(mem);
goto err;
}
}
@@ -364,22 +370,21 @@ int
copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len)
{
uint64 n, va0, pa0;
- pte_t *pte;
+ // pte_t *pte;
while(len > 0){
va0 = PGROUNDDOWN(dstva);
- if (va0 >= MAXVA)
- return -1;
- if((pte = walk(pagetable, va0, 0)) == 0) {
- // printf("copyout: pte should exist 0x%x %d\n", dstva, len);
- return -1;
- }
-
- // forbid copyout over read-only user text pages.
- if((*pte & PTE_W) == 0)
+ if(cow_handler(pagetable, va0) < 0)
return -1;
+ /*
+ pte = walk(pagetable, va0, 0);
+ if(pte == 0 || (*pte & PTE_V) == 0 || (*pte & PTE_U) == 0 ||
+ (*pte & PTE_W) == 0)
+ return -1;
+ pa0 = PTE2PA(*pte);
+ */
pa0 = walkaddr(pagetable, va0);
if(pa0 == 0)
return -1;
@@ -463,5 +468,29 @@ copyinstr(pagetable_t pagetable, char *dst, uint64 srcva, uint64 max)
}
}
+static void
+walkprint(pagetable_t pgtbl, int level)
+{
+ for(int i = 0; i < 512; i++){
+ pte_t pte = pgtbl[i];
+ if(pte & PTE_V){
+ for(int j = 0; j < level; j++){
+ printf(" ..");
+ }
+ printf("%d: pte %p pa %p\n", i, pte, PTE2PA(pte));
+ if((pte & (PTE_R|PTE_W|PTE_X)) == 0){
+ // this PTE points to a lower-level page table.
+ walkprint((pagetable_t)PTE2PA(pte), level+1);
+ }
+ }
+ }
+}
+// Print the contents of a page table
+void
+vmprint(pagetable_t pgtbl)
+{
+ printf("page table %p\n", pgtbl);
+ walkprint(pgtbl, 1);
+}
diff --git a/time.txt b/time.txt
new file mode 100644
index 0000000..00750ed
--- /dev/null
+++ b/time.txt
@@ -0,0 +1 @@
+3
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);
+}
diff --git a/user/cowtest.c b/user/cowtest.c
new file mode 100644
index 0000000..29b918f
--- /dev/null
+++ b/user/cowtest.c
@@ -0,0 +1,197 @@
+//
+// tests for copy-on-write fork() assignment.
+//
+
+#include "kernel/types.h"
+#include "kernel/memlayout.h"
+#include "user/user.h"
+
+// allocate more than half of physical memory,
+// then fork. this will fail in the default
+// kernel, which does not support copy-on-write.
+void
+simpletest()
+{
+ uint64 phys_size = PHYSTOP - KERNBASE;
+ int sz = (phys_size / 3) * 2;
+
+ printf("simple: ");
+
+ char *p = sbrk(sz);
+ if(p == (char*)0xffffffffffffffffL){
+ printf("sbrk(%d) failed\n", sz);
+ exit(-1);
+ }
+
+ for(char *q = p; q < p + sz; q += 4096){
+ *(int*)q = getpid();
+ }
+
+ int pid = fork();
+ if(pid < 0){
+ printf("fork() failed\n");
+ exit(-1);
+ }
+
+ if(pid == 0)
+ exit(0);
+
+ wait(0);
+
+ if(sbrk(-sz) == (char*)0xffffffffffffffffL){
+ printf("sbrk(-%d) failed\n", sz);
+ exit(-1);
+ }
+
+ printf("ok\n");
+}
+
+// three processes all write COW memory.
+// this causes more than half of physical memory
+// to be allocated, so it also checks whether
+// copied pages are freed.
+void
+threetest()
+{
+ uint64 phys_size = PHYSTOP - KERNBASE;
+ int sz = phys_size / 4;
+ int pid1, pid2;
+
+ printf("three: ");
+
+ char *p = sbrk(sz);
+ if(p == (char*)0xffffffffffffffffL){
+ printf("sbrk(%d) failed\n", sz);
+ exit(-1);
+ }
+
+ pid1 = fork();
+ if(pid1 < 0){
+ printf("fork failed\n");
+ exit(-1);
+ }
+ if(pid1 == 0){
+ pid2 = fork();
+ if(pid2 < 0){
+ printf("fork failed");
+ exit(-1);
+ }
+ if(pid2 == 0){
+ for(char *q = p; q < p + (sz/5)*4; q += 4096){
+ *(int*)q = getpid();
+ }
+ for(char *q = p; q < p + (sz/5)*4; q += 4096){
+ if(*(int*)q != getpid()){
+ printf("wrong content\n");
+ exit(-1);
+ }
+ }
+ exit(-1);
+ }
+ for(char *q = p; q < p + (sz/2); q += 4096){
+ *(int*)q = 9999;
+ }
+ exit(0);
+ }
+
+ for(char *q = p; q < p + sz; q += 4096){
+ *(int*)q = getpid();
+ }
+
+ wait(0);
+
+ sleep(1);
+
+ for(char *q = p; q < p + sz; q += 4096){
+ if(*(int*)q != getpid()){
+ printf("wrong content\n");
+ exit(-1);
+ }
+ }
+
+ if(sbrk(-sz) == (char*)0xffffffffffffffffL){
+ printf("sbrk(-%d) failed\n", sz);
+ exit(-1);
+ }
+
+ printf("ok\n");
+}
+
+char junk1[4096];
+int fds[2];
+char junk2[4096];
+char buf[4096];
+char junk3[4096];
+
+// test whether copyout() simulates COW faults.
+void
+filetest()
+{
+ printf("file: ");
+
+ buf[0] = 99;
+
+ for(int i = 0; i < 4; i++){
+ if(pipe(fds) != 0){
+ printf("pipe() failed\n");
+ exit(-1);
+ }
+ int pid = fork();
+ if(pid < 0){
+ printf("fork failed\n");
+ exit(-1);
+ }
+ if(pid == 0){
+ sleep(1);
+ if(read(fds[0], buf, sizeof(i)) != sizeof(i)){
+ printf("error: read failed\n");
+ exit(1);
+ }
+ sleep(1);
+ int j = *(int*)buf;
+ if(j != i){
+ printf("error: read the wrong value\n");
+ exit(1);
+ }
+ exit(0);
+ }
+ if(write(fds[1], &i, sizeof(i)) != sizeof(i)){
+ printf("error: write failed\n");
+ exit(-1);
+ }
+ }
+
+ int xstatus = 0;
+ for(int i = 0; i < 4; i++) {
+ wait(&xstatus);
+ if(xstatus != 0) {
+ exit(1);
+ }
+ }
+
+ if(buf[0] != 99){
+ printf("error: child overwrote parent\n");
+ exit(1);
+ }
+
+ printf("ok\n");
+}
+
+int
+main(int argc, char *argv[])
+{
+ simpletest();
+
+ // check that the first simpletest() freed the physical memory.
+ simpletest();
+
+ threetest();
+ threetest();
+ threetest();
+
+ filetest();
+
+ printf("ALL COW TESTS PASSED\n");
+
+ exit(0);
+}
diff --git a/user/find.c b/user/find.c
new file mode 100644
index 0000000..e185e9d
--- /dev/null
+++ b/user/find.c
@@ -0,0 +1,84 @@
+#include "kernel/types.h"
+
+#include "kernel/fcntl.h"
+#include "kernel/fs.h"
+#include "kernel/stat.h"
+#include "user/user.h"
+
+char*
+fmtname(char* path)
+{
+ char* p;
+
+ // Find first character after last slash.
+ for (p = path + strlen(path); p >= path && *p != '/'; p--)
+ ;
+ p++;
+ return p;
+}
+
+void
+find(char* root_path, char* filename)
+{
+ static char buf[512];
+ char* p;
+ int fd;
+ struct dirent de;
+ struct stat st;
+
+ if ((fd = open(root_path, O_RDONLY)) < 0) {
+ fprintf(2, "find: cannot open %s\n", root_path);
+ return;
+ }
+ if (fstat(fd, &st) < 0) {
+ fprintf(2, "find: cannot stat %s\n", root_path);
+ close(fd);
+ return;
+ }
+
+ switch (st.type) {
+ case T_FILE:
+ if (!strcmp(fmtname(root_path), filename)) {
+ printf("%s\n", root_path);
+ }
+ break;
+ case T_DIR:
+ if (strlen(root_path) + 1 + DIRSIZ + 1 > sizeof(buf)) {
+ printf("find: path too long\n");
+ break;
+ }
+
+ strcpy(buf, root_path);
+
+ p = buf + strlen(buf);
+ *p++ = '/';
+ while (read(fd, &de, sizeof(de)) == sizeof(de)) {
+ if (de.inum == 0)
+ continue;
+ memmove(p, de.name, DIRSIZ);
+ p[DIRSIZ] = '\0';
+
+ // printf("i'm finding %s!\n", fmtname(buf));
+
+ if (!strcmp(fmtname(buf), ".") || !strcmp(fmtname(buf), "..")) {
+ continue;
+ }
+
+ find(buf, filename);
+ }
+ }
+ close(fd);
+}
+
+int
+main(int argc, char* argv[])
+{
+ if (argc < 3) {
+ fprintf(2, "usage: find [root_path] filename...\n");
+ exit(1);
+ }
+
+ for (int i = 2; i < argc; i++) {
+ find(argv[1], argv[i]);
+ }
+}
diff --git a/user/pgtbltest.c b/user/pgtbltest.c
new file mode 100644
index 0000000..bce158a
--- /dev/null
+++ b/user/pgtbltest.c
@@ -0,0 +1,70 @@
+#include "kernel/param.h"
+#include "kernel/fcntl.h"
+#include "kernel/types.h"
+#include "kernel/riscv.h"
+#include "user/user.h"
+
+void ugetpid_test();
+void pgaccess_test();
+
+int
+main(int argc, char *argv[])
+{
+ ugetpid_test();
+ pgaccess_test();
+ printf("pgtbltest: all tests succeeded\n");
+ exit(0);
+}
+
+char *testname = "???";
+
+void
+err(char *why)
+{
+ printf("pgtbltest: %s failed: %s, pid=%d\n", testname, why, getpid());
+ exit(1);
+}
+
+void
+ugetpid_test()
+{
+ int i;
+
+ printf("ugetpid_test starting\n");
+ testname = "ugetpid_test";
+
+ for (i = 0; i < 64; i++) {
+ int ret = fork();
+ if (ret != 0) {
+ wait(&ret);
+ if (ret != 0)
+ exit(1);
+ continue;
+ }
+ if (getpid() != ugetpid())
+ err("missmatched PID");
+ exit(0);
+ }
+ printf("ugetpid_test: OK\n");
+}
+
+void
+pgaccess_test()
+{
+ char *buf;
+ unsigned int abits;
+ printf("pgaccess_test starting\n");
+ testname = "pgaccess_test";
+ buf = malloc(32 * PGSIZE);
+ if (pgaccess(buf, 32, &abits) < 0)
+ err("pgaccess failed");
+ buf[PGSIZE * 1] += 1;
+ buf[PGSIZE * 2] += 1;
+ buf[PGSIZE * 30] += 1;
+ if (pgaccess(buf, 32, &abits) < 0)
+ err("pgaccess failed");
+ if (abits != ((1 << 1) | (1 << 2) | (1 << 30)))
+ err("incorrect access bits set");
+ free(buf);
+ printf("pgaccess_test: OK\n");
+}
diff --git a/user/pingpong.c b/user/pingpong.c
index 6ed12e7..7b03a76 100644
--- a/user/pingpong.c
+++ b/user/pingpong.c
@@ -1,52 +1,44 @@
#include "kernel/types.h"
-#include "kernel/stat.h"
#include "user/user.h"
-#define N 5
-char buf[N];
+int
+main(int argc, char* argv[])
+{
+ int p[2];
-void
-pong(int *parent_to_child, int *child_to_parent) {
- if (read(parent_to_child[0], buf, N) < 0) {
- printf("read failed\n");
- }
- printf("%d: received %s\n", getpid(), buf);
- if (write(child_to_parent[1], "pong", 4) != 4) {
- printf("write failed\n");
+ if (argc > 1) {
+ fprintf(2, "usage: pingpong\n");
+ exit(1);
}
-}
-void
-ping(int *parent_to_child, int *child_to_parent) {
-
- if (write(parent_to_child[1], "ping", 4) != 4) {
- printf("write failed\n");
- }
- if (read(child_to_parent[0], buf, N) < 0) {
- printf("read failed\n");
- }
- printf("%d: received %s\n", getpid(), buf);
-}
+ pipe(p);
-int
-main(int argc, char *argv[])
-{
- int parent_to_child[2];
- int child_to_parent[2];
+ int pid = fork();
- int pid;
-
- if (pipe(parent_to_child) < 0 || pipe(child_to_parent) < 0) {
- printf("pipe failed\n");
- }
- if ((pid = fork()) < 0) {
- printf("fork failed\n");
- }
if (pid == 0) {
- pong(parent_to_child, child_to_parent);
- } else {
- ping(parent_to_child, child_to_parent);
+ short n;
+ read(p[0], &n, sizeof(n));
+ if (n == 42) {
+ fprintf(1, "%d: received ping\n", getpid());
+ }
+ n++;
+ write(p[1], &n, sizeof(n));
+ close(p[0]);
+ close(p[1]);
+ exit(0);
+ }
+
+ short n = 42;
+
+ write(p[1], &n, sizeof(n));
+ read(p[0], &n, sizeof(n));
+ if (n == 43) {
+ fprintf(1, "%d: received pong\n", getpid());
}
-
+ close(p[0]);
+ close(p[1]);
+
+ wait(0);
+
exit(0);
}
diff --git a/user/primes.c b/user/primes.c
new file mode 100644
index 0000000..b359524
--- /dev/null
+++ b/user/primes.c
@@ -0,0 +1,74 @@
+#include "kernel/types.h"
+#include "user/user.h"
+
+#define MAX 36
+#define FIRST_PRIME 2
+
+int
+generate_natural(); // -> out_fd
+int
+prime_filter(int in_fd, int prime); // -> out_fd
+
+int
+main(int argc, char* argv[])
+{
+ int prime;
+
+ int in = generate_natural();
+ while (read(in, &prime, sizeof(int))) {
+ // printf("prime %d: in_fd: %d\n", prime, in); // debug
+ printf("prime %d\n", prime);
+ in = prime_filter(in, prime);
+ }
+
+ close(in);
+
+ exit(0);
+}
+
+int
+generate_natural()
+{
+ int out_pipe[2];
+
+ pipe(out_pipe);
+
+ if (!fork()) {
+ for (int i = FIRST_PRIME; i < MAX; i++) {
+ write(out_pipe[1], &i, sizeof(int));
+ }
+ close(out_pipe[1]);
+
+ exit(0);
+ }
+
+ close(out_pipe[1]);
+
+ return out_pipe[0];
+}
+
+int
+prime_filter(int in_fd, int prime)
+{
+ int num;
+ int out_pipe[2];
+
+ pipe(out_pipe);
+
+ if (!fork()) {
+ while (read(in_fd, &num, sizeof(int))) {
+ if (num % prime) {
+ write(out_pipe[1], &num, sizeof(int));
+ }
+ }
+ close(in_fd);
+ close(out_pipe[1]);
+
+ exit(0);
+ }
+
+ close(in_fd);
+ close(out_pipe[1]);
+
+ return out_pipe[0];
+}
diff --git a/user/sh.c b/user/sh.c
index 836ebcb..5eceda0 100644
--- a/user/sh.c
+++ b/user/sh.c
@@ -165,6 +165,9 @@ main(void)
fprintf(2, "cannot cd %s\n", buf+3);
continue;
}
+ if(buf[0] == 'e' && buf[1] == 'x' && buf[2] == 'i' && buf[3] == 't'){
+ exit(0);
+ }
if(fork1() == 0)
runcmd(parsecmd(buf));
wait(0);
diff --git a/user/sleep.c b/user/sleep.c
new file mode 100644
index 0000000..961f558
--- /dev/null
+++ b/user/sleep.c
@@ -0,0 +1,22 @@
+#include "kernel/types.h"
+#include "user/user.h"
+
+int
+main(int argc, char* argv[])
+{
+ uint sec = 0;
+
+ if (argc <= 1) {
+ fprintf(2, "usage: sleep [time (ticks)]\n");
+ exit(1);
+ }
+ sec = atoi(argv[1]);
+
+ sleep(sec);
+
+ if (argc <= 2) {
+ exit(0);
+ }
+
+ exit(0);
+}
diff --git a/user/sysinfotest.c b/user/sysinfotest.c
new file mode 100644
index 0000000..8a648a6
--- /dev/null
+++ b/user/sysinfotest.c
@@ -0,0 +1,153 @@
+#include "kernel/types.h"
+#include "kernel/riscv.h"
+#include "kernel/sysinfo.h"
+#include "user/user.h"
+
+
+void
+sinfo(struct sysinfo *info) {
+ if (sysinfo(info) < 0) {
+ printf("FAIL: sysinfo failed");
+ exit(1);
+ }
+}
+
+//
+// use sbrk() to count how many free physical memory pages there are.
+//
+int
+countfree()
+{
+ uint64 sz0 = (uint64)sbrk(0);
+ struct sysinfo info;
+ int n = 0;
+
+ while(1){
+ if((uint64)sbrk(PGSIZE) == 0xffffffffffffffff){
+ break;
+ }
+ n += PGSIZE;
+ }
+ sinfo(&info);
+ if (info.freemem != 0) {
+ printf("FAIL: there is no free mem, but sysinfo.freemem=%d\n",
+ info.freemem);
+ exit(1);
+ }
+ sbrk(-((uint64)sbrk(0) - sz0));
+ return n;
+}
+
+void
+testmem() {
+ struct sysinfo info;
+ uint64 n = countfree();
+
+ sinfo(&info);
+
+ if (info.freemem!= n) {
+ printf("FAIL: free mem %d (bytes) instead of %d\n", info.freemem, n);
+ exit(1);
+ }
+
+ if((uint64)sbrk(PGSIZE) == 0xffffffffffffffff){
+ printf("sbrk failed");
+ exit(1);
+ }
+
+ sinfo(&info);
+
+ if (info.freemem != n-PGSIZE) {
+ printf("FAIL: free mem %d (bytes) instead of %d\n", n-PGSIZE, info.freemem);
+ exit(1);
+ }
+
+ if((uint64)sbrk(-PGSIZE) == 0xffffffffffffffff){
+ printf("sbrk failed");
+ exit(1);
+ }
+
+ sinfo(&info);
+
+ if (info.freemem != n) {
+ printf("FAIL: free mem %d (bytes) instead of %d\n", n, info.freemem);
+ exit(1);
+ }
+}
+
+void
+testcall() {
+ struct sysinfo info;
+
+ if (sysinfo(&info) < 0) {
+ printf("FAIL: sysinfo failed\n");
+ exit(1);
+ }
+
+ if (sysinfo((struct sysinfo *) 0xeaeb0b5b00002f5e) != 0xffffffffffffffff) {
+ printf("FAIL: sysinfo succeeded with bad argument\n");
+ exit(1);
+ }
+}
+
+void testproc() {
+ struct sysinfo info;
+ uint64 nproc;
+ int status;
+ int pid;
+
+ sinfo(&info);
+ nproc = info.nproc;
+
+ pid = fork();
+ if(pid < 0){
+ printf("sysinfotest: fork failed\n");
+ exit(1);
+ }
+ if(pid == 0){
+ sinfo(&info);
+ if(info.nproc != nproc+1) {
+ printf("sysinfotest: FAIL nproc is %d instead of %d\n", info.nproc, nproc+1);
+ exit(1);
+ }
+ exit(0);
+ }
+ wait(&status);
+ sinfo(&info);
+ if(info.nproc != nproc) {
+ printf("sysinfotest: FAIL nproc is %d instead of %d\n", info.nproc, nproc);
+ exit(1);
+ }
+}
+
+void testbad() {
+ int pid = fork();
+ int xstatus;
+
+ if(pid < 0){
+ printf("sysinfotest: fork failed\n");
+ exit(1);
+ }
+ if(pid == 0){
+ sinfo(0x0);
+ exit(0);
+ }
+ wait(&xstatus);
+ if(xstatus == -1) // kernel killed child?
+ exit(0);
+ else {
+ printf("sysinfotest: testbad succeeded %d\n", xstatus);
+ exit(xstatus);
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ printf("sysinfotest: start\n");
+ testcall();
+ testmem();
+ testproc();
+ printf("sysinfotest: OK\n");
+ exit(0);
+}
diff --git a/user/trace.c b/user/trace.c
new file mode 100644
index 0000000..dd77760
--- /dev/null
+++ b/user/trace.c
@@ -0,0 +1,27 @@
+#include "kernel/param.h"
+#include "kernel/types.h"
+#include "kernel/stat.h"
+#include "user/user.h"
+
+int
+main(int argc, char *argv[])
+{
+ int i;
+ char *nargv[MAXARG];
+
+ if(argc < 3 || (argv[1][0] < '0' || argv[1][0] > '9')){
+ fprintf(2, "Usage: %s mask command\n", argv[0]);
+ exit(1);
+ }
+
+ if (trace(atoi(argv[1])) < 0) {
+ fprintf(2, "%s: trace failed\n", argv[0]);
+ exit(1);
+ }
+
+ for(i = 2; i < argc && i < MAXARG; i++){
+ nargv[i-2] = argv[i];
+ }
+ exec(nargv[0], nargv);
+ exit(0);
+}
diff --git a/user/ulib.c b/user/ulib.c
index c7b66c4..871adc9 100644
--- a/user/ulib.c
+++ b/user/ulib.c
@@ -1,8 +1,13 @@
#include "kernel/types.h"
#include "kernel/stat.h"
#include "kernel/fcntl.h"
+#ifdef LAB_PGTBL
+#include "kernel/riscv.h"
+#include "kernel/memlayout.h"
+#endif
#include "user/user.h"
+
//
// wrapper so that it's OK if main() does not call exit().
//
@@ -145,3 +150,12 @@ memcpy(void *dst, const void *src, uint n)
{
return memmove(dst, src, n);
}
+
+#ifdef LAB_PGTBL
+int
+ugetpid(void)
+{
+ struct usyscall *u = (struct usyscall *)USYSCALL;
+ return u->pid;
+}
+#endif
diff --git a/user/user.h b/user/user.h
index 16cf173..34591fd 100644
--- a/user/user.h
+++ b/user/user.h
@@ -1,4 +1,5 @@
struct stat;
+struct sysinfo;
// system calls
int fork(void);
@@ -30,6 +31,10 @@ int pgaccess(void *base, int len, void *mask);
// usyscall region
int ugetpid(void);
#endif
+int trace(int);
+int sysinfo(struct sysinfo*);
+int sigalarm(int ticks, void (*handler)());
+int sigreturn(void);
// ulib.c
int stat(const char*, struct stat*);
diff --git a/user/usys.pl b/user/usys.pl
index 6453fe9..33af0ad 100755
--- a/user/usys.pl
+++ b/user/usys.pl
@@ -36,5 +36,9 @@ entry("getpid");
entry("sbrk");
entry("sleep");
entry("uptime");
+entry("trace");
+entry("sysinfo");
entry("connect");
entry("pgaccess");
+entry("sigalarm");
+entry("sigreturn");
diff --git a/user/xargs.c b/user/xargs.c
new file mode 100644
index 0000000..b5b9bee
--- /dev/null
+++ b/user/xargs.c
@@ -0,0 +1,60 @@
+#include "kernel/types.h"
+
+#include "kernel/param.h"
+#include "kernel/stat.h"
+#include "user/user.h"
+
+#define is_blank(chr) (chr == ' ' || chr == '\t')
+
+int
+main(int argc, char* argv[])
+{
+ char buf[2048], ch;
+ char* p = buf;
+ char* v[MAXARG];
+ int c;
+ int blanks = 0;
+ int offset = 0;
+
+ if (argc <= 1) {
+ fprintf(2, "usage: xargs <command> [argv...]\n");
+ exit(1);
+ }
+
+ for (c = 1; c < argc; c++) {
+ v[c - 1] = argv[c];
+ }
+ --c;
+
+ while (read(0, &ch, 1) > 0) {
+ if (is_blank(ch)) {
+ blanks++;
+ continue;
+ }
+
+ if (blanks) {
+ buf[offset++] = 0;
+
+ v[c++] = p;
+ p = buf + offset;
+
+ blanks = 0;
+ }
+
+ if (ch != '\n') {
+ buf[offset++] = ch;
+ } else {
+ v[c++] = p;
+ p = buf + offset;
+
+ if (!fork()) {
+ exit(exec(v[0], v));
+ }
+ wait(0);
+
+ c = argc - 1;
+ }
+ }
+
+ exit(0);
+}
diff --git a/user/xargstest.sh b/user/xargstest.sh
new file mode 100644
index 0000000..4362589
--- /dev/null
+++ b/user/xargstest.sh
@@ -0,0 +1,6 @@
+mkdir a
+echo hello > a/b
+mkdir c
+echo hello > c/b
+echo hello > b
+find . b | xargs grep hello